Squashed 'third_party/osqp/' content from commit 33454b3e23

Change-Id: I056df0582ca06664e86554c341a94c47ab932001
git-subtree-dir: third_party/osqp
git-subtree-split: 33454b3e236f1f44193bfbbb6b8c8e71f8f04e9a
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..d626829
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,23 @@
+# https://editorconfig.org
+
+root = true
+
+[*.{c, h, cpp, hpp}]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+trim_trailing_whitespace = true
+
+
+# Python
+[*.py]
+indent_size = 4
+
+# Python
+[*.yml]
+indent_size = 2
+
+# CMake
+[CMakeLists.txt]
+indent_size = 4
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..9243092
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,4 @@
+* text=auto
+*.c text
+*.h text
+*.cpp text
\ No newline at end of file
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..4514277
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,48 @@
+name: Docs 
+
+on:
+  push:
+    branches: [ master ]
+    tags:
+      - '*'
+
+jobs:
+
+  build_docs:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python: [3.9]
+
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          lfs: false
+
+      - name: Install OS dependencies
+        run: |
+          sudo apt-get install doxygen
+
+      - name: Setup Python
+        uses: actions/setup-python@v2
+        with:
+          python-version: ${{ matrix.python }}
+
+      - name: Install Python dependencies
+        run: |
+          pip install sphinx sphinx-rtd-theme breathe
+
+      - name: Setup Envvars
+        run: |
+            if [[ $GITHUB_REF = "refs/tags/"* ]] ; then echo "OSQP_VERSION=${GITHUB_REF/refs\/tags\//}" ; else echo "OSQP_VERSION=0.0.0" ; fi >> $GITHUB_ENV
+            if [[ $GITHUB_REF = "refs/tags/"* ]] ; then echo "OSQP_NAME=osqp-${GITHUB_REF/refs\/tags\//}-${{ runner.os }}" ; else echo "OSQP_NAME=osqp-0.0.0-${{ runner.os }}" ; fi >> $GITHUB_ENV
+
+      - name: Build docs
+        run: |
+          cd docs && make HTMLCOPYDIR=../site/docs html && touch ../site/docs/.nojekyll
+
+      - name: Deploy
+        uses: JamesIves/github-pages-deploy-action@4.1.1
+        with:
+          branch: gh-pages
+          folder: site
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..d87ff95
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,127 @@
+name: Main
+
+on:
+  push:
+    branches: [ master, develop**, ci ]
+    tags:
+      - '*'
+  pull_request:
+    branches: [ master, develop** ]
+
+jobs:
+
+  build_and_test:
+      runs-on: ${{ matrix.os }}
+
+      strategy:
+        fail-fast: false
+
+        matrix:
+          os: [ubuntu-latest, macos-latest, windows-latest]
+          python-version: [3.9]
+          cmake_flags: ['', '-DUNITTESTS=ON -DCOVERAGE=ON']
+          cmake_flags_extra: ['', '-DENABLE_MKL_PARDISO=OFF', '-DDFLOAT=ON', '-DDLONG=OFF', '-DEMBEDDED=1',
+                              '-DEMBEDDED=2', '-DPROFILING=OFF', '-DCTRLC=OFF', '-DPRINTING=OFF']
+          exclude:
+            - cmake_flags: '-DUNITTESTS=ON -DCOVERAGE=ON'
+              cmake_flags_extra: '-DEMBEDDED=1'
+            - cmake_flags: '-DUNITTESTS=ON -DCOVERAGE=ON'
+              cmake_flags_extra: '-DEMBEDDED=2'
+
+          include:
+            - os: ubuntu-latest
+              cmake_generator: "Unix Makefiles"
+            - os: macos-latest
+              cmake_generator: "Unix Makefiles"
+            - os: windows-latest
+              cmake_generator: "MinGW Makefiles"
+
+      defaults:
+        run:
+          # Required when using an activated conda environment in steps
+          # See https://github.com/conda-incubator/setup-miniconda#IMPORTANT
+          shell: bash -l {0}
+
+      steps:
+        - uses: actions/checkout@v2
+          with:
+            lfs: false
+            submodules: recursive
+
+        - name: Set up conda
+          uses: conda-incubator/setup-miniconda@v2
+          with:
+            auto-update-conda: true
+            python-version: ${{ matrix.python-version }}
+
+        # -----------------
+        # OS-specific setup
+        # -----------------
+        - name: Setup (Linux)
+          if: runner.os == 'Linux'
+          run: |
+            echo "LD_LIBRARY_PATH=$CONDA_PREFIX/lib" >> $GITHUB_ENV
+
+        - name: Setup (macOS)
+          if: runner.os == 'macOS'
+          # Newer versions of MacOS effectively block DYLD_LIBRARY_PATH being set (System Integrity Protection)
+          # Explicitly setting RPATH using `install_name_tool -add_rpath $CONDA_PREFIX/lib ./build/out/osqp_tester`
+          #   doesn't work either.
+          # Here we get around it by using a standard non-root location for .dylib files as a soft link
+          run: |
+            echo "DYLD_LIBRARY_PATH=$CONDA_PREFIX/lib" >> $GITHUB_ENV
+            ln -s $CONDA_PREFIX/lib ~/lib
+
+        - name: Setup (Windows)
+          if: runner.os == 'Windows'
+          run: |
+            echo "$CONDA_PREFIX/Library/bin" >> $GITHUB_PATH
+        # -----------------
+
+        # Fetching mkl from the anaconda channel instead of defaults gives us the MKL runtime dynamic libraries
+        # as well (mkl_rt.<dll/so>), required during the runtime testing steps.
+        # MKL on Anaconda 2021.* seems to have inexplicably renamed mkl_rt.dll to mkl_rt.1.dll, so we insist on
+        # a version earlier than 2021
+        - name: Install python dependencies
+          run: |
+            conda install -c anaconda "mkl<2021" numpy scipy
+            conda info
+            conda list
+
+        - name: Build
+          run: |
+            cmake -G "${{ matrix.cmake_generator }}" -S . -B build ${{ matrix.cmake_flags }} ${{ matrix.cmake_flags_extra }}
+            cmake --build build
+
+        # useful for inspecting the OSQP version information
+        - name: OSQP Demo
+          run: |
+            ./build/out/osqp_demo
+          if: ${{ !contains(matrix.cmake_flags_extra, 'EMBEDDED') }}
+
+        - name: Test
+          run: |
+            ./build/out/osqp_tester
+          if: ${{ matrix.cmake_flags == '-DUNITTESTS=ON -DCOVERAGE=ON' }}
+
+        - name: Valgrid check
+          run: |
+            sudo apt-get install valgrind
+            valgrind --suppressions=.valgrind-suppress.supp --leak-check=full --gen-suppressions=all \
+              --track-origins=yes --error-exitcode=1 build/out/osqp_tester
+          if: ${{ runner.os == 'Linux' && matrix.cmake_flags == '-DUNITTESTS=ON -DCOVERAGE=ON' && matrix.cmake_flags_extra == '-DENABLE_MKL_PARDISO=OFF' }}
+
+        - name: Generate coverage
+          uses: imciner2/run-lcov@v1
+          with:
+            input_directory: '${{ runner.workspace }}/osqp/build'
+            exclude: '"$GITHUB_WORKSPACE/tests/*" "$GITHUB_WORKSPACE/lin_sys/direct/qdldl/amd/*" "$GITHUB_WORKSPACE/lin_sys/direct/qdldl/qdldl_sources/*" "/usr/include/*"'
+            output_file: '${{ runner.workspace }}/osqp/build/coverage.info'
+          if: ${{ runner.os == 'Linux' && matrix.cmake_flags == '-DUNITTESTS=ON -DCOVERAGE=ON' && matrix.cmake_flags_extra == '' }}
+
+        - name: Coveralls
+          uses: coverallsapp/github-action@master
+          with:
+            path-to-lcov: '${{ runner.workspace }}/osqp/build/coverage.info'
+            github-token: ${{ secrets.GITHUB_TOKEN }}
+          if: ${{ runner.os == 'Linux' && matrix.cmake_flags == '-DUNITTESTS=ON -DCOVERAGE=ON' && matrix.cmake_flags_extra == '' }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..eb61467
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,117 @@
+name: Release
+
+on:
+  push:
+    tags:
+      - '*'
+
+jobs:
+
+  release:
+    runs-on: ${{ matrix.os }}
+
+    strategy:
+      fail-fast: false
+
+      matrix:
+        os: [ubuntu-latest, macos-latest, windows-latest]
+        python-version: [3.9]
+
+        include:
+          - os: ubuntu-latest
+            cmake_generator: "Unix Makefiles"
+          - os: macos-latest
+            cmake_generator: "Unix Makefiles"
+          - os: windows-latest
+            cmake_generator: "MinGW Makefiles"
+
+    defaults:
+      run:
+        # Required when using an activated conda environment in steps
+        # See https://github.com/conda-incubator/setup-miniconda#IMPORTANT
+        shell: bash -l {0}
+
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          lfs: false
+          submodules: recursive
+
+      - name: Set up conda
+        uses: conda-incubator/setup-miniconda@v2
+        with:
+          auto-update-conda: true
+          python-version: ${{ matrix.python-version }}
+
+      - name: Setup Envvars
+        run: |
+          echo "OSQP_VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
+          echo "OSQP_NAME=osqp-${GITHUB_REF/refs\/tags\//}-${{ runner.os }}" >> $GITHUB_ENV
+
+      # -----------------
+      # OS-specific setup
+      # -----------------
+      - name: Setup (Linux)
+        if: runner.os == 'Linux'
+        run: |
+          echo "LD_LIBRARY_PATH=$CONDA_PREFIX/lib" >> $GITHUB_ENV
+
+      - name: Setup (macOS)
+        if: runner.os == 'macOS'
+        # Newer versions of MacOS effectively block DYLD_LIBRARY_PATH being set (System Integrity Protection)
+        # Explicitly setting RPATH using `install_name_tool -add_rpath $CONDA_PREFIX/lib ./build/out/osqp_tester`
+        #   doesn't work either.
+        # Here we get around it by using a standard non-root location for .dylib files as a soft link
+        run: |
+          echo "DYLD_LIBRARY_PATH=$CONDA_PREFIX/lib" >> $GITHUB_ENV
+          ln -s $CONDA_PREFIX/lib ~/lib
+
+      - name: Setup (Windows)
+        if: runner.os == 'Windows'
+        run: |
+          echo "$CONDA_PREFIX/Library/bin" >> $GITHUB_PATH
+      # -----------------
+
+      # Fetching mkl from the anaconda channel instead of defaults gives us the MKL runtime dynamic libraries
+      # as well (mkl_rt.<dll/so>), required during the runtime testing steps.
+      # MKL on Anaconda 2021.* seems to have inexplicably renamed mkl_rt.dll to mkl_rt.1.dll, so we insist on
+      # a version earlier than 2021
+      - name: Install python dependencies
+        run: |
+          conda install -c anaconda "mkl<2021" numpy scipy
+          conda info
+          conda list
+
+      - name: Build
+        run: |
+          cmake -DOSQP_VERSION=$OSQP_VERSION -G "${{ matrix.cmake_generator }}" -S . -B build
+          cmake --build build
+
+      # useful for inspecting the OSQP version information
+      - name: OSQP Demo
+        run: |
+          ./build/out/osqp_demo
+
+      - name: Generate binary release file
+        run: |
+          mkdir -p $OSQP_NAME/lib $OSQP_NAME/include
+          cp LICENSE $OSQP_NAME/
+          cp include/*.h $OSQP_NAME/include/
+          cp build/out/libosqp.* $OSQP_NAME/lib/
+          cd $OSQP_NAME && tar -czvf ../$OSQP_NAME.tar.gz *
+
+      - name: Release binary
+        uses: softprops/action-gh-release@v1
+        with:
+          files: ${{env.OSQP_NAME}}.tar.gz
+
+      - name: Generate source release file
+        if: ${{ runner.os == 'Linux' }}
+        run: |
+          git ls-files --recurse-submodules | grep -v ^site/ | tar czf osqp-${{env.OSQP_VERSION}}-src.tar.gz --files-from -
+
+      - name: Release source
+        if: ${{ runner.os == 'Linux' }}
+        uses: softprops/action-gh-release@v1
+        with:
+          files: osqp-${{env.OSQP_VERSION}}-src.tar.gz
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0f28cd4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,218 @@
+# C language
+# -------------------------------------------------------------------
+# Compile commands for cquery
+compile_commands.json
+
+# Out folder
+out/
+
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+
+# Code coverage
+*.gcda
+*.gcno
+coverage.info
+coverage_html/
+
+# Doxygen output
+docs/doxygen_out
+
+# Editor files
+# -------------------------------------------------------------------
+*.swp
+.ycm_extra_conf.py
+*.swo
+*.vscode/
+
+
+
+# Gurobi
+# -------------------------------------------------------------------
+gurobi.log
+
+
+# Tags
+# -------------------------------------------------------------------
+tags
+.tags
+.tags1
+TAGS
+
+# Python Language
+# -------------------------------------------------------------------
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# IPython Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# dotenv
+.env
+
+# virtualenv
+.venv/
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+
+# Rope project settings
+.ropeproject
+
+# Python 3 porting backup files
+*.bak
+
+# Mac OSX Files
+# -------------------------------------------------------------------
+.DS_Store
+
+
+# Matlab
+# -------------------------------------------------------------------
+# Files generated during compilation
+*.o
+*.m~
+*.mexmaci64*
+*.mexw64*
+*.mexa64*
+out/
+*.asv
+
+# Other data files
+# *.mat
+
+# Emacs
+\#*\#
+.\#*
+*~
+
+# Clang completer
+# -------------------------------------------------------------------
+.clang_complete
+
+# CMake Files
+# -------------------------------------------------------------------
+*.stamp
+
+.cquery_cached_index
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..00e6fcd
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "lin_sys/direct/qdldl/qdldl_sources"]
+	path = lin_sys/direct/qdldl/qdldl_sources
+	url = https://github.com/osqp/qdldl.git
diff --git a/.valgrind-suppress.supp b/.valgrind-suppress.supp
new file mode 100644
index 0000000..30c581a
--- /dev/null
+++ b/.valgrind-suppress.supp
@@ -0,0 +1,57 @@
+{
+   ignore_get_max_threads_intel
+   Memcheck:Param
+   sched_setaffinity(mask)
+   fun:syscall
+   fun:__kmp_affinity_determine_capable
+   fun:_Z20__kmp_env_initializePKc
+   fun:__kmp_middle_initialize
+   fun:omp_get_num_procs@@VERSION
+   fun:mkl_serv_domain_get_max_threads
+   obj:*
+}
+
+{
+   ignore mkl_get_max_threads
+   Memcheck:Cond
+   fun:mkl_serv_domain_get_max_threads
+   obj:*
+}
+
+{
+   ignore_libiomp_intel
+   Memcheck:Cond
+   fun:__intel_sse2_strrchr
+   fun:*_INTERNAL_45_______src_thirdparty_tbb_omp_dynamic_link_cpp_*
+   fun:__sti__$E
+   obj:*libiomp*.so
+}
+
+
+{
+   mkl_malloc
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   fun:_dl_signal_error
+   fun:_dl_open
+   fun:dlopen_doit
+   fun:_dl_catch_error
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.2.5
+   fun:mkl_serv_inspector_suppress
+   fun:mkl_serv_lock
+   fun:mkl_serv_core_register_cleanup
+   fun:__sti__$E
+   fun:call_init.part.0
+}
+
+{
+   another_intel
+   Memcheck:Addr16
+   fun:__intel_sse2_strrchr
+   fun:_ZN67_INTERNAL_45_______src_thirdparty_tbb_omp_dynamic_link_cpp_d77f5ef55__kmp12init_dl_dataEv
+   fun:__sti__$E
+   obj:*libiomp5.so
+   obj:*libiomp5.so
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..b6774b3
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,112 @@
+Version 0.6.2 (6 January 2021)
+------------------------------
+* Fix segfault python multithreading
+* Compatibility python 3.9
+* Updated QDLDL to version 0.1.5
+* Drop Python 2.7 support
+
+
+
+Version 0.6.0 (30 August 2019)
+------------------------------
+* Added meaningful return values to internal functions. Changed syntax of `osqp_setup` function. It now returns an exitflag.
+* `osqp_setup` function requires `P` to be upper triangular. It returns a nonzero exitflag otherwise.
+* Custom memory allocators via cmake and the configure file.
+* Changed interfaces to linsys solver functions. The solve function now computes `(x_tilde,z_tilde)` instead of `(x_tilde,nu)`. This allows to implement custom linear system solvers (also indirect).
+* Added `solve` function in Python interface that performs `setup` `solve` and `cleanup` for you directly and disables GIL.
+* Improved code generation folder structure.
+
+
+Version 0.5.0 (10 December 2018)
+----------------
+* Added `update_time` to the info structure.
+* Fixed [#101](https://github.com/osqp/osqp/issues/101).
+
+
+Version 0.4.1 (25 September 2018)
+---------------------------------
+* Updated QDLDL to version 0.1.3.
+* Added check for nonconvex cost function (non-positive semidefinite `P`) after factorization is performed.
+* Added complete sources distribution on bintray.com (including QDLDL).
+
+
+Version 0.4.0 (23 July 2018)
+----------------------------
+* Added check for nonconvex cost function (non-positive semidefinite `P`).
+* Removed SuiteSparse LDL in favor of [QDLDL](https://github.com/osqp/qdldl).
+* Static library `libosqpstatic` now renamed as `libosqp`.
+
+
+Version 0.3.1 (10 June 2018)
+----------------------------
+* Fixed [#62](https://github.com/osqp/osqp/issues/62).
+* Moved interfaces to separate repositories
+* Fixed [#54](https://github.com/osqp/osqp/issues/54).
+* Changes to support Matlab 2018a
+* Added support for new interface in R
+
+Version 0.3.0 (5 March 2018)
+----------------------------
+* Added `time_limit` option
+* Added CUTEst interface
+* Fixed bug in upper triangular `P` extraction. Now the solver can accept both complete `P` matrix or just the upper triangular part.
+* Fixed [#33](https://github.com/osqp/osqp/issues/33)
+* Fixed [#34](https://github.com/osqp/osqp/issues/34)
+* Allow `eps_rel=0` [#40](https://github.com/osqp/osqp/issues/40)
+* Fixed bug when calling `osqp_solve` or `osqp_cleanup` after failed linear system initialization
+* Add "install" CMake target and installation of CMake configuration files
+* Fixed potential name conflict with SCS [47](https://github.com/osqp/osqp/issues/47)
+* Changed `set_default_settings` to `osqp_set_default_settings` and brought function to main API header `osqp.h`
+* Fixed [#49](https://github.com/osqp/osqp/issues/49)
+
+
+Version 0.2.1 (25 November 2017)
+---------------------------------
+* Fixed problem with code generation and pypi `data_files` (everything now in [MANIFEST.in](https://github.com/pypa/sampleproject/issues/30))
+
+
+Version 0.2.0 (23 November 2017)
+---------------------------------
+*   Added adaptive rho -> Much more reliable convergence!
+*   Simplified several settings
+    *  "early_terminate" and "early_terminate_interval" -> "check_termination"
+    *  "scaling_iter" removed and put inside "scaling" parameter
+*   Julia interface [OSQP.jl](https://github.com/osqp/OSQP.jl)
+*   Shared libraries available on bintray.com
+*   Added inaccurate return statuses
+*   Added new object-oriented structure for linear system solvers
+*   Added MKL Pardiso interface using shared dynamic library loader
+*   Added diagonal rho vector with different values for equality/inequality constraints (interface still have scalar rho)
+*   Return certificates of infeasibility in results structure
+*   Now code generation produces a static library
+
+
+Version 0.1.3 (21 September 2017)
+---------------------------------
+* Fixed sources distribution on Python
+
+
+Version 0.1.2 (20 July 2017)
+------------------------------
+*   Added option to terminate with scaled or unscaled residual
+*   Now Matlab interface does support logical entries for the settings
+*   Fixed bug in index ordering of sparse matrices of Python interface
+*   Changed 2-norms to inf-norms
+*   Fixed code generation bug when scaling is disabled [#7](https://github.com/osqp/osqp/issues/7)
+*   Removed warnings in code-generation for standard <= C99
+*   Fixed MATLAB 2015b compatibility [#6](https://github.com/osqp/osqp/issues/6)
+
+
+Version 0.1.1 (11 April 2017)
+-----------------------------
+*   Fixed crashes during polishing when factorization fails
+*   Added package to Pypi
+*   Fixed relative paths Matlab
+
+
+Version 0.1.0 (10 April 2017)
+-----------------------------
+*   Linux, Mac and Windows
+*   Interface to Python 2.7, 3.5+
+*   Interface to Matlab 2015b+
+*   Embedded code generation
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 0000000..8b18b87
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,39 @@
+cff-version: 1.2.0
+message: "If you use this software, please cite the article from the preferred-citation below."
+title: "OSQP Solver"
+authors:
+  - family-names: "Stellato"
+    given-names: "Bartolomeo"
+  - family-names: "Banjac"
+    given-names: "Goran"
+  - family-names: "Goulart"
+    given-names: "Paul"
+  - family-names: "Bemporad"
+    given-names: "Alberto"
+  - family-names: "Boyd"
+    given-names: "Stephen"
+url: "https://github.com/osqp/osqp"
+
+# This is the main OSQP paper that describes the algorithm
+preferred-citation:
+  type: article
+  authors:
+  - family-names: "Stellato"
+    given-names: "Bartolomeo"
+  - family-names: "Banjac"
+    given-names: "Goran"
+  - family-names: "Goulart"
+    given-names: "Paul"
+  - family-names: "Bemporad"
+    given-names: "Alberto"
+  - family-names: "Boyd"
+    given-names: "Stephen"
+  title: "OSQP: an operator splitting solver for quadratic programs"
+  journal: "Mathematical Programming Computation"
+  start: 637 # First page number
+  end: 672 # Last page number
+  volume: 12
+  issue: 4
+  year: 2020
+  doi: 10.1007/s12532-020-00179-2
+  url: https://doi.org/10.1007/s12532-020-00179-2
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..afd7bb9
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,452 @@
+# Minimum version required
+cmake_minimum_required (VERSION 3.2)
+
+# Project name
+project (osqp)
+
+set(OSQP_VERSION "0.0.0" CACHE STRING "The version number of OSQP")
+if(NOT OSQP_VERSION STREQUAL "0.0.0")
+    configure_file (
+        "${PROJECT_SOURCE_DIR}/configure/version.h.in"
+        "${PROJECT_SOURCE_DIR}/include/version.h"
+    )
+endif()
+
+# Export compile commands
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+# Set the output folder where your program will be created
+set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/out)
+set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/out)
+
+# Some non-standard CMake modules
+LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/configure/cmake)
+INCLUDE(FindPythonModule)
+INCLUDE(Utils)
+# include(FindMKL)  # Find MKL module
+
+
+# Detect operating system
+# ----------------------------------------------
+message(STATUS "We are on a ${CMAKE_SYSTEM_NAME} system")
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+    set(IS_LINUX ON)
+elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+    set(IS_MAC ON)
+elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+    set(IS_WINDOWS ON)
+endif()
+
+
+
+# Set options
+# ----------------------------------------------
+
+# Are unittests generated?
+option (UNITTESTS "Enable unittests generation" OFF)
+
+# Is the code generated for embedded platforms?
+#   1 :   Yes. Matrix update not allowed.
+#   2 :   Yes. Matrix update allowed.
+if (NOT DEFINED EMBEDDED)
+    message(STATUS "Embedded is OFF")
+else()
+    message(STATUS "Embedded is ${EMBEDDED}")
+endif()
+
+# Is printing enabled?
+option (PRINTING "Enable solver printing" ON)
+if (DEFINED EMBEDDED)
+    message(STATUS "Disabling printing for embedded")
+    set(PRINTING OFF)
+endif()
+message(STATUS "Printing is ${PRINTING}")
+
+
+# Is profiling enabled?
+option (PROFILING "Enable solver profiling (timing)" ON)
+if (DEFINED EMBEDDED)
+    message(STATUS "Disabling profiling for embedded")
+    set(PROFILING OFF)
+endif()
+message(STATUS "Profiling is ${PROFILING}")
+
+# Is user interrupt enabled?
+option (CTRLC "Enable user interrupt (Ctrl-C)" ON)
+if (DEFINED EMBEDDED)
+    message(STATUS "Disabling user interrupt for embedded")
+    set(CTRLC OFF)
+endif()
+message(STATUS "User interrupt is ${CTRLC}")
+
+# Use floats instead of integers
+option (DFLOAT "Use float numbers instead of doubles" OFF)
+message(STATUS "Floats are ${DFLOAT}")
+
+# Use long integers for indexing
+option (DLONG "Use long integers (64bit) for indexing" ON)
+if (NOT (CMAKE_SIZEOF_VOID_P EQUAL 8))
+	message(STATUS "Disabling long integers (64bit) on 32bit machine")
+	set(DLONG OFF)
+endif()
+message(STATUS "Long integers (64bit) are ${DLONG}")
+
+
+option (DEBUG "Debug mode" OFF)
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+	set (DEBUG ON)
+	message(STATUS "Debug mode is ${DEBUG}")
+endif()
+
+# Add code coverage
+option (COVERAGE "Perform code coverage" OFF)
+message(STATUS "Code coverage is ${COVERAGE}")
+
+
+# Memory allocators
+# ----------------------------------------------
+
+#Report on custom user header options.  This is intended to allow
+#users to provide definitions of their own memory functions
+# The header should define the functions as follows
+#
+# define c_malloc mymalloc
+# define c_calloc mycalloc
+# define c_realloc myrealloc
+# define c_free myfree
+
+if(OSQP_CUSTOM_MEMORY)
+	message(STATUS "User custom memory management header: ${OSQP_CUSTOM_MEMORY}")
+endif()
+
+
+
+# Linear solvers dependencies
+# ---------------------------------------------
+option (ENABLE_MKL_PARDISO "Enable MKL Pardiso solver" ON)
+if (DFLOAT)
+	message(STATUS "Disabling MKL Pardiso Solver with floats")
+	set(ENABLE_MKL_PARDISO OFF)
+elseif(DEFINED EMBEDDED)
+	message(STATUS "Disabling MKL Pardiso Solver for embedded")
+	set(ENABLE_MKL_PARDISO OFF)
+endif()
+message(STATUS "MKL Pardiso: ${ENABLE_MKL_PARDISO}")
+
+
+# Generate header file with the global options
+# ---------------------------------------------
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure/osqp_configure.h.in
+               ${CMAKE_CURRENT_SOURCE_DIR}/include/osqp_configure.h
+               NEWLINE_STYLE LF)
+
+# Set Compiler flags
+# ----------------------------------------------
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)  # -fPIC
+
+
+if (NOT MSVC)
+
+    if (COVERAGE)
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
+	if(FORTRAN)
+		set(CMAKE_FORTRAN_FLAGS "${CMAKE_FORTRAN_FLAGS} --coverage")
+	endif(FORTRAN)
+    endif()
+
+    if (DEBUG)
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g")
+    else()
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
+    endif()
+
+    set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} -lm")      # Include math
+    # Include real time library in linux
+    if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+        set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} -lrt -ldl")
+    endif()
+endif (NOT MSVC)
+
+# Set sources and includes
+# ----------------------------------------------
+add_subdirectory (src)
+add_subdirectory (include)
+
+
+# if we are building the Python interface, let's look for Python
+# and set some options
+# -----------------------------------------------------------------
+if (PYTHON)
+
+    # Python include directories need to be passed by the python compilation process
+    if (NOT PYTHON_INCLUDE_DIRS)
+            message( FATAL_ERROR "You need Python include directories to build the Python interface" )
+    endif (NOT PYTHON_INCLUDE_DIRS)
+
+    # Include directories for Python headers
+    include_directories(${PYTHON_INCLUDE_DIRS})
+
+    # Pass PYTHON flag to C compiler
+    add_definitions(-DPYTHON)
+
+    if (UNITTESTS)
+        # Disable unittests
+        message(STATUS "Disabling UNITTESTS because we are building Python interface")
+        set(UNITTESTS OFF)
+    endif (UNITTESTS)
+
+endif (PYTHON)
+
+
+# if we are building the Matlab interface, let's look for Matlab
+# and set some options
+# -----------------------------------------------------------------
+if (MATLAB)
+
+    find_package(Matlab)
+
+    if (NOT Matlab_FOUND)
+        message( FATAL_ERROR "You need Matlab libraries to build the Matlab interface" )
+    endif (NOT Matlab_FOUND)
+
+    # Include directories for Matlab headers
+    include_directories(${Matlab_INCLUDE_DIRS})
+
+    message(STATUS "Matlab root is " ${Matlab_ROOT_DIR})
+
+    # Pass MATLAB flag to C compiler
+    add_definitions(-DMATLAB)
+
+    # Insist on the pre 2018 complex data API
+    # so that mxGetPr will work correctly
+    add_definitions(-DMATLAB_MEXSRC_RELEASE=R2017b)
+
+    message(STATUS "Using Matlab pre-2018a API for mxGetPr compatibility")
+
+    if (UNITTESTS)
+        # Disable unittests
+        message(STATUS "Disabling UNITTESTS because we are building Matlab interface")
+        set(UNITTESTS OFF)
+    endif (UNITTESTS)
+
+endif (MATLAB)
+
+# if we are building the R interface, let's look for R
+# and set some options
+# -----------------------------------------------------------------
+if (R_LANG)
+
+    message(STATUS "We are building the R interface")
+
+    # Look for R libraries
+    find_package(R)
+
+    if (NOT R_FOUND)
+            message( FATAL_ERROR "You need R libraries to build the R interface" )
+    endif (NOT R_FOUND)
+
+    message(STATUS "R exec is: " ${R_EXEC})
+    message(STATUS "R root dir is: " ${R_ROOT_DIR})
+    message(STATUS "R includes are in: " ${R_INCLUDE_DIRS})
+
+    # Include directories for R headers
+    include_directories(${R_INCLUDE_DIRS})
+
+    # Pass R_LANG flag to C compiler
+    add_definitions(-DR_LANG)
+
+    if (UNITTESTS)
+        # Disable unittests
+        message(STATUS "Disabling UNITTESTS because we are building the R interface")
+        set(UNITTESTS OFF)
+    endif (UNITTESTS)
+
+endif (R_LANG)
+
+
+# Create Static Library
+# ----------------------------------------------
+
+# Add linear system solvers cumulative library
+add_subdirectory(lin_sys)
+
+# Static library
+add_library (osqpstatic STATIC ${osqp_src} ${osqp_headers} ${linsys_solvers})
+# Give same name to static library output
+set_target_properties(osqpstatic PROPERTIES OUTPUT_NAME osqp)
+
+# Include directories for linear system solvers
+target_include_directories(osqpstatic PRIVATE ${linsys_solvers_includes})
+
+# Declare include directories for the cmake exported target
+target_include_directories(osqpstatic
+                           PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
+                                  "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}/osqp>")
+
+# Install Static Library
+# ----------------------------------------------
+
+include(GNUInstallDirs)
+
+install(TARGETS osqpstatic
+        EXPORT  ${PROJECT_NAME}
+        ARCHIVE       DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        LIBRARY       DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+        RUNTIME       DESTINATION "${CMAKE_INSTALL_BINDIR}")
+
+
+# Install Headers
+# ----------------------------------------------
+
+install(FILES ${osqp_headers} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/osqp")
+
+
+
+if (MATLAB)
+target_link_libraries (osqpstatic ${Matlab_LIBRARIES})
+endif (MATLAB)
+
+# If we are building Python/Matlab/R interface:
+#   - do not build shared library
+#   - do not build demo
+if (NOT PYTHON AND NOT MATLAB AND NOT R_LANG AND NOT EMBEDDED)
+    # Create osqp shared library
+    # NB: Add all the linear system solvers here
+    add_library (osqp SHARED ${osqp_src} ${osqp_headers} ${linsys_solvers})
+
+    # Include directories for linear system solvers
+    target_include_directories(osqp PRIVATE ${linsys_solvers_includes})
+
+    # Declare include directories for the cmake exported target
+    target_include_directories(osqp
+                               PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
+                                      "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}/osqp>")
+
+    # Install osqp shared library
+    install(TARGETS osqp
+            EXPORT  ${PROJECT_NAME}
+            LIBRARY       DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+            ARCHIVE       DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+            RUNTIME       DESTINATION "${CMAKE_INSTALL_BINDIR}")
+
+    # Create demo executable (linked to static library)
+    add_executable (osqp_demo ${PROJECT_SOURCE_DIR}/examples/osqp_demo.c)
+    target_link_libraries (osqp_demo osqpstatic)
+
+endif (NOT PYTHON AND NOT MATLAB AND NOT R_LANG AND NOT EMBEDDED)
+
+# Create CMake packages for the build directory
+# ----------------------------------------------
+include(CMakePackageConfigHelpers)
+
+export(EXPORT ${PROJECT_NAME}
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/osqp-targets.cmake"
+  NAMESPACE osqp::)
+
+if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/osqp-config.cmake)
+  file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/osqp-config.cmake "include(\"\${CMAKE_CURRENT_LIST_DIR}/osqp-targets.cmake\")\n")
+endif()
+
+
+# Create CMake packages for the install directory
+# ----------------------------------------------
+
+set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/osqp)
+
+install(EXPORT ${PROJECT_NAME}
+        FILE osqp-targets.cmake
+        NAMESPACE osqp::
+        DESTINATION ${ConfigPackageLocation})
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/osqp-config.cmake
+        DESTINATION ${ConfigPackageLocation})
+
+
+
+# Add uninstall command
+# ----------------------------------------------
+if(NOT TARGET uninstall)
+    configure_file(
+        "${CMAKE_CURRENT_SOURCE_DIR}/configure/cmake/cmake_uninstall.cmake.in"
+        "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
+        IMMEDIATE @ONLY)
+
+    add_custom_target(uninstall
+        COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
+endif()
+
+
+
+# Add testing
+# ----------------------------------------------
+# Add custom command to generate tests
+if (UNITTESTS)
+    find_package(PythonInterp)
+    if(NOT PYTHONINTERP_FOUND)
+        message( FATAL_ERROR "You need python installed to generate unittests. If you do not want to compile the unittests pass -DUNITTESTS=OFF to cmake." )
+    endif()
+
+    INCLUDE(FindPythonModule)
+    find_python_module(numpy)
+    IF(NOT NUMPY_FOUND)
+        message( FATAL_ERROR "You need numpy python module installed to generate unittests. If you do not want to compile the unittests pass -DUNITTESTS=OFF to cmake." )
+    ENDIF()
+
+    find_python_module(scipy)
+    # Check scipy version for sparse.random functionalities
+    IF((NOT SCIPY_FOUND) OR (SCIPY_VERSION VERSION_LESS 0.17.0))
+        message( FATAL_ERROR "You need scipy python module installed to generate unittests. If you do not want to compile the unittests pass -DUNITTESTS=OFF to cmake." )
+    ENDIF()
+
+    find_python_module(__future__)
+    IF(NOT __FUTURE___FOUND)
+        message( FATAL_ERROR "You need future python module installed to generate unittests. If you do not want to compile the unittests pass -DUNITTESTS=OFF to cmake." )
+    ENDIF()
+
+    # Add test_headers and codegen_test_headers
+    add_subdirectory(tests)
+
+    # Generating tests.stamp so that the test data are not always generated
+    # set(data_timestamp ${PROJECT_SOURCE_DIR}/tests/tests_data.stamp)
+    add_custom_command(
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+        COMMAND ${PYTHON_EXECUTABLE} generate_tests_data.py
+        DEPENDS ${PROJECT_SOURCE_DIR}/tests/generate_tests_data.py
+        OUTPUT ${codegen_test_headers}
+        COMMENT "Generating unittests data files using Python"
+    )
+
+    # Direct linear solver testing
+    include_directories(tests)
+    add_executable(osqp_tester
+            ${PROJECT_SOURCE_DIR}/tests/osqp_tester.cpp
+            ${PROJECT_SOURCE_DIR}/tests/osqp_tester.h
+            ${test_headers}
+            ${codegen_test_headers}
+            )
+    set_target_properties(osqp_tester
+            PROPERTIES
+                CXX_STANDARD 11
+                CXX_STANDARD_REQUIRED YES
+                CXX_EXTENSIONS NO
+            )
+    target_link_libraries (osqp_tester osqpstatic ${CMAKE_DL_LIBS})
+
+    # Add custom memory target
+    add_executable(osqp_tester_custom_memory
+            EXCLUDE_FROM_ALL
+            ${PROJECT_SOURCE_DIR}/tests/osqp_tester.cpp
+            ${PROJECT_SOURCE_DIR}/tests/osqp_tester.h
+            ${test_headers}
+            ${codegen_test_headers}
+		   ${PROJECT_SOURCE_DIR}/tests/custom_memory/custom_memory.c
+		   ${PROJECT_SOURCE_DIR}/tests/custom_memory/custom_memory.h
+		)
+    target_link_libraries (osqp_tester_custom_memory osqpstatic ${CMAKE_DL_LIBS})
+
+    # Add testing
+    include(CTest)
+    enable_testing()
+    add_test(NAME tester COMMAND $<TARGET_FILE:osqp_tester>)
+endif()
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..999bae3
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2021 Bartolomeo Stellato, Goran Banjac, Paul Goulart, Stephen Boyd
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..dd7c53f
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,14 @@
+OSQP
+Copyright (c) 2019 Bartolomeo Stellato, Goran Banjac, Paul Goulart, Stephen Boyd
+
+This product includes software developed at Stanford University and at the University of Oxford.
+
+
+The following external modules are included in this software library
+
+QDLDL
+Copyright (c) 2018, Paul Goulart, Bartolomeo Stellato, Goran Banjac.
+
+AMD
+Copyright (c) 1996-2015, Timothy A. Davis, Patrick R. Amestoy, and Iain S. Duff.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5aaedea
--- /dev/null
+++ b/README.md
@@ -0,0 +1,44 @@
+# The Operator Splitting QP Solver
+
+[![CI](https://github.com/osqp/osqp/actions/workflows/main.yml/badge.svg)](https://github.com/osqp/osqp/actions/workflows/main.yml)
+[![Code coverage](https://coveralls.io/repos/github/osqp/osqp/badge.svg?branch=master)](https://coveralls.io/github/osqp/osqp?branch=master)
+![License](https://img.shields.io/badge/License-Apache%202.0-brightgreen.svg)
+
+
+![PyPI - downloads](https://img.shields.io/pypi/dm/osqp.svg?label=Pypi%20downloads)
+![Conda - downloads](https://img.shields.io/conda/dn/conda-forge/osqp.svg?label=Conda%20downloads)
+
+[**Join our forum on Discourse**](https://osqp.discourse.group) for any questions related to the solver!
+
+**The documentation** is available at [**osqp.org**](https://osqp.org/)
+
+The OSQP (Operator Splitting Quadratic Program) solver is a numerical optimization package for solving problems in the form
+```
+minimize        0.5 x' P x + q' x
+
+subject to      l <= A x <= u
+```
+
+where `x in R^n` is the optimization variable. The objective function is defined by a positive semidefinite matrix `P in S^n_+` and vector `q in R^n`. The linear constraints are defined by matrix `A in R^{m x n}` and vectors `l` and `u` so that `l_i in R U {-inf}` and `u_i in R U {+inf}` for all `i in 1,...,m`.
+
+
+The latest version is `0.6.2`.
+
+## Citing OSQP
+
+If you are using OSQP for your work, we encourage you to
+
+* [Cite the related papers](https://osqp.org/citing/),
+* Put a star on this repository.
+
+**We are looking forward to hearing your success stories with OSQP!** Please [share them with us](mailto:bartolomeo.stellato@gmail.com).
+
+
+## Bug reports and support
+
+Please report any issues via the [Github issue tracker](https://github.com/osqp/osqp/issues). All types of issues are welcome including bug reports, documentation typos, feature requests and so on.
+
+
+## Numerical benchmarks
+Numerical benchmarks against other solvers are available [here](https://github.com/osqp/osqp_benchmarks).
+
diff --git a/ROADMAP.md b/ROADMAP.md
new file mode 100644
index 0000000..ee24da5
--- /dev/null
+++ b/ROADMAP.md
@@ -0,0 +1,4 @@
+## TODO
+-   [ ] Add GPU solver
+-   [ ] Add indirect solver
+
diff --git a/configure/cmake/FindPythonModule.cmake b/configure/cmake/FindPythonModule.cmake
new file mode 100644
index 0000000..f890d4f
--- /dev/null
+++ b/configure/cmake/FindPythonModule.cmake
@@ -0,0 +1,94 @@
+# Find python module and version
+#
+# It sets the variables (given module called MODULE)
+# unless MODULE is __FUTURE__. In that case
+# it only checks if it has been found.
+#
+# MODULE_FOUND               - has the module been found?
+# MODULE_VERSION             - module version as a string
+# MODULE_VERSION_MAJOR       - major version number
+# MODULE_VERSION_MINOR       - minor version number
+# MODULE_VERSION_PATCH       - patch version number
+# MODULE_VERSION_DECIMAL     - e.g. version 1.6.1 is 10601
+#
+
+
+function(find_python_module module)
+    # Write module in upper and lower case
+	string(TOUPPER ${module} module_upper)
+	string(TOLOWER ${module} module_lower)
+
+    unset(${module_upper}_VERSION)
+    #
+    # if(ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED")
+    #     set(${module_upper}_FIND_REQUIRED TRUE)
+    # endif()
+
+    if(PYTHONINTERP_FOUND)
+        if (NOT ${module} STREQUAL "__future__")
+            execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
+              "import ${module_lower} as n; print(n.__version__);"
+              RESULT_VARIABLE __result
+              OUTPUT_VARIABLE __output
+              ERROR_QUIET
+              OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+              if(__result MATCHES 0)
+                string(REGEX REPLACE ";" "\\\\;" __values ${__output})
+                string(REGEX REPLACE "\r?\n" ";"    __values ${__values})
+                list(GET __values 0 ${module_upper}_VERSION)
+
+                string(REGEX MATCH "^([0-9])+\\.([0-9])+\\.([0-9])+" __ver_check "${${module_upper}_VERSION}")
+
+                if(NOT "${__ver_check}" STREQUAL "")
+                  set(${module_upper}_VERSION_MAJOR ${CMAKE_MATCH_1})
+                  set(${module_upper}_VERSION_MINOR ${CMAKE_MATCH_2})
+                  set(${module_upper}_VERSION_PATCH ${CMAKE_MATCH_3})
+                  math(EXPR ${module_upper}_VERSION_DECIMAL
+                    "(${CMAKE_MATCH_1} * 10000) + (${CMAKE_MATCH_2} * 100) + ${CMAKE_MATCH_3}")
+                else()
+                 unset(${module_upper}_VERSION)
+                 message(STATUS "Requested ${module_lower} version, but got instead:\n${__output}\n")
+                endif()
+
+                find_package_handle_standard_args(${module_upper}
+                    FOUND_VAR ${module_upper}_FOUND
+                    REQUIRED_VARS ${module_upper}_VERSION
+                    VERSION_VAR  ${module_upper}_VERSION)
+
+              endif()
+        else()
+            execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
+              "import ${module_lower} as n"
+              RESULT_VARIABLE __result
+              OUTPUT_VARIABLE __output
+              ERROR_QUIET
+              OUTPUT_STRIP_TRAILING_WHITESPACE)
+              if(NOT __result)
+                  set(${module_upper}_FOUND ON)
+              endif()
+              message(STATUS "Found Python __FUTURE__")
+        endif()
+
+
+    else()
+        message(STATUS "Python interpreter not found. To find ${module} you need the Python interpreter.")
+    endif()
+
+
+    # Set variables in parent scope
+    if(${module_upper}_FOUND)
+        set(${module_upper}_FOUND ON PARENT_SCOPE)
+        if (NOT ${module} STREQUAL "__future__")
+            set(${module_upper}_VERSION ${${module_upper}_VERSION} PARENT_SCOPE)
+            set(${module_upper}_VERSION_MAJOR ${${module_upper}_VERSION_MAJOR} PARENT_SCOPE)
+            set(${module_upper}_VERSION_MINOR ${${module_upper}_VERSION_MINOR} PARENT_SCOPE)
+            set(${module_upper}_VERSION_PATCH ${${module_upper}_VERSION_PATCH} PARENT_SCOPE)
+            set(${module_upper}_VERSION_DECIMAL ${${module_upper}_VERSION_DECIMAL} PARENT_SCOPE)
+        endif()
+    endif()
+
+    # Clear variables
+    osqp_clear_vars(__result __output __values __ver_check)
+
+endfunction(find_python_module)
diff --git a/configure/cmake/FindR.cmake b/configure/cmake/FindR.cmake
new file mode 100644
index 0000000..5a39077
--- /dev/null
+++ b/configure/cmake/FindR.cmake
@@ -0,0 +1,34 @@
+# CMake module to find R
+# - Try to find R.  If found, defines:
+#
+#  R_FOUND        - system has R
+#  R_EXEC         - the system R command
+#  R_ROOT_DIR     - the R root directory
+#  R_INCLUDE_DIRS - the R include directories
+
+set(TEMP_CMAKE_FIND_APPBUNDLE ${CMAKE_FIND_APPBUNDLE})
+set(CMAKE_FIND_APPBUNDLE "NEVER")
+find_program(R_EXEC NAMES R R.exe)
+set(CMAKE_FIND_APPBUNDLE ${TEMP_CMAKE_FIND_APPBUNDLE})
+
+#---Find includes and libraries if R exists
+if(R_EXEC)
+
+  set(R_FOUND TRUE)
+
+  if((CMAKE_HOST_SOLARIS) AND (DEFINED ENV{R_HOME}))
+      message(STATUS "Unsetting R_HOME on Solaris.")
+      unset(ENV{R_HOME})
+  endif()
+
+  execute_process(WORKING_DIRECTORY .
+  COMMAND ${R_EXEC} RHOME
+  OUTPUT_VARIABLE R_ROOT_DIR
+  OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+  find_path(R_INCLUDE_DIRS R.h
+            PATHS /usr/local/lib /usr/local/lib64 /usr/share /usr/include ${R_ROOT_DIR} PATH_SUFFIXES include R R/include)
+
+endif()
+
+mark_as_advanced(R_FOUND R_EXEC R_ROOT_DIR R_INCLUDE_DIRS)
diff --git a/configure/cmake/Utils.cmake b/configure/cmake/Utils.cmake
new file mode 100644
index 0000000..ac4cc71
--- /dev/null
+++ b/configure/cmake/Utils.cmake
@@ -0,0 +1,8 @@
+# Clears variables from list
+# Usage:
+#   osqp_clear_vars(<variables_list>)
+macro(osqp_clear_vars)
+  foreach(_var ${ARGN})
+    unset(${_var})
+  endforeach()
+endmacro()
diff --git a/configure/cmake/cmake_uninstall.cmake.in b/configure/cmake/cmake_uninstall.cmake.in
new file mode 100644
index 0000000..4457d1b
--- /dev/null
+++ b/configure/cmake/cmake_uninstall.cmake.in
@@ -0,0 +1,21 @@
+if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
+endif(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
+
+file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+foreach(file ${files})
+  message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+  if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    exec_program(
+      "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+      OUTPUT_VARIABLE rm_out
+      RETURN_VALUE rm_retval
+      )
+    if(NOT "${rm_retval}" STREQUAL 0)
+      message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+    endif(NOT "${rm_retval}" STREQUAL 0)
+  else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+  endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+endforeach(file)
\ No newline at end of file
diff --git a/configure/osqp_configure.h.in b/configure/osqp_configure.h.in
new file mode 100644
index 0000000..6df0ed5
--- /dev/null
+++ b/configure/osqp_configure.h.in
@@ -0,0 +1,49 @@
+#ifndef OSQP_CONFIGURE_H
+# define OSQP_CONFIGURE_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* ifdef __cplusplus */
+
+/* DEBUG */
+#cmakedefine DEBUG
+
+/* Operating system */
+#cmakedefine IS_LINUX
+#cmakedefine IS_MAC
+#cmakedefine IS_WINDOWS
+
+/* EMBEDDED */
+#cmakedefine EMBEDDED (@EMBEDDED@)
+
+/* PRINTING */
+#cmakedefine PRINTING
+
+/* PROFILING */
+#cmakedefine PROFILING
+
+/* CTRLC */
+#cmakedefine CTRLC
+
+/* DFLOAT */
+#cmakedefine DFLOAT
+
+/* DLONG */
+#cmakedefine DLONG
+
+/* ENABLE_MKL_PARDISO */
+#cmakedefine ENABLE_MKL_PARDISO
+
+/* MEMORY MANAGEMENT */
+#cmakedefine OSQP_CUSTOM_MEMORY
+#ifdef OSQP_CUSTOM_MEMORY
+#include "@OSQP_CUSTOM_MEMORY@"
+#endif
+
+
+
+# ifdef __cplusplus
+}
+# endif /* ifdef __cplusplus */
+
+#endif /* ifndef OSQP_CONFIGURE_H */
diff --git a/configure/version.h.in b/configure/version.h.in
new file mode 100644
index 0000000..121d089
--- /dev/null
+++ b/configure/version.h.in
@@ -0,0 +1,3 @@
+#ifndef OSQP_VERSION
+#define OSQP_VERSION "@OSQP_VERSION@"
+#endif
\ No newline at end of file
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..61564e9
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,24 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+SPHINXPROJ    = osqp
+SOURCEDIR     = .
+BUILDDIR      = _build
+HTMLCOPYDIR   = _build/html
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+ifneq ("$(HTMLCOPYDIR)", "$(BUILDDIR)/html")
+	cp -rT $(BUILDDIR)/html $(HTMLCOPYDIR)
+endif
\ No newline at end of file
diff --git a/docs/_static/css/osqp_theme.css b/docs/_static/css/osqp_theme.css
new file mode 100644
index 0000000..161e526
--- /dev/null
+++ b/docs/_static/css/osqp_theme.css
@@ -0,0 +1,198 @@
+/* Default header fonts are ugly */
+@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,400i,600,600i,700,700i);
+@import url(https://fonts.googleapis.com/css?family=Inconsolata);
+
+h1, h2, h3, h4, h5, h6 {
+    font-family:  "Open Sans", "Helvetica Neue",Helvetica,Arial,sans-serif;
+    font-weight: 600;
+}
+body {
+  font-family: 'Open Sans', sans-serif;
+}
+
+.wy-nav-top a {
+    font-size: 175%;
+    font-weight: 600;
+}
+
+/* Remove rounded navigation bar */
+.wy-side-nav-search input[type=text] {
+    border-radius: 2px;
+}
+
+div[class^='highlight'] pre {
+    font-family: 'Inconsolata', monospace;
+    font-size: 16px;
+    background: #f3f6f6;
+}
+
+/* Make glossary entries bolder to stand out */
+dl.glossary>dt{
+    font-weight: 700;
+}
+
+/* Make bold things less bold */
+b, strong {
+    font-weight: 600;
+}
+
+/* Less bold table head */
+.wy-table thead th, .rst-content table.docutils thead th, .rst-content table.field-list thead th {
+    font-weight: 600;
+}
+
+/* Less bold current active element */
+.wy-menu-vertical li.on a, .wy-menu-vertical li.current>a {
+    font-weight: 600;
+}
+
+
+/* Types link refs less bold */
+.rst-content tt.xref, a .rst-content tt, .rst-content tt.xref, .rst-content code.xref, a .rst-content tt, a .rst-content code {
+    font-weight: normal;
+}
+
+/* Make types in C docs less bold */
+.rst-content dl dt {
+    font-weight: 600;
+}
+
+
+/* Link Styling */
+a,a:visited, a:focus {
+	color: rgb(46, 118, 176);  /* Light blue*/
+	text-decoration: none;
+}
+a:hover, a:active {
+	color: rgb(0, 32, 72);  /*  blue*/
+	text-decoration: none;
+}
+
+
+
+/* Make bar oxford blue*/
+.wy-side-nav-search {
+    background-color: rgb(0, 32, 72); /* Oxford blue */
+}
+
+.wy-nav-side {
+    background: rgb(0, 32, 72); /* Oxford blue */
+}
+
+
+.wy-nav-content-wrap, .wy-menu li.current > a  {
+    background-color: #fff;
+    /* font-weight: 600; */
+}
+
+@media screen and (min-width: 1400px) {
+    .wy-nav-content-wrap {
+        /*background-color: rgba(0, 0, 0, 0.0470588);*/
+        background-color: rgb(245, 245, 245); /* Light grey */
+    }
+
+    .wy-nav-content {
+        background-color: #fff;
+    }
+}
+
+.wy-nav-top {
+    background-color: rgb(0, 32, 72); /* Oxford blue */
+}
+
+
+/* Change color of the line at the top of Read the Docs box at the bottom left
+   corner */
+.rst-versions{
+    border-top:solid 10px rgb(0, 32, 72); /* Oxford blue */
+}
+
+
+/* Change color of top left name */
+.wy-side-nav-search>a{
+    color: rgb(255, 255, 255); /* White text*/
+}
+.wy-side-nav-search>div.version{
+    color: rgb(245, 245, 245); /* Light grey text*/
+}
+.wy-nav-top a{
+    color: rgb(255, 255, 255); /* White text*/
+}
+
+
+
+/* Navigation bar text */
+.wy-menu-vertical header, .wy-menu-vertical p.caption {
+    color: rgb(255, 255, 255); /* White text*/
+    font-weight: 600;
+}
+
+.wy-menu-vertical a{
+    color: rgb(178, 188, 200); /* Grey */
+}
+
+.wy-menu-vertical a:hover{
+    background: rgb(46, 118, 176);
+    color: rgb(255, 255, 255); /* White text*/
+}
+
+
+
+
+
+
+/* Change text color for tt literal */
+.rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal{
+    color: rgb(46, 118, 176);  /* Light blue*/
+    font-size: 14px;
+}
+
+tr td code span{
+   font-size: 13px;
+}
+
+.s1, .s2{
+	color: rgb(46, 118, 176);  /* Light blue*/
+}
+
+
+/* Rubric bigger (initial page) */
+.rst-content p.rubric {
+    font-family:  "Open Sans", "Helvetica Neue",Helvetica,Arial,sans-serif;
+    font-weight: 600;
+    font-size: 150%;
+    margin-bottom: 20px;
+}
+
+/* Smaller rubric in C description */
+p.breathe-sectiondef-title.rubric {
+	font-size: 100%;
+}
+
+
+
+
+/* Remove box shadow from buttons */
+.btn {
+    font-weight: normal;
+    font-family:  "Open Sans", "Helvetica Neue",Helvetica,Arial,sans-serif;
+    box-shadow: none;
+}
+/* Force breathe display inline parameters */
+/*.wy-plain-list-disc li p:last-child, .rst-content .section ul li p:last-child, .rst-content .toctree-wrapper ul li p:last-child, article ul li p:last-child{
+    display: inline;
+}*/
+
+
+/* Remove line block indentation */
+.rst-content .line-block {
+    margin-bottom: 0px !important;
+    margin-left: 0px !important;
+}
+
+
+/* Margin below tables */
+table p {
+    margin-bottom: 0px;
+}
+
diff --git a/docs/_static/img/favicon.ico b/docs/_static/img/favicon.ico
new file mode 100644
index 0000000..282b6d1
--- /dev/null
+++ b/docs/_static/img/favicon.ico
Binary files differ
diff --git a/docs/_static/img/logo.pdf b/docs/_static/img/logo.pdf
new file mode 100644
index 0000000..2db6eaf
--- /dev/null
+++ b/docs/_static/img/logo.pdf
Binary files differ
diff --git a/docs/_static/img/logo.png b/docs/_static/img/logo.png
new file mode 100644
index 0000000..06ee335
--- /dev/null
+++ b/docs/_static/img/logo.png
Binary files differ
diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html
new file mode 100644
index 0000000..a651407
--- /dev/null
+++ b/docs/_templates/layout.html
@@ -0,0 +1,20 @@
+{% extends '!layout.html' %}
+
+{% block extrahead %}
+<meta name="twitter:card" content="summary_large_image" >
+<meta name="twitter:creator" content="@bstellato" >
+<meta name="twitter:image"
+      content="https://osqp.org/docs/_static/img/logo.png">
+<meta name="twitter:description" content="The OSQP (Operator Splitting
+Quadratic Program) solver is a numerical optimization package for solving
+convex quadratic programs." >
+<meta property="og:url" content="https://osqp.org" >
+<meta property="og:title" content="OSQP solver documentation" >
+<meta property="og:description" content="The OSQP (Operator Splitting Quadratic
+Program) solver is a numerical optimization package for solving convex
+quadratic programs." >
+<meta property="og:image"
+content="https://osqp.org/docs/_static/img/logo.png">
+<meta property="fb:app_id" content="129555174389689">
+<meta property="og:type" content="website" >
+{% endblock %}
diff --git a/docs/citing/index.rst b/docs/citing/index.rst
new file mode 100644
index 0000000..d114e6e
--- /dev/null
+++ b/docs/citing/index.rst
@@ -0,0 +1,87 @@
+.. _citing :
+
+Citing OSQP
+===========
+
+If you use OSQP for published work, we encourage you to put a star on `GitHub <https://github.com/osqp/osqp>`_ and cite the accompanying papers:
+
+
+.. glossary::
+
+    Main paper
+        Main algorithm description, derivation and benchmark available in this `paper <https://web.stanford.edu/~boyd/papers/pdf/osqp.pdf>`__.
+
+        .. code:: latex
+
+          @article{osqp,
+            author  = {Stellato, B. and Banjac, G. and Goulart, P. and Bemporad, A. and Boyd, S.},
+            title   = {{OSQP}: an operator splitting solver for quadratic programs},
+            journal = {Mathematical Programming Computation},
+            volume  = {12},
+            number  = {4},
+            pages   = {637--672},
+            year    = {2020},
+            doi     = {10.1007/s12532-020-00179-2},
+            url     = {https://doi.org/10.1007/s12532-020-00179-2},
+          }
+
+    Infeasibility detection
+        Infeasibility detection proofs using ADMM (also for general conic programs) in this `paper <https://stanford.edu/~boyd/papers/pdf/admm_infeas.pdf>`__.
+
+        .. code:: latex
+
+          @article{osqp-infeasibility,
+            author  = {Banjac, G. and Goulart, P. and Stellato, B. and Boyd, S.},
+            title   = {Infeasibility detection in the alternating direction method of multipliers for convex optimization},
+            journal = {Journal of Optimization Theory and Applications},
+            year    = {2019},
+            volume  = {183},
+            number  = {2},
+            pages   = {490--519},
+            doi     = {10.1007/s10957-019-01575-y},
+            url     = {https://doi.org/10.1007/s10957-019-01575-y},
+          }
+
+    GPU implementation
+        GPU implementation and PCG method for solving linear systems in this `paper <https://doi.org/10.1016/j.jpdc.2020.05.021>`__.
+
+        .. code:: latex
+
+          @article{osqp-gpu,
+            author  = {Schubiger, M. and Banjac, G. and Lygeros, J.},
+            title   = {{GPU} acceleration of {ADMM} for large-scale quadratic programming},
+            journal = {Journal of Parallel and Distributed Computing},
+            year    = {2020},
+            volume  = {144},
+            pages   = {55--67},
+            doi     = {10.1016/j.jpdc.2020.05.021},
+            url     = {https://doi.org/10.1016/j.jpdc.2020.05.021},
+          }
+
+    Code generation
+        Code generation functionality and example in this `paper <https://stanford.edu/~boyd/papers/pdf/osqp_embedded.pdf>`__.
+
+        .. code:: latex
+
+          @inproceedings{osqp-codegen,
+            author    = {Banjac, G. and Stellato, B. and Moehle, N. and Goulart, P. and Bemporad, A. and Boyd, S.},
+            title     = {Embedded code generation using the {OSQP} solver},
+            booktitle = {IEEE Conference on Decision and Control (CDC)},
+            year      = {2017},
+            doi       = {10.1109/CDC.2017.8263928},
+            url       = {https://doi.org/10.1109/CDC.2017.8263928},
+          }
+
+    Mixed-integer optimization
+        A branch-and-bound solver for mixed-integer quadratic optimization in this `paper <https://stellato.io/downloads/publications/2018/miosqp_ecc.pdf>`__.
+
+        .. code:: latex
+
+          @inproceedings{miosqp,
+            author    = {Stellato, B. and Naik, V. V. and Bemporad, A. and Goulart, P. and Boyd, S.},
+            title     = {Embedded mixed-integer quadratic optimization using the {OSQP} solver},
+            booktitle = {European Control Conference (ECC)},
+            year      = {2018},
+            doi       = {10.23919/ECC.2018.8550136},
+            url       = {https://doi.org/10.23919/ECC.2018.8550136},
+          }
diff --git a/docs/codegen/index.rst b/docs/codegen/index.rst
new file mode 100644
index 0000000..d9ebdc1
--- /dev/null
+++ b/docs/codegen/index.rst
@@ -0,0 +1,46 @@
+Code generation
+===============
+
+OSQP can generate tailored C code that compiles into a fast and reliable solver
+for the given family of QP problems in which the problem data, but not its
+dimensions, change between problem instances.
+
+
+The generated code is:
+
+.. glossary::
+
+      Malloc-free
+          It does not perform any dynamic memory allocation.
+
+      Library-free
+          It is not linked to any external library.
+
+      Division-free
+          There are no division required in the ADMM algorithm
+
+
+We make a distinction between two cases depending on which of the data are to be
+treated as parameters.
+
+.. glossary::
+
+    Vectors as parameters
+        Vectors :math:`q`, :math:`l` and :math:`u` can change between problem instances.
+        This corresponds to the compiler flag :code:`EMBEDDED=1`.
+        :math:`\rho` adaptation is not enabled. 
+        The generated code is division-free and has a very small footprint.
+
+    Matrices as parameters
+        Both vectors :math:`q`, :math:`l`, :math:`u` and values in matrices
+        :math:`P` and :math:`A` can change between problem instances.
+        This corresponds to the compiler flag :code:`EMBEDDED=2`.
+        :math:`\rho` adaptation is enabled but the frequency does not rely on any timing. 
+        We assume that the sparsity patterns of :math:`P` and :math:`A` are fixed.
+
+
+.. toctree::
+   :maxdepth: 1
+   :glob:
+
+   *
diff --git a/docs/codegen/matlab.rst b/docs/codegen/matlab.rst
new file mode 100644
index 0000000..1b7d0f2
--- /dev/null
+++ b/docs/codegen/matlab.rst
@@ -0,0 +1,168 @@
+Matlab
+======
+
+Before generating code for a parametric problem, the problem should be first
+specified in the setup phase. See :ref:`matlab_setup` for more details.
+
+
+Codegen
+-------
+The code is generated by running
+
+.. code:: matlab
+
+    m.codegen(dir_name, varargin)
+
+The argument :code:`dir_name` is the name of a directory where the generated
+code is stored.
+The second argument :code:`varargin` specifies additional codegen options
+shown in the following table
+
+
++-----------------------+---------------------------------------------------+--------------------------------+
+| Option                | Description                                       | Allowed values                 |
++=======================+===================================================+================================+
+| :code:`project_type`  | Build environment                                 | | :code:`''` (default)         |
+|                       |                                                   | | :code:`'Makefile'`           |
+|                       |                                                   | | :code:`'MinGW Makefiles'`    |
+|                       |                                                   | | :code:`'Unix Makefiles'`     |
+|                       |                                                   | | :code:`'CodeBlocks'`         |
+|                       |                                                   | | :code:`'Xcode'`              |
++-----------------------+---------------------------------------------------+--------------------------------+
+| :code:`parameters`    | Problem parameters                                | | :code:`'vectors'` (default)  |
+|                       |                                                   | | :code:`'matrices'`           |
++-----------------------+---------------------------------------------------+--------------------------------+
+| :code:`mexname`       | Name of the compiled mex interface                | | :code:`'emosqp'` (default)   |
+|                       |                                                   | | Nonempty string              |
++-----------------------+---------------------------------------------------+--------------------------------+
+| :code:`force_rewrite` | Rewrite existing directory                        | | :code:`false` (default)      |
+|                       |                                                   | | :code:`true`                 |
++-----------------------+---------------------------------------------------+--------------------------------+
+| :code:`FLOAT`         | Use :code:`float` type instead of :code:`double`  | | :code:`false` (default)      |
+|                       |                                                   | | :code:`true`                 |
++-----------------------+---------------------------------------------------+--------------------------------+
+| :code:`LONG`          | Use :code:`long long` type instead of :code:`int` | | :code:`true` (default)       |
+|                       |                                                   | | :code:`false`                |
++-----------------------+---------------------------------------------------+--------------------------------+
+
+You can pass the options as field-value pairs, e.g.,
+
+.. code:: matlab
+
+    m.codegen('code', 'parameters', 'matrices', 'mexname', 'emosqp');
+
+If the :code:`project_type` argument is not passed or is set to :code:`''`,
+then no build files are generated.
+
+
+
+Mex interface
+-------------
+Once the code is generated the following functions are provided through its mex interface. Each function is called as
+
+.. code:: matlab
+
+    emosqp('function_name');
+
+
+where :code:`emosqp` is the name of the mex interface specified in the previous section
+
+.. function:: emosqp('solve')
+   :noindex:
+
+   Solve the problem.
+
+   :returns: multiple variables [x, y, status_val, iter, run_time]
+
+             - **x** (*ndarray*) - Primal solution
+             - **y** (*ndarray*) - Dual solution
+             - **status_val** (*int*) - Status value as in :ref:`status_values`
+             - **iter** (*int*) - Number of iterations
+             - **run_time** (*double*) - Run time
+
+
+.. function:: emosqp('update_lin_cost', q_new)
+   :noindex:
+
+   Update linear cost.
+
+   :param ndarray q_new: New linear cost vector
+
+
+.. function:: emosqp('update_lower_bound', l_new)
+   :noindex:
+
+   Update lower bound in the constraints.
+
+   :param ndarray l_new: New lower bound vector
+
+
+.. function:: emosqp('update_upper_bound', u_new)
+   :noindex:
+
+   Update upper bound in the constraints.
+
+   :param ndarray u_new: New upper bound vector
+
+
+.. function:: emosqp('update_bounds', l_new, u_new)
+   :noindex:
+
+   Update lower and upper bounds in the constraints.
+
+   :param ndarray l_new: New lower bound vector
+   :param ndarray u_new: New upper bound vector
+
+
+If the code is generated with the option :code:`parameters` set to
+:code:`'matrices'`, then the following functions are also provided
+
+
+.. function:: emosqp('update_P', Px, Px_idx, Px_n)
+   :noindex:
+
+  Update nonzero entries of the quadratic cost matrix (only upper triangular) without changing sparsity structure.
+
+   :param ndarray Px: Values of entries to be updated
+   :param ndarray Px_idx: Indices of entries to be updated. Pass :code:`[]` if
+                         all the indices are to be updated
+   :param int Px_n: Number of entries to be updated. Used only if Px_idx is not
+                   :code:`[]`.
+
+
+.. function:: emosqp('update_A', Ax, Ax_idx, Ax_n)
+   :noindex:
+
+   Update nonzero entries of the constraint matrix.
+
+   :param ndarray Ax: Values of entries to be updated
+   :param ndarray Ax_idx: Indices of entries to be updated. Pass :code:`[]` if
+                         all the indices are to be updated
+   :param int Ax_n: Number of entries to be updated. Used only if Ax_idx is not
+                   :code:`[]`.
+
+
+.. function:: emosqp('update_P_A', Px, Px_idx, Px_n, Ax, Ax_idx, Ax_n)
+   :noindex:
+
+  Update nonzero entries of the quadratic cost and constraint matrices. It considers only the upper-triangular part of P.
+
+   :param ndarray Px: Values of entries to be updated
+   :param ndarray Px_idx: Indices of entries to be updated. Pass :code:`[]` if
+                         all the indices are to be updated
+   :param int Px_n: Number of entries to be updated. Used only if Px_idx is not
+                   :code:`[]`.
+   :param ndarray Ax: Values of entries to be updated
+   :param ndarray Ax_idx: Indices of entries to be updated. Pass :code:`[]` if
+                         all the indices are to be updated
+   :param int Ax_n: Number of entries to be updated. Used only if Ax_idx is not
+                   :code:`[]`.
+
+
+You can update all the nonzero entries in matrix :math:`A` by running
+
+.. code:: matlab
+
+    emosqp('update_A', Ax_new, [], 0);
+
+See C :ref:`C_sublevel_API` for more details on the input arguments.
diff --git a/docs/codegen/python.rst b/docs/codegen/python.rst
new file mode 100644
index 0000000..512ddc3
--- /dev/null
+++ b/docs/codegen/python.rst
@@ -0,0 +1,168 @@
+Python
+======
+
+Before generating code for a parametric problem, the problem should be first
+specified in the setup phase. See :ref:`python_setup` for more details.
+
+
+Codegen
+-------
+The code is generated by running
+
+.. code:: python
+
+    m.codegen(dir_name, **opts)
+
+The argument :code:`dir_name` is the name of a directory where the generated
+code is stored.
+Additional codegen options are shown in the following table
+
++-------------------------+---------------------------------------------------+--------------------------------+
+| Option                  | Description                                       | Allowed values                 |
++=========================+===================================================+================================+
+| :code:`project_type`    | Build environment                                 | | :code:`''` (default)         |
+|                         |                                                   | | :code:`'Makefile'`           |
+|                         |                                                   | | :code:`'MinGW Makefiles'`    |
+|                         |                                                   | | :code:`'Unix Makefiles'`     |
+|                         |                                                   | | :code:`'CodeBlocks'`         |
+|                         |                                                   | | :code:`'Xcode'`              |
++-------------------------+---------------------------------------------------+--------------------------------+
+| :code:`parameters`      | Problem parameters                                | | :code:`'vectors'` (default)  |
+|                         |                                                   | | :code:`'matrices'`           |
++-------------------------+---------------------------------------------------+--------------------------------+
+| :code:`python_ext_name` | Name of the generated Python module               | | :code:`'emosqp'` (default)   |
+|                         |                                                   | | Nonempty string              |
++-------------------------+---------------------------------------------------+--------------------------------+
+| :code:`force_rewrite`   | Rewrite existing directory                        | | :code:`False` (default)      |
+|                         |                                                   | | :code:`True`                 |
++-------------------------+---------------------------------------------------+--------------------------------+
+| :code:`FLOAT`           | Use :code:`float` type instead of :code:`double`  | | :code:`False` (default)      |
+|                         |                                                   | | :code:`True`                 |
++-------------------------+---------------------------------------------------+--------------------------------+
+| :code:`LONG`            | Use :code:`long long` type instead of :code:`int` | | :code:`True` (default)       |
+|                         |                                                   | | :code:`False`                |
++-------------------------+---------------------------------------------------+--------------------------------+
+
+The options are passed using named arguments, e.g.,
+
+.. code:: python
+
+    m.codegen('code', parameters='matrices', python_ext_name='emosqp')
+
+If the :code:`project_type` argument is not passed or is set to :code:`''`,
+then no build files are generated.
+
+
+
+Extension module API
+--------------------
+Once the code is generated, you can import a light python wrapper with
+
+.. code:: python
+
+    import emosqp
+
+where :code:`emosqp` is the extension name given in the previous section. The module imports the following functions
+
+.. py:function:: solve()
+   :noindex:
+
+   Solve the problem.
+
+   :returns: tuple (x, y, status_val, iter, run_time)
+
+             - **x** (*ndarray*) - Primal solution
+             - **y** (*ndarray*) - Dual solution
+             - **status_val** (*int*) - Status value as in :ref:`status_values`
+             - **iter** (*int*) - Number of iterations
+             - **run_time** (*double*) - Run time
+
+
+
+
+
+.. py:function:: update_lin_cost(q_new)
+   :noindex:
+
+   Update linear cost.
+
+   :param ndarray q_new: New linear cost vector
+
+
+.. py:function:: update_lower_bound(l_new)
+   :noindex:
+
+   Update lower bound in the constraints.
+
+   :param ndarray l_new: New lower bound vector
+
+
+.. py:function:: update_upper_bound(u_new)
+   :noindex:
+
+   Update upper bound in the constraints.
+
+   :param ndarray u_new: New upper bound vector
+
+
+.. py:function:: update_bounds(l_new, u_new)
+   :noindex:
+
+   Update lower and upper bounds in the constraints.
+
+   :param ndarray l_new: New lower bound vector
+   :param ndarray u_new: New upper bound vector
+
+
+If the code is generated with the option :code:`parameters` set to
+:code:`'matrices'`, the following functions are also provided
+
+
+.. py:function:: update_P(Px, Px_idx, Px_n)
+  :noindex:
+
+  Update nonzero entries of the quadratic cost matrix (only upper triangular) without changing sparsity structure.
+
+  :param ndarray Px: Values of entries to be updated
+  :param ndarray Px_idx: Indices of entries to be updated. Pass :code:`None` if
+                         all the indices are to be updated
+  :param int Px_n: Number of entries to be updated. Used only if Px_idx is not
+                   :code:`None`.
+
+
+.. py:function:: update_A(Ax, Ax_idx, Ax_n)
+  :noindex:
+
+  Update nonzero entries of the constraint matrix.
+
+  :param ndarray Ax: Values of entries to be updated
+  :param ndarray Ax_idx: Indices of entries to be updated. Pass :code:`None` if
+                         all the indices are to be updated
+  :param int Ax_n: Number of entries to be updated. Used only if Ax_idx is not
+                   :code:`None`.
+
+
+.. py:function:: update_P_A(Px, Px_idx, Px_n, Ax, Ax_idx, Ax_n)
+  :noindex:
+
+  Update nonzero entries of the quadratic cost and constraint matrices. It considers only the upper-triangular part of P.
+
+  :param ndarray Px: Values of entries to be updated
+  :param ndarray Px_idx: Indices of entries to be updated. Pass :code:`None` if
+                         all the indices are to be updated
+  :param int Px_n: Number of entries to be updated. Used only if Px_idx is not
+                   :code:`None`.
+  :param ndarray Ax: Values of entries to be updated
+  :param ndarray Ax_idx: Indices of entries to be updated. Pass :code:`None` if
+                         all the indices are to be updated
+  :param int Ax_n: Number of entries to be updated. Used only if Ax_idx is not
+                   :code:`None`.
+
+
+You can update all the nonzero entries in matrix :math:`A` by running
+
+.. code:: python
+
+    emosqp.update_A(Ax_new, None, 0);
+
+See C :ref:`C_sublevel_API` for more details on the input arguments.
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..baac394
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# OSQP documentation build configuration file, created by
+# sphinx-quickstart on Sat Feb 18 15:49:00 2017.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+
+import sphinx_rtd_theme
+import os
+import subprocess
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+__version__ = os.environ.get('OSQP_VERSION', '0.0.0')
+
+# An incoming version number of '0.0.0' is a placeholder for missing version information.
+# In such cases, use a <blank> version to effectively avoid mentioning the version number
+# in the built documentation at all.
+__version__ = '' if __version__ == '0.0.0' else __version__
+
+# Set OSQP_VERSION envvar in case subprocesses (like doxygen) need it too
+os.environ['OSQP_VERSION'] = __version__
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinx.ext.todo', 'sphinx.ext.mathjax', 'breathe']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'OSQP'
+copyright = '2021, Bartolomeo Stellato, Goran Banjac'
+author = 'Bartolomeo Stellato, Goran Banjac'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+
+version = '.'.join(__version__.split('.')[:4])
+
+# The full version, including alpha/beta/rc tags.
+release = __version__
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+
+html_theme_options = {
+    'logo_only': True,
+}
+
+
+html_logo = '_static/img/logo.png'
+html_favicon = "_static/img/favicon.ico"
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org
+on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+
+if not on_rtd:  # only import and set the theme if we're building docs locally
+    # Override default css to get a larger width for local build
+    def setup(app):
+        app.add_css_file('css/osqp_theme.css')
+else:
+    html_context = {
+        'css_files': [
+                'https://media.readthedocs.org/css/sphinx_rtd_theme.css',
+                'https://media.readthedocs.org/css/readthedocs-doc-embed.css',
+                '_static/css/osqp_theme.css'],
+    }
+
+
+
+# -- Options for HTMLHelp output ------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'OSQPdoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    #
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    #
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    #
+    # 'preamble': '',
+
+    # Latex figure (float) alignment
+    #
+    # 'figure_align': 'htbp',
+    'sphinxsetup': 'hmargin={1.5cm,1.5cm}',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'OSQP.tex', 'OSQP Documentation',
+     'Bartolomeo Stellato, Goran Banjac', 'manual'),
+]
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'OSQP', 'OSQP Documentation',
+     [author], 1)
+]
+
+# -- Options for breathe ---------------------------------------
+
+# Generate doxygen documentation
+subprocess.call('doxygen doxygen.conf', shell=True)
+
+breathe_projects = {"osqp": "doxygen_out/xml/"}
+breathe_default_project = "osqp"
+
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'OSQP', 'OSQP Documentation',
+     author, 'OSQP', 'One line description of project.',
+     'Miscellaneous'),
+]
diff --git a/docs/contributing/index.rst b/docs/contributing/index.rst
new file mode 100644
index 0000000..a3a235d
--- /dev/null
+++ b/docs/contributing/index.rst
@@ -0,0 +1,93 @@
+Contributing
+=============
+
+OSQP is an open-source project open to any academic or commercial applications.
+Contributions are welcome as `GitHub pull requests <https://help.github.com/articles/creating-a-pull-request/>`_ in any part of the project such as
+
+* algorithm developments
+* interfaces to other languages
+* compatibility with new architectures
+* linear system solvers interfaces
+
+
+.. _interfacing_new_linear_system_solvers :
+
+Interfacing new linear system solvers
+-------------------------------------
+OSQP is designed to be easily interfaced to new linear system solvers via dynamic library loading.
+To add a linear system solver interface you need to edit the :code:`lin_sys/` directory subfolder :code:`direct/` or :code:`indirect/` depending on the type of solver.
+Create a subdirectory with your solver name with four files:
+
+* Dynamic library loading: :code:`mysolver_loader.c` and :code:`mysolver_loader.h`.
+* Linear system solution: :code:`mysolver.c` and :code:`mysolver.h`.
+
+We suggest you to have a look at the `MKL Pardiso solver interface <https://github.com/osqp/osqp/tree/master/lin_sys/direct/pardiso>`_ for more details.
+
+Dynamic library loading
+^^^^^^^^^^^^^^^^^^^^^^^
+In this part define the methods to load the shared library and obtain the functions required to solve the linear system.
+The main functions to be exported are :code:`lh_load_mysolver(const char* libname)` and :code:`lh_unload_mysolver()`.
+In addition, the file :code:`mysolver_loader.c` must define static function pointers to the shared library functions to be loaded.
+
+Linear system solution
+^^^^^^^^^^^^^^^^^^^^^^
+In this part we define the core of the interface: **linear system solver object**.
+The main functions are the external method
+
+* :code:`init_linsys_solver_mysolver`: create the instance and perform the setup
+
+and the internal methods of the object
+
+* :code:`free_linsys_solver_mysolver`: free the instance
+* :code:`solve_linsys_mysolver`: solve the linear system
+* :code:`update_matrices`: update problem matrices
+* :code:`update_rho_vec`: update :math:`\rho` as a diagonal vector.
+
+After the initializations these functions are assigned to the internal pointers so that, for an instance :code:`s` they can be called as :code:`s->free`, :code:`s->solve`, :code:`s->update_matrices` and :code:`s->update_rho_vec`.
+
+The linear system solver object is defined in :code:`mysolver.h` as follows
+
+.. code:: c
+
+        typedef struct mysolver mysolver_solver;
+
+        struct mysolver {
+            // Methods
+            enum linsys_solver_type type; // Linear system solver defined in constants.h
+
+            c_int (*solve)(struct mysolver * self, c_float * b);
+            void (*free)(struct mysolver * self);
+            c_int (*update_matrices)(struct mysolver * self, const csc *P, const csc *A);
+            c_int (*update_rho_vec)(struct mysolver * self, const c_float * rho_vec);
+
+            // Attributes
+            c_int nthreads; // Number of threads used (required!)
+
+            // Internal attributes of the solver
+            ...
+
+            // Internal attributes required for matrix updates
+            c_int * Pdiag_idx, Pdiag_n;  ///< index and number of diagonal elements in P
+            c_int * PtoKKT, * AtoKKT;    ///< Index of elements from P and A to KKT matrix
+            c_int * rhotoKKT;            ///< Index of rho places in KKT matrix
+            ...
+
+        };
+
+        // Initialize mysolver solver
+        c_int init_linsys_solver_mysolver(mysolver_solver ** s, const csc * P, const csc * A, c_float sigma, c_float * rho_vec, c_int polish);
+
+        // Solve linear system and store result in b
+        c_int solve_linsys_mysolver(mysolver_solver * s, c_float * b);
+
+         // Update linear system solver matrices
+        c_int update_linsys_solver_matrices_mysolver(mysolver_solver * s, const csc *P, const csc *A);
+
+        // Update rho_vec parameter in linear system solver structure
+        c_int update_linsys_solver_rho_vec_mysolver(mysolver_solver * s, const c_float * rho_vec);
+
+        // Free linear system solver
+        void free_linsys_solver_mysolver(mysolver_solver * s);
+
+
+The function details are coded in the :code:`mysolver.c` file.
diff --git a/docs/doxygen.conf b/docs/doxygen.conf
new file mode 100644
index 0000000..09626ad
--- /dev/null
+++ b/docs/doxygen.conf
@@ -0,0 +1,2473 @@
+# Doxyfile 1.8.13
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "OSQP"
+
+# 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         = $(OSQP_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          = "The Operator Splitting QP Solver"
+
+# 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       = ./doxygen_out
+
+# 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         = YES
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "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 QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When 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: 0.
+# 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:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# 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
+
+#---------------------------------------------------------------------------
+# 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_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 the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = 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 http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# 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                  = ./../include ./../src
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# 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.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+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              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# 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
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = NO 
+
+# 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
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to 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_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom 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      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# 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
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using 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            = YES
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. 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 pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# 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
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+# 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           = YES
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = NO
+
+# 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
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. 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 dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/docs/examples/huber.rst b/docs/examples/huber.rst
new file mode 100644
index 0000000..b21c50a
--- /dev/null
+++ b/docs/examples/huber.rst
@@ -0,0 +1,162 @@
+Huber fitting
+=============
+
+*Huber fitting* or the *robust least-squares problem* performs linear regression under the assumption that there are outliers in the data.
+The fitting problem is written as
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \sum_{i=1}^{m} \phi_{\rm hub}(a_i^T x - b_i),
+  \end{array}
+
+with the Huber penalty function :math:`\phi_{\rm hub}:\mathbf{R}\to\mathbf{R}` defined as
+
+.. math::
+  \phi_{\rm hub}(u) =
+  \begin{cases}
+      u^2         & |u| \le 1 \\
+      (2|u| - 1)  & |u| > 1
+  \end{cases}
+
+The problem has the following equivalent form (see `here <https://doi.org/10.1109/34.877518>`_ for more details)
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize}   & u^T u + 2\,\boldsymbol{1}^T (r+s) \\
+    \mbox{subject to} & Ax - b - u = r - s \\
+                      & r \ge 0 \\
+                      & s \ge 0
+  \end{array}
+
+
+
+Python
+------
+
+.. code:: python
+
+    import osqp
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Generate problem data
+    sp.random.seed(1)
+    n = 10
+    m = 100
+    Ad = sparse.random(m, n, density=0.5, format='csc')
+    x_true = np.random.randn(n) / np.sqrt(n)
+    ind95 = (np.random.rand(m) < 0.95).astype(float)
+    b = Ad.dot(x_true) + np.multiply(0.5*np.random.randn(m), ind95) \
+        + np.multiply(10.*np.random.rand(m), 1. - ind95)
+
+    # OSQP data
+    Im = sparse.eye(m)
+    P = sparse.block_diag([sparse.csc_matrix((n, n)), 2*Im,
+                           sparse.csc_matrix((2*m, 2*m))],
+                           format='csc')
+    q = np.append(np.zeros(m+n), 2*np.ones(2*m))
+    A = sparse.bmat([[Ad,   -Im,   -Im,   Im],
+                     [None,  None,  Im,   None],
+                     [None,  None,  None, Im]], format='csc')
+    l = np.hstack([b, np.zeros(2*m)])
+    u = np.hstack([b, np.inf*np.ones(2*m)])
+
+    # Create an OSQP object
+    prob = osqp.OSQP()
+
+    # Setup workspace
+    prob.setup(P, q, A, l, u)
+
+    # Solve problem
+    res = prob.solve()
+
+
+
+Matlab
+------
+
+.. code:: matlab
+
+    % Generate problem data
+    rng(1)
+    n = 10;
+    m = 100;
+    Ad = sprandn(m, n, 0.5);
+    x_true = randn(n, 1) / sqrt(n);
+    ind95 = rand(m, 1) > 0.95;
+    b = Ad*x_true + 10*rand(m, 1).*ind95 + 0.5*randn(m, 1).*(1-ind95);
+
+    % OSQP data
+    Im = speye(m);
+    Om = sparse(m, m);
+    Omn = sparse(m, n);
+    P = blkdiag(sparse(n, n), 2*Im, sparse(2*m, 2*m));
+    q = [zeros(m + n, 1); 2*ones(2*m, 1)];
+    A = [Ad,  -Im, -Im, Im;
+         Omn,  Om,  Im, Om;
+         Omn,  Om,  Om, Im];
+    l = [b; zeros(2*m, 1)];
+    u = [b; inf*ones(2*m, 1)];
+
+    % Create an OSQP object
+    prob = osqp;
+
+    % Setup workspace
+    prob.setup(P, q, A, l, u);
+
+    % Solve problem
+    res = prob.solve();
+
+
+
+CVXPY
+-----
+
+.. code:: python
+
+    from cvxpy import *
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Generate problem data
+    sp.random.seed(1)
+    n = 10
+    m = 100
+    A = sparse.random(m, n, density=0.5, format='csc')
+    x_true = np.random.randn(n) / np.sqrt(n)
+    ind95 = (np.random.rand(m) < 0.95).astype(float)
+    b = A.dot(x_true) + np.multiply(0.5*np.random.randn(m), ind95) \
+        + np.multiply(10.*np.random.rand(m), 1. - ind95)
+
+    # Define problem
+    x = Variable(n)
+    objective = sum(huber(A*x - b))
+
+    # Solve with OSQP
+    Problem(Minimize(objective)).solve(solver=OSQP)
+
+
+
+YALMIP
+------
+
+.. code:: matlab
+
+    % Generate problem data
+    rng(1)
+    n = 10;
+    m = 100;
+    A = sprandn(m, n, 0.5);
+    x_true = randn(n, 1) / sqrt(n);
+    ind95 = rand(m, 1) > 0.95;
+    b = A*x_true + 10*rand(m, 1).*ind95 + 0.5*randn(m, 1).*(1-ind95);
+
+    % Define problem
+    x = sdpvar(n, 1);
+    objective = huber(A*x - b);
+
+    % Solve with OSQP
+    options = sdpsettings('solver', 'osqp');
+    optimize([], objective, options);
diff --git a/docs/examples/index.rst b/docs/examples/index.rst
new file mode 100644
index 0000000..86cd66e
--- /dev/null
+++ b/docs/examples/index.rst
@@ -0,0 +1,27 @@
+Examples
+========
+
+
+Demo
+----
+
+.. toctree::
+   :maxdepth: 1
+
+   setup-and-solve.rst
+   update-vectors.rst
+   update-matrices.rst
+
+
+Applications
+------------
+
+.. toctree::
+   :maxdepth: 1
+
+   huber.rst
+   lasso.rst
+   least-squares.rst
+   mpc.rst
+   portfolio.rst
+   svm.rst
diff --git a/docs/examples/lasso.rst b/docs/examples/lasso.rst
new file mode 100644
index 0000000..cbb566b
--- /dev/null
+++ b/docs/examples/lasso.rst
@@ -0,0 +1,181 @@
+Lasso
+=====
+
+
+Lasso is a well known technique for sparse linear regression.
+It is obtained by adding an :math:`\ell_1` regularization term in the objective,
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} \| Ax - b \|_2^2 + \gamma \| x \|_1
+  \end{array}
+
+
+where :math:`x \in \mathbf{R}^{n}` is the vector of parameters, :math:`A \in \mathbf{R}^{m \times n}` is the data matrix, and :math:`\gamma > 0` is the weighting parameter.
+The problem has the following equivalent form,
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize}   & \frac{1}{2} y^T y + \gamma \boldsymbol{1}^T t \\
+    \mbox{subject to} & y = Ax - b \\
+                      & -t \le x \le t
+  \end{array}
+
+
+In order to get a good trade-off between sparsity of the solution and quality of the linear fit, we solve the problem for varying weighting parameter :math:`\gamma`.
+Since :math:`\gamma` enters only in the linear part of the objective function, we can reuse the matrix factorization and enable warm starting to reduce the computation time.
+
+
+
+Python
+------
+
+.. code:: python
+
+    import osqp
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Generate problem data
+    sp.random.seed(1)
+    n = 10
+    m = 1000
+    Ad = sparse.random(m, n, density=0.5)
+    x_true = np.multiply((np.random.rand(n) > 0.8).astype(float),
+                         np.random.randn(n)) / np.sqrt(n)
+    b = Ad.dot(x_true) + 0.5*np.random.randn(m)
+    gammas = np.linspace(1, 10, 11)
+
+    # Auxiliary data
+    In = sparse.eye(n)
+    Im = sparse.eye(m)
+    On = sparse.csc_matrix((n, n))
+    Onm = sparse.csc_matrix((n, m))
+
+    # OSQP data
+    P = sparse.block_diag([On, sparse.eye(m), On], format='csc')
+    q = np.zeros(2*n + m)
+    A = sparse.vstack([sparse.hstack([Ad, -Im, Onm.T]),
+                       sparse.hstack([In, Onm, -In]),
+                       sparse.hstack([In, Onm, In])], format='csc')
+    l = np.hstack([b, -np.inf * np.ones(n), np.zeros(n)])
+    u = np.hstack([b, np.zeros(n), np.inf * np.ones(n)])
+
+    # Create an OSQP object
+    prob = osqp.OSQP()
+
+    # Setup workspace
+    prob.setup(P, q, A, l, u, warm_start=True)
+
+    # Solve problem for different values of gamma parameter
+    for gamma in gammas:
+        # Update linear cost
+        q_new = np.hstack([np.zeros(n+m), gamma*np.ones(n)])
+        prob.update(q=q_new)
+
+        # Solve
+        res = prob.solve()
+
+
+Matlab
+------
+
+.. code:: matlab
+
+    % Generate problem data
+    rng(1)
+    n = 10;
+    m = 1000;
+    Ad = sprandn(m, n, 0.5);
+    x_true = (randn(n, 1) > 0.8) .* randn(n, 1) / sqrt(n);
+    b = Ad * x_true + 0.5 * randn(m, 1);
+    gammas = linspace(1, 10, 11);
+
+    % OSQP data
+    P = blkdiag(sparse(n, n), speye(m), sparse(n, n));
+    q = zeros(2*n+m, 1);
+    A = [Ad, -speye(m), sparse(m,n);
+        speye(n), sparse(n, m), -speye(n);
+        speye(n), sparse(n, m), speye(n);];
+    l = [b; -inf*ones(n, 1); zeros(n, 1)];
+    u = [b; zeros(n, 1); inf*ones(n, 1)];
+
+    % Create an OSQP object
+    prob = osqp;
+
+    % Setup workspace
+    prob.setup(P, q, A, l, u, 'warm_start', true);
+
+    % Solve problem for different values of gamma parameter
+    for i = 1 : length(gammas)
+        % Update linear cost
+        gamma = gammas(i);
+        q_new = [zeros(n+m,1); gamma*ones(n,1)];
+        prob.update('q', q_new);
+
+        % Solve
+        res = prob.solve();
+    end
+
+
+
+CVXPY
+-----
+
+.. code:: python
+
+    from cvxpy import *
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Generate problem data
+    sp.random.seed(1)
+    n = 10
+    m = 1000
+    A = sparse.random(m, n, density=0.5)
+    x_true = np.multiply((np.random.rand(n) > 0.8).astype(float),
+                         np.random.randn(n)) / np.sqrt(n)
+    b = A.dot(x_true) + 0.5*np.random.randn(m)
+    gammas = np.linspace(1, 10, 11)
+
+    # Define problem
+    x = Variable(n)
+    gamma = Parameter(nonneg=True)
+    objective = 0.5*sum_squares(A*x - b) + gamma*norm1(x)
+    prob = Problem(Minimize(objective))
+
+    # Solve problem for different values of gamma parameter
+    for gamma_val in gammas:
+        gamma.value = gamma_val
+        prob.solve(solver=OSQP, warm_start=True)
+
+
+YALMIP
+------
+
+.. code:: matlab
+
+    % Generate problem data
+    rng(1)
+    n = 10;
+    m = 1000;
+    A = sprandn(m, n, 0.5);
+    x_true = (randn(n, 1) > 0.8) .* randn(n, 1) / sqrt(n);
+    b = A * x_true + 0.5 * randn(m, 1);
+    gammas = linspace(1, 10, 11);
+
+    % Define problem
+    x = sdpvar(n, 1);
+    gamma = sdpvar;
+    objective = 0.5*norm(A*x - b)^2 + gamma*norm(x,1);
+
+    % Solve with OSQP
+    options = sdpsettings('solver', 'osqp');
+    x_opt = optimizer([], objective, options, gamma, x);
+
+    % Solve problem for different values of gamma parameter
+    for i = 1 : length(gammas)
+        x_opt(gammas(i));
+    end
diff --git a/docs/examples/least-squares.rst b/docs/examples/least-squares.rst
new file mode 100644
index 0000000..56adb7e
--- /dev/null
+++ b/docs/examples/least-squares.rst
@@ -0,0 +1,138 @@
+Least-squares
+=============
+
+Consider the following constrained least-squares problem
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} \|Ax - b\|_2^2 \\
+    \mbox{subject to} & 0 \leq x \leq 1
+  \end{array}
+
+The problem has the following equivalent form
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} y^T y \\
+    \mbox{subject to} & y = A x - b \\
+                      & 0 \le x \le 1
+  \end{array}
+
+
+
+Python
+------
+
+.. code:: python
+
+    import osqp
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Generate problem data
+    sp.random.seed(1)
+    m = 30
+    n = 20
+    Ad = sparse.random(m, n, density=0.7, format='csc')
+    b = np.random.randn(m)
+
+    # OSQP data
+    P = sparse.block_diag([sparse.csc_matrix((n, n)), sparse.eye(m)], format='csc')
+    q = np.zeros(n+m)
+    A = sparse.vstack([
+            sparse.hstack([Ad, -sparse.eye(m)]),
+            sparse.hstack([sparse.eye(n), sparse.csc_matrix((n, m))])], format='csc')
+    l = np.hstack([b, np.zeros(n)])
+    u = np.hstack([b, np.ones(n)])
+
+    # Create an OSQP object
+    prob = osqp.OSQP()
+
+    # Setup workspace
+    prob.setup(P, q, A, l, u)
+
+    # Solve problem
+    res = prob.solve()
+
+
+
+Matlab
+------
+
+.. code:: matlab
+
+   % Generate problem data
+   rng(1)
+   m = 30;
+   n = 20;
+   Ad = sprandn(m, n, 0.7);
+   b = randn(m, 1);
+
+   % OSQP data
+   P = blkdiag(sparse(n, n), speye(m));
+   q = zeros(n+m, 1);
+   A = [Ad, -speye(m);
+        speye(n), sparse(n, m)];
+   l = [b; zeros(n, 1)];
+   u = [b; ones(n, 1)];
+
+   % Create an OSQP object
+   prob = osqp;
+
+   % Setup workspace
+   prob.setup(P, q, A, l, u);
+
+   % Solve problem
+   res = prob.solve();
+
+
+
+CVXPY
+-----
+
+.. code:: python
+
+    from cvxpy import *
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Generate problem data
+    sp.random.seed(1)
+    m = 30
+    n = 20
+    A = sparse.random(m, n, density=0.7, format='csc')
+    b = np.random.randn(m)
+
+    # Define problem
+    x = Variable(n)
+    objective = 0.5*sum_squares(A*x - b)
+    constraints = [x >= 0, x <= 1]
+
+    # Solve with OSQP
+    Problem(Minimize(objective), constraints).solve(solver=OSQP)
+
+
+
+YALMIP
+------
+
+.. code:: matlab
+
+   % Generate data
+   rng(1)
+   m = 30;
+   n = 20;
+   A = sprandn(m, n, 0.7);
+   b = randn(m, 1);
+
+   % Define problem
+   x = sdpvar(n, 1);
+   objective = 0.5*norm(A*x - b)^2;
+   constraints = [ 0 <= x <= 1];
+
+   % Solve with OSQP
+   options = sdpsettings('solver','osqp');
+   optimize(constraints, objective, options);
+
diff --git a/docs/examples/mpc.rst b/docs/examples/mpc.rst
new file mode 100644
index 0000000..f5da713
--- /dev/null
+++ b/docs/examples/mpc.rst
@@ -0,0 +1,489 @@
+Model predictive control (MPC)
+==============================
+
+We consider the problem of controlling a linear time-invariant dynamical system to some reference state :math:`x_r \in \mathbf{R}^{n_x}`.
+To achieve this we use *constrained linear-quadratic MPC*, which solves at each time step the following finite-horizon optimal control problem
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize}   & (x_N-x_r)^T Q_N (x_N-x_r) + \sum_{k=0}^{N-1} (x_k-x_r)^T Q (x_k-x_r) + u_k^T R u_k \\
+    \mbox{subject to} & x_{k+1} = A x_k + B u_k \\
+                      & x_{\rm min} \le x_k  \le x_{\rm max} \\
+                      & u_{\rm min} \le u_k  \le u_{\rm max} \\
+                      & x_0 = \bar{x}
+  \end{array}
+
+The states :math:`x_k \in \mathbf{R}^{n_x}` and the inputs :math:`u_k \in \mathbf{R}^{n_u}` are constrained to be between some lower and upper bounds.
+The problem is solved repeatedly for varying initial state :math:`\bar{x} \in \mathbf{R}^{n_x}`.
+
+
+Python
+------
+
+.. code:: python
+
+    import osqp
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Discrete time model of a quadcopter
+    Ad = sparse.csc_matrix([
+      [1.,      0.,     0., 0., 0., 0., 0.1,     0.,     0.,  0.,     0.,     0.    ],
+      [0.,      1.,     0., 0., 0., 0., 0.,      0.1,    0.,  0.,     0.,     0.    ],
+      [0.,      0.,     1., 0., 0., 0., 0.,      0.,     0.1, 0.,     0.,     0.    ],
+      [0.0488,  0.,     0., 1., 0., 0., 0.0016,  0.,     0.,  0.0992, 0.,     0.    ],
+      [0.,     -0.0488, 0., 0., 1., 0., 0.,     -0.0016, 0.,  0.,     0.0992, 0.    ],
+      [0.,      0.,     0., 0., 0., 1., 0.,      0.,     0.,  0.,     0.,     0.0992],
+      [0.,      0.,     0., 0., 0., 0., 1.,      0.,     0.,  0.,     0.,     0.    ],
+      [0.,      0.,     0., 0., 0., 0., 0.,      1.,     0.,  0.,     0.,     0.    ],
+      [0.,      0.,     0., 0., 0., 0., 0.,      0.,     1.,  0.,     0.,     0.    ],
+      [0.9734,  0.,     0., 0., 0., 0., 0.0488,  0.,     0.,  0.9846, 0.,     0.    ],
+      [0.,     -0.9734, 0., 0., 0., 0., 0.,     -0.0488, 0.,  0.,     0.9846, 0.    ],
+      [0.,      0.,     0., 0., 0., 0., 0.,      0.,     0.,  0.,     0.,     0.9846]
+    ])
+    Bd = sparse.csc_matrix([
+      [0.,      -0.0726,  0.,     0.0726],
+      [-0.0726,  0.,      0.0726, 0.    ],
+      [-0.0152,  0.0152, -0.0152, 0.0152],
+      [-0.,     -0.0006, -0.,     0.0006],
+      [0.0006,   0.,     -0.0006, 0.0000],
+      [0.0106,   0.0106,  0.0106, 0.0106],
+      [0,       -1.4512,  0.,     1.4512],
+      [-1.4512,  0.,      1.4512, 0.    ],
+      [-0.3049,  0.3049, -0.3049, 0.3049],
+      [-0.,     -0.0236,  0.,     0.0236],
+      [0.0236,   0.,     -0.0236, 0.    ],
+      [0.2107,   0.2107,  0.2107, 0.2107]])
+    [nx, nu] = Bd.shape
+
+    # Constraints
+    u0 = 10.5916
+    umin = np.array([9.6, 9.6, 9.6, 9.6]) - u0
+    umax = np.array([13., 13., 13., 13.]) - u0
+    xmin = np.array([-np.pi/6,-np.pi/6,-np.inf,-np.inf,-np.inf,-1.,
+                     -np.inf,-np.inf,-np.inf,-np.inf,-np.inf,-np.inf])
+    xmax = np.array([ np.pi/6, np.pi/6, np.inf, np.inf, np.inf, np.inf,
+                      np.inf, np.inf, np.inf, np.inf, np.inf, np.inf])
+
+    # Objective function
+    Q = sparse.diags([0., 0., 10., 10., 10., 10., 0., 0., 0., 5., 5., 5.])
+    QN = Q
+    R = 0.1*sparse.eye(4)
+
+    # Initial and reference states
+    x0 = np.zeros(12)
+    xr = np.array([0.,0.,1.,0.,0.,0.,0.,0.,0.,0.,0.,0.])
+
+    # Prediction horizon
+    N = 10
+
+    # Cast MPC problem to a QP: x = (x(0),x(1),...,x(N),u(0),...,u(N-1))
+    # - quadratic objective
+    P = sparse.block_diag([sparse.kron(sparse.eye(N), Q), QN,
+                           sparse.kron(sparse.eye(N), R)], format='csc')
+    # - linear objective
+    q = np.hstack([np.kron(np.ones(N), -Q.dot(xr)), -QN.dot(xr),
+                   np.zeros(N*nu)])
+    # - linear dynamics
+    Ax = sparse.kron(sparse.eye(N+1),-sparse.eye(nx)) + sparse.kron(sparse.eye(N+1, k=-1), Ad)
+    Bu = sparse.kron(sparse.vstack([sparse.csc_matrix((1, N)), sparse.eye(N)]), Bd)
+    Aeq = sparse.hstack([Ax, Bu])
+    leq = np.hstack([-x0, np.zeros(N*nx)])
+    ueq = leq
+    # - input and state constraints
+    Aineq = sparse.eye((N+1)*nx + N*nu)
+    lineq = np.hstack([np.kron(np.ones(N+1), xmin), np.kron(np.ones(N), umin)])
+    uineq = np.hstack([np.kron(np.ones(N+1), xmax), np.kron(np.ones(N), umax)])
+    # - OSQP constraints
+    A = sparse.vstack([Aeq, Aineq], format='csc')
+    l = np.hstack([leq, lineq])
+    u = np.hstack([ueq, uineq])
+
+    # Create an OSQP object
+    prob = osqp.OSQP()
+
+    # Setup workspace
+    prob.setup(P, q, A, l, u, warm_start=True)
+
+    # Simulate in closed loop
+    nsim = 15
+    for i in range(nsim):
+        # Solve
+        res = prob.solve()
+
+        # Check solver status
+        if res.info.status != 'solved':
+            raise ValueError('OSQP did not solve the problem!')
+
+        # Apply first control input to the plant
+        ctrl = res.x[-N*nu:-(N-1)*nu]
+        x0 = Ad.dot(x0) + Bd.dot(ctrl)
+
+        # Update initial state
+        l[:nx] = -x0
+        u[:nx] = -x0
+        prob.update(l=l, u=u)
+
+
+
+Matlab
+------
+
+.. code:: matlab
+
+    % Discrete time model of a quadcopter
+    Ad = [1       0       0   0   0   0   0.1     0       0    0       0       0;
+          0       1       0   0   0   0   0       0.1     0    0       0       0;
+          0       0       1   0   0   0   0       0       0.1  0       0       0;
+          0.0488  0       0   1   0   0   0.0016  0       0    0.0992  0       0;
+          0      -0.0488  0   0   1   0   0      -0.0016  0    0       0.0992  0;
+          0       0       0   0   0   1   0       0       0    0       0       0.0992;
+          0       0       0   0   0   0   1       0       0    0       0       0;
+          0       0       0   0   0   0   0       1       0    0       0       0;
+          0       0       0   0   0   0   0       0       1    0       0       0;
+          0.9734  0       0   0   0   0   0.0488  0       0    0.9846  0       0;
+          0      -0.9734  0   0   0   0   0      -0.0488  0    0       0.9846  0;
+          0       0       0   0   0   0   0       0       0    0       0       0.9846];
+    Bd = [0      -0.0726  0       0.0726;
+         -0.0726  0       0.0726  0;
+         -0.0152  0.0152 -0.0152  0.0152;
+          0      -0.0006 -0.0000  0.0006;
+          0.0006  0      -0.0006  0;
+          0.0106  0.0106  0.0106  0.0106;
+          0      -1.4512  0       1.4512;
+         -1.4512  0       1.4512  0;
+         -0.3049  0.3049 -0.3049  0.3049;
+          0      -0.0236  0       0.0236;
+          0.0236  0      -0.0236  0;
+          0.2107  0.2107  0.2107  0.2107];
+    [nx, nu] = size(Bd);
+
+    % Constraints
+    u0 = 10.5916;
+    umin = [9.6; 9.6; 9.6; 9.6] - u0;
+    umax = [13; 13; 13; 13] - u0;
+    xmin = [-pi/6; -pi/6; -Inf; -Inf; -Inf; -1; -Inf(6,1)];
+    xmax = [ pi/6;  pi/6;  Inf;  Inf;  Inf; Inf; Inf(6,1)];
+
+    % Objective function
+    Q = diag([0 0 10 10 10 10 0 0 0 5 5 5]);
+    QN = Q;
+    R = 0.1*eye(4);
+
+    % Initial and reference states
+    x0 = zeros(12,1);
+    xr = [0; 0; 1; 0; 0; 0; 0; 0; 0; 0; 0; 0];
+
+    % Prediction horizon
+    N = 10;
+
+    % Cast MPC problem to a QP: x = (x(0),x(1),...,x(N),u(0),...,u(N-1))
+    % - quadratic objective
+    P = blkdiag( kron(speye(N), Q), QN, kron(speye(N), R) );
+    % - linear objective
+    q = [repmat(-Q*xr, N, 1); -QN*xr; zeros(N*nu, 1)];
+    % - linear dynamics
+    Ax = kron(speye(N+1), -speye(nx)) + kron(sparse(diag(ones(N, 1), -1)), Ad);
+    Bu = kron([sparse(1, N); speye(N)], Bd);
+    Aeq = [Ax, Bu];
+    leq = [-x0; zeros(N*nx, 1)];
+    ueq = leq;
+    % - input and state constraints
+    Aineq = speye((N+1)*nx + N*nu);
+    lineq = [repmat(xmin, N+1, 1); repmat(umin, N, 1)];
+    uineq = [repmat(xmax, N+1, 1); repmat(umax, N, 1)];
+    % - OSQP constraints
+    A = [Aeq; Aineq];
+    l = [leq; lineq];
+    u = [ueq; uineq];
+
+    % Create an OSQP object
+    prob = osqp;
+
+    % Setup workspace
+    prob.setup(P, q, A, l, u, 'warm_start', true);
+
+    % Simulate in closed loop
+    nsim = 15;
+    for i = 1 : nsim
+        % Solve
+        res = prob.solve();
+
+        % Check solver status
+        if ~strcmp(res.info.status, 'solved')
+            error('OSQP did not solve the problem!')
+        end
+
+        % Apply first control input to the plant
+        ctrl = res.x((N+1)*nx+1:(N+1)*nx+nu);
+        x0 = Ad*x0 + Bd*ctrl;
+
+        % Update initial state
+        l(1:nx) = -x0;
+        u(1:nx) = -x0;
+        prob.update('l', l, 'u', u);
+    end
+
+
+
+CVXPY
+-----
+
+.. code:: python
+
+    from cvxpy import *
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Discrete time model of a quadcopter
+    Ad = sparse.csc_matrix([
+      [1.,      0.,     0., 0., 0., 0., 0.1,     0.,     0.,  0.,     0.,     0.    ],
+      [0.,      1.,     0., 0., 0., 0., 0.,      0.1,    0.,  0.,     0.,     0.    ],
+      [0.,      0.,     1., 0., 0., 0., 0.,      0.,     0.1, 0.,     0.,     0.    ],
+      [0.0488,  0.,     0., 1., 0., 0., 0.0016,  0.,     0.,  0.0992, 0.,     0.    ],
+      [0.,     -0.0488, 0., 0., 1., 0., 0.,     -0.0016, 0.,  0.,     0.0992, 0.    ],
+      [0.,      0.,     0., 0., 0., 1., 0.,      0.,     0.,  0.,     0.,     0.0992],
+      [0.,      0.,     0., 0., 0., 0., 1.,      0.,     0.,  0.,     0.,     0.    ],
+      [0.,      0.,     0., 0., 0., 0., 0.,      1.,     0.,  0.,     0.,     0.    ],
+      [0.,      0.,     0., 0., 0., 0., 0.,      0.,     1.,  0.,     0.,     0.    ],
+      [0.9734,  0.,     0., 0., 0., 0., 0.0488,  0.,     0.,  0.9846, 0.,     0.    ],
+      [0.,     -0.9734, 0., 0., 0., 0., 0.,     -0.0488, 0.,  0.,     0.9846, 0.    ],
+      [0.,      0.,     0., 0., 0., 0., 0.,      0.,     0.,  0.,     0.,     0.9846]
+    ])
+    Bd = sparse.csc_matrix([
+      [0.,      -0.0726,  0.,     0.0726],
+      [-0.0726,  0.,      0.0726, 0.    ],
+      [-0.0152,  0.0152, -0.0152, 0.0152],
+      [-0.,     -0.0006, -0.,     0.0006],
+      [0.0006,   0.,     -0.0006, 0.0000],
+      [0.0106,   0.0106,  0.0106, 0.0106],
+      [0,       -1.4512,  0.,     1.4512],
+      [-1.4512,  0.,      1.4512, 0.    ],
+      [-0.3049,  0.3049, -0.3049, 0.3049],
+      [-0.,     -0.0236,  0.,     0.0236],
+      [0.0236,   0.,     -0.0236, 0.    ],
+      [0.2107,   0.2107,  0.2107, 0.2107]])
+    [nx, nu] = Bd.shape
+
+    # Constraints
+    u0 = 10.5916
+    umin = np.array([9.6, 9.6, 9.6, 9.6]) - u0
+    umax = np.array([13., 13., 13., 13.]) - u0
+    xmin = np.array([-np.pi/6,-np.pi/6,-np.inf,-np.inf,-np.inf,-1.,
+                     -np.inf,-np.inf,-np.inf,-np.inf,-np.inf,-np.inf])
+    xmax = np.array([ np.pi/6, np.pi/6, np.inf, np.inf, np.inf, np.inf,
+                      np.inf, np.inf, np.inf, np.inf, np.inf, np.inf])
+
+    # Objective function
+    Q = sparse.diags([0., 0., 10., 10., 10., 10., 0., 0., 0., 5., 5., 5.])
+    QN = Q
+    R = 0.1*sparse.eye(4)
+
+    # Initial and reference states
+    x0 = np.zeros(12)
+    xr = np.array([0.,0.,1.,0.,0.,0.,0.,0.,0.,0.,0.,0.])
+
+    # Prediction horizon
+    N = 10
+
+    # Define problem
+    u = Variable((nu, N))
+    x = Variable((nx, N+1))
+    x_init = Parameter(nx)
+    objective = 0
+    constraints = [x[:,0] == x_init]
+    for k in range(N):
+        objective += quad_form(x[:,k] - xr, Q) + quad_form(u[:,k], R)
+        constraints += [x[:,k+1] == Ad*x[:,k] + Bd*u[:,k]]
+        constraints += [xmin <= x[:,k], x[:,k] <= xmax]
+        constraints += [umin <= u[:,k], u[:,k] <= umax]
+    objective += quad_form(x[:,N] - xr, QN)
+    prob = Problem(Minimize(objective), constraints)
+
+    # Simulate in closed loop
+    nsim = 15
+    for i in range(nsim):
+        x_init.value = x0
+        prob.solve(solver=OSQP, warm_start=True)
+        x0 = Ad.dot(x0) + Bd.dot(u[:,0].value)
+
+
+
+YALMIP
+------
+
+.. code:: matlab
+
+    % Discrete time model of a quadcopter
+    Ad = [1       0       0   0   0   0   0.1     0       0    0       0       0;
+          0       1       0   0   0   0   0       0.1     0    0       0       0;
+          0       0       1   0   0   0   0       0       0.1  0       0       0;
+          0.0488  0       0   1   0   0   0.0016  0       0    0.0992  0       0;
+          0      -0.0488  0   0   1   0   0      -0.0016  0    0       0.0992  0;
+          0       0       0   0   0   1   0       0       0    0       0       0.0992;
+          0       0       0   0   0   0   1       0       0    0       0       0;
+          0       0       0   0   0   0   0       1       0    0       0       0;
+          0       0       0   0   0   0   0       0       1    0       0       0;
+          0.9734  0       0   0   0   0   0.0488  0       0    0.9846  0       0;
+          0      -0.9734  0   0   0   0   0      -0.0488  0    0       0.9846  0;
+          0       0       0   0   0   0   0       0       0    0       0       0.9846];
+    Bd = [0      -0.0726  0       0.0726;
+         -0.0726  0       0.0726  0;
+         -0.0152  0.0152 -0.0152  0.0152;
+          0      -0.0006 -0.0000  0.0006;
+          0.0006  0      -0.0006  0;
+          0.0106  0.0106  0.0106  0.0106;
+          0      -1.4512  0       1.4512;
+         -1.4512  0       1.4512  0;
+         -0.3049  0.3049 -0.3049  0.3049;
+          0      -0.0236  0       0.0236;
+          0.0236  0      -0.0236  0;
+          0.2107  0.2107  0.2107  0.2107];
+    [nx, nu] = size(Bd);
+
+    % Constraints
+    u0 = 10.5916;
+    umin = [9.6; 9.6; 9.6; 9.6] - u0;
+    umax = [13; 13; 13; 13] - u0;
+    xmin = [-pi/6; -pi/6; -Inf; -Inf; -Inf; -1; -Inf(6,1)];
+    xmax = [ pi/6;  pi/6;  Inf;  Inf;  Inf; Inf; Inf(6,1)];
+
+    % Objective function
+    Q = diag([0 0 10 10 10 10 0 0 0 5 5 5]);
+    QN = Q;
+    R = 0.1*eye(4);
+
+    % Initial and reference states
+    x0 = zeros(12,1);
+    xr = [0; 0; 1; 0; 0; 0; 0; 0; 0; 0; 0; 0];
+
+    % Prediction horizon
+    N = 10;
+
+    % Define problem
+    u = sdpvar(repmat(nu,1,N), repmat(1,1,N));
+    x = sdpvar(repmat(nx,1,N+1), repmat(1,1,N+1));
+    constraints = [xmin <= x{1} <= xmax];
+    objective = 0;
+    for k = 1 : N
+        objective = objective + (x{k}-xr)'*Q*(x{k}-xr) + u{k}'*R*u{k};
+        constraints = [constraints, x{k+1} == Ad*x{k} + Bd*u{k}];
+        constraints = [constraints, umin <= u{k}<= umax, xmin <= x{k+1} <= xmax];
+    end
+    objective = objective + (x{N+1}-xr)'*QN*(x{N+1}-xr);
+    options = sdpsettings('solver', 'osqp');
+    controller = optimizer(constraints, objective, options, x{1}, [u{:}]);
+
+    % Simulate in closed loop
+    nsim = 15;
+    for i = 1 : nsim
+        U = controller{x0};
+        x0 = Ad*x0 + Bd*U(:,1);
+    end
+
+
+
+Julia
+------
+
+.. code:: julia
+
+    # Add packages - uncomment for first-time setup
+    # using Pkg; Pkg.add(["SparseArrays", "OSQP"])
+
+    using SparseArrays, OSQP
+
+    # Utility function
+    speye(N) = spdiagm(ones(N))
+
+    # Discrete time model of a quadcopter
+    Ad = [1       0       0   0   0   0   0.1     0       0    0       0       0;
+          0       1       0   0   0   0   0       0.1     0    0       0       0;
+          0       0       1   0   0   0   0       0       0.1  0       0       0;
+          0.0488  0       0   1   0   0   0.0016  0       0    0.0992  0       0;
+          0      -0.0488  0   0   1   0   0      -0.0016  0    0       0.0992  0;
+          0       0       0   0   0   1   0       0       0    0       0       0.0992;
+          0       0       0   0   0   0   1       0       0    0       0       0;
+          0       0       0   0   0   0   0       1       0    0       0       0;
+          0       0       0   0   0   0   0       0       1    0       0       0;
+          0.9734  0       0   0   0   0   0.0488  0       0    0.9846  0       0;
+          0      -0.9734  0   0   0   0   0      -0.0488  0    0       0.9846  0;
+          0       0       0   0   0   0   0       0       0    0       0       0.9846] |> sparse
+    Bd = [0      -0.0726  0       0.0726;
+         -0.0726  0       0.0726  0;
+         -0.0152  0.0152 -0.0152  0.0152;
+          0      -0.0006 -0.0000  0.0006;
+          0.0006  0      -0.0006  0;
+          0.0106  0.0106  0.0106  0.0106;
+          0      -1.4512  0       1.4512;
+         -1.4512  0       1.4512  0;
+         -0.3049  0.3049 -0.3049  0.3049;
+          0      -0.0236  0       0.0236;
+          0.0236  0      -0.0236  0;
+          0.2107  0.2107  0.2107  0.2107] |> sparse
+    (nx, nu) = size(Bd)
+
+    # Constraints
+    u0 = 10.5916
+    umin = [9.6, 9.6, 9.6, 9.6] .- u0
+    umax = [13, 13, 13, 13] .- u0 
+    xmin = [[-pi/6, -pi/6, -Inf, -Inf, -Inf, -1]; -Inf .* ones(6)]
+    xmax = [[pi/6,  pi/6,  Inf,  Inf,  Inf, Inf]; Inf .* ones(6)]
+
+    # Objective function
+    Q = spdiagm([0, 0, 10, 10, 10, 10, 0, 0, 0, 5, 5, 5])
+    QN = Q
+    R = 0.1 * speye(nu)
+
+    # Initial and reference states
+    x0 = zeros(12)
+    xr = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+
+    # Prediction horizon
+    N = 10
+
+    # Cast MPC problem to a QP: x = (x(0),x(1),...,x(N),u(0),...,u(N-1))
+    # - quadratic objective
+    P = blockdiag(kron(speye(N), Q), QN, kron(speye(N), R))
+    # - linear objective
+    q = [repeat(-Q * xr, N); -QN * xr; zeros(N*nu)]
+    # - linear dynamics
+    Ax = kron(speye(N + 1), -speye(nx)) + kron(spdiagm(-1 => ones(N)), Ad)
+    Bu = kron([spzeros(1, N); speye(N)], Bd)
+    Aeq = [Ax Bu]
+    leq = [-x0; zeros(N * nx)]
+    ueq = leq
+    # - input and state constraints
+    Aineq = speye((N + 1) * nx + N * nu)
+    lineq = [repeat(xmin, N + 1); repeat(umin, N)]
+    uineq = [repeat(xmax, N + 1); repeat(umax, N)]
+    # - OSQP constraints
+    A, l, u = [Aeq; Aineq], [leq; lineq], [ueq; uineq]
+
+    # Create an OSQP model
+    m = OSQP.Model()
+
+    # Setup workspace
+    OSQP.setup!(m; P=P, q=q, A=A, l=l, u=u, warm_start=true)
+
+    # Simulate in closed loop
+    nsim = 15;
+    @time for _ in 1 : nsim
+        # Solve
+        res = OSQP.solve!(m)
+
+        # Check solver status
+        if res.info.status != :Solved
+            error("OSQP did not solve the problem!")
+        end
+
+        # Apply first control input to the plant
+        ctrl = res.x[(N+1)*nx+1:(N+1)*nx+nu]
+        global x0 = Ad * x0 + Bd * ctrl
+
+        # Update initial state
+        l[1:nx], u[1:nx] = -x0, -x0
+        OSQP.update!(m; l=l, u=u)
+    end
diff --git a/docs/examples/portfolio.rst b/docs/examples/portfolio.rst
new file mode 100644
index 0000000..60649b6
--- /dev/null
+++ b/docs/examples/portfolio.rst
@@ -0,0 +1,165 @@
+Portfolio optimization
+======================
+
+
+Portfolio optimization seeks to allocate assets in a way that maximizes the risk adjusted return,
+
+
+.. math::
+  \begin{array}{ll}
+    \mbox{maximize} & \mu^T x - \gamma \left( x^T \Sigma x \right) \\
+    \mbox{subject to} & \boldsymbol{1}^T x = 1 \\
+                      & x \ge 0
+  \end{array}
+
+
+where :math:`x \in \mathbf{R}^{n}` represents the portfolio, :math:`\mu \in \mathbf{R}^{n}` the vector of expected returns, :math:`\gamma > 0` the risk aversion parameter, and :math:`\Sigma \in \mathbf{S}^{n}_{+}` the risk model covariance matrix.
+The risk model is usually assumed to be the sum of a diagonal and a rank :math:`k < n` matrix,
+
+
+.. math::
+  \Sigma = F F^T + D,
+
+
+where :math:`F \in \mathbf{R}^{n \times k}` is the factor loading matrix and :math:`D \in \mathbf{S}^{n}_{+}` is a diagonal matrix describing the asset-specific risk.
+The resulting problem has the following equivalent form,
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} x^T D x + \frac{1}{2} y^T y - \frac{1}{2\gamma}\mu^T x \\
+    \mbox{subject to} & y = F^T x \\
+                      & \boldsymbol{1}^T x = 1 \\
+                      & x \ge 0
+  \end{array}
+
+
+
+Python
+------
+
+.. code:: python
+
+    import osqp
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Generate problem data
+    sp.random.seed(1)
+    n = 100
+    k = 10
+    F = sparse.random(n, k, density=0.7, format='csc')
+    D = sparse.diags(np.random.rand(n) * np.sqrt(k), format='csc')
+    mu = np.random.randn(n)
+    gamma = 1
+
+    # OSQP data
+    P = sparse.block_diag([D, sparse.eye(k)], format='csc')
+    q = np.hstack([-mu / (2*gamma), np.zeros(k)])
+    A = sparse.vstack([
+            sparse.hstack([F.T, -sparse.eye(k)]),
+            sparse.hstack([sparse.csc_matrix(np.ones((1, n))), sparse.csc_matrix((1, k))]),
+            sparse.hstack((sparse.eye(n), sparse.csc_matrix((n, k))))
+        ], format='csc')
+    l = np.hstack([np.zeros(k), 1., np.zeros(n)])
+    u = np.hstack([np.zeros(k), 1., np.ones(n)])
+
+    # Create an OSQP object
+    prob = osqp.OSQP()
+
+    # Setup workspace
+    prob.setup(P, q, A, l, u)
+
+    # Solve problem
+    res = prob.solve()
+
+
+
+Matlab
+------
+
+.. code:: matlab
+
+    % Generate problem data
+    rng(1)
+    n = 100;
+    k = 10;
+    F = sprandn(n, k, 0.7);
+    D = sparse(diag( sqrt(k)*rand(n,1) ));
+    mu = randn(n, 1);
+    gamma = 1;
+
+    % OSQP data
+    P = blkdiag(D, speye(k));
+    q = [-mu/(2*gamma); zeros(k, 1)];
+    A = [F', -speye(k);
+         ones(1, n), zeros(1, k);
+         speye(n), sparse(n, k)];
+    l = [zeros(k, 1); 1; zeros(n, 1)];
+    u = [zeros(k, 1); 1; ones(n, 1)];
+
+    % Create an OSQP object
+    prob = osqp;
+
+    % Setup workspace
+    prob.setup(P, q, A, l, u);
+
+    % Solve problem
+    res = prob.solve();
+
+
+
+CVXPY
+-----
+
+.. code:: python
+
+    from cvxpy import *
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Generate problem data
+    sp.random.seed(1)
+    n = 100
+    k = 10
+    F = sparse.random(n, k, density=0.7, format='csc')
+    D = sparse.diags(np.random.rand(n) * np.sqrt(k), format='csc')
+    mu = np.random.randn(n)
+    gamma = 1
+    Sigma = F*F.T + D
+
+    # Define problem
+    x = Variable(n)
+    objective = mu.T*x - gamma*quad_form(x, Sigma)
+    constraints = [sum(x) == 1, x >= 0]
+
+    # Solve with OSQP
+    Problem(Maximize(objective), constraints).solve(solver=OSQP)
+
+
+
+YALMIP
+------
+
+.. code:: matlab
+
+    % Generate problem data
+    rng(1)
+    n = 100;
+    k = 10;
+    F = sprandn(n, k, 0.7);
+    D = sparse(diag( sqrt(k)*rand(n,1) ));
+    mu = randn(n, 1);
+    gamma = 1;
+    Sigma = F*F' + D;
+
+    % Define problem
+    x = sdpvar(n, 1);
+    objective = gamma * (x'*Sigma*x) - mu'*x;
+    constraints = [sum(x) == 1, x >= 0];
+
+    % Solve with OSQP
+    options = sdpsettings('solver', 'osqp');
+    optimize(constraints, objective, options);
+
diff --git a/docs/examples/setup-and-solve.rst b/docs/examples/setup-and-solve.rst
new file mode 100644
index 0000000..8e6ea72
--- /dev/null
+++ b/docs/examples/setup-and-solve.rst
@@ -0,0 +1,186 @@
+Setup and solve
+===============
+
+
+Consider the following QP
+
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} x^T \begin{bmatrix}4 & 1\\ 1 & 2 \end{bmatrix} x + \begin{bmatrix}1 \\ 1\end{bmatrix}^T x \\
+    \mbox{subject to} & \begin{bmatrix}1 \\ 0 \\ 0\end{bmatrix} \leq \begin{bmatrix} 1 & 1\\ 1 & 0\\ 0 & 1\end{bmatrix} x \leq  \begin{bmatrix}1 \\ 0.7 \\ 0.7\end{bmatrix}
+  \end{array}
+
+
+
+We show below how to solve the problem in Python, Matlab, Julia and C.
+
+
+
+Python
+------
+
+.. code:: python
+
+    import osqp
+    import numpy as np
+    from scipy import sparse
+
+    # Define problem data
+    P = sparse.csc_matrix([[4, 1], [1, 2]])
+    q = np.array([1, 1])
+    A = sparse.csc_matrix([[1, 1], [1, 0], [0, 1]])
+    l = np.array([1, 0, 0])
+    u = np.array([1, 0.7, 0.7])
+
+    # Create an OSQP object
+    prob = osqp.OSQP()
+
+    # Setup workspace and change alpha parameter
+    prob.setup(P, q, A, l, u, alpha=1.0)
+
+    # Solve problem
+    res = prob.solve()
+
+
+
+Matlab
+------
+
+.. code:: matlab
+
+    % Define problem data
+    P = sparse([4, 1; 1, 2]);
+    q = [1; 1];
+    A = sparse([1, 1; 1, 0; 0, 1]);
+    l = [1; 0; 0];
+    u = [1; 0.7; 0.7];
+
+    % Create an OSQP object
+    prob = osqp;
+
+    % Setup workspace and change alpha parameter
+    prob.setup(P, q, A, l, u, 'alpha', 1);
+
+    % Solve problem
+    res = prob.solve();
+
+
+
+Julia
+------
+
+.. code:: julia
+
+    using OSQP
+    using Compat.SparseArrays
+
+    # Define problem data
+    P = sparse([4. 1.; 1. 2.])
+    q = [1.; 1.]
+    A = sparse([1. 1.; 1. 0.; 0. 1.])
+    l = [1.; 0.; 0.]
+    u = [1.; 0.7; 0.7]
+
+    # Crate OSQP object
+    prob = OSQP.Model()
+
+    # Setup workspace and change alpha parameter
+    OSQP.setup!(prob; P=P, q=q, A=A, l=l, u=u, alpha=1)
+
+    # Solve problem
+    results = OSQP.solve!(prob)
+
+
+
+R
+-
+
+.. code:: r
+
+    library(osqp)
+    library(Matrix)
+
+    # Define problem data
+    P <- Matrix(c(4., 1.,
+                  1., 2.), 2, 2, sparse = TRUE)
+    q <- c(1., 1.)
+    A <- Matrix(c(1., 1., 0.,
+                  1., 0., 1.), 3, 2, sparse = TRUE)
+    l <- c(1., 0., 0.)
+    u <- c(1., 0.7, 0.7)
+
+    # Change alpha parameter and setup workspace
+    settings <- osqpSettings(alpha = 1.0)
+    model <- osqp(P, q, A, l, u, settings)
+
+    # Solve problem
+    res <- model$Solve()
+
+
+
+C
+-
+
+.. code:: c
+
+    #include "osqp.h"
+
+    int main(int argc, char **argv) {
+        // Load problem data
+        c_float P_x[3] = {4.0, 1.0, 2.0, };
+        c_int P_nnz = 3;
+        c_int P_i[3] = {0, 0, 1, };
+        c_int P_p[3] = {0, 1, 3, };
+        c_float q[2] = {1.0, 1.0, };
+        c_float A_x[4] = {1.0, 1.0, 1.0, 1.0, };
+        c_int A_nnz = 4;
+        c_int A_i[4] = {0, 1, 0, 2, };
+        c_int A_p[3] = {0, 2, 4, };
+        c_float l[3] = {1.0, 0.0, 0.0, };
+        c_float u[3] = {1.0, 0.7, 0.7, };
+        c_int n = 2;
+        c_int m = 3;
+
+        // Exitflag
+        c_int exitflag = 0;
+
+        // Workspace structures
+        OSQPWorkspace *work;
+        OSQPSettings  *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+        OSQPData      *data     = (OSQPData *)c_malloc(sizeof(OSQPData));
+
+        // Populate data
+        if (data) {
+            data->n = n;
+            data->m = m;
+            data->P = csc_matrix(data->n, data->n, P_nnz, P_x, P_i, P_p);
+            data->q = q;
+            data->A = csc_matrix(data->m, data->n, A_nnz, A_x, A_i, A_p);
+            data->l = l;
+            data->u = u;
+        }
+
+        // Define solver settings as default
+        if (settings) {
+            osqp_set_default_settings(settings);
+            settings->alpha = 1.0; // Change alpha parameter
+        }
+
+        // Setup workspace
+        exitflag = osqp_setup(&work, data, settings);
+
+        // Solve Problem
+        osqp_solve(work);
+
+        // Cleanup
+        osqp_cleanup(work);
+        if (data) {
+            if (data->A) c_free(data->A);
+            if (data->P) c_free(data->P);
+            c_free(data);
+        }
+        if (settings) c_free(settings);
+
+        return exitflag;
+    };
diff --git a/docs/examples/svm.rst b/docs/examples/svm.rst
new file mode 100644
index 0000000..d9ecfb5
--- /dev/null
+++ b/docs/examples/svm.rst
@@ -0,0 +1,165 @@
+Support vector machine (SVM)
+============================
+
+*Support vector machine* seeks an affine function that approximately classifies the two sets of points.
+The problem can be stated as
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} x^T x + \gamma \sum_{i=1}^{m} \max(0, b_i a_i^T x + 1),
+  \end{array}
+
+where :math:`b_i \in \{ -1, +1 \}` is a set label, and :math:`a_i` is a vector of features for the :math:`i`-th point.
+The problem has the following equivalent form
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize}   & \frac{1}{2} x^T x + \gamma \boldsymbol{1}^T t \\
+    \mbox{subject to} & t \ge {\rm diag}(b) Ax + 1 \\
+                      & t \ge 0,
+  \end{array}
+
+where :math:`{\rm diag}(b)` denotes the diagonal matrix with elements of :math:`b` on its diagonal.
+
+
+
+Python
+------
+
+.. code:: python
+
+    import osqp
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Generate problem data
+    sp.random.seed(1)
+    n = 10
+    m = 1000
+    N = int(m / 2)
+    gamma = 1.0
+    b = np.hstack([np.ones(N), -np.ones(N)])
+    A_upp = sparse.random(N, n, density=0.5)
+    A_low = sparse.random(N, n, density=0.5)
+    Ad = sparse.vstack([
+            A_upp / np.sqrt(n) + (A_upp != 0.).astype(float) / n,
+            A_low / np.sqrt(n) - (A_low != 0.).astype(float) / n
+         ], format='csc')
+
+    # OSQP data
+    Im = sparse.eye(m)
+    P = sparse.block_diag([sparse.eye(n), sparse.csc_matrix((m, m))], format='csc')
+    q = np.hstack([np.zeros(n), gamma*np.ones(m)])
+    A = sparse.vstack([
+            sparse.hstack([sparse.diags(b).dot(Ad), -Im]),
+            sparse.hstack([sparse.csc_matrix((m, n)), Im])
+        ], format='csc')
+    l = np.hstack([-np.inf*np.ones(m), np.zeros(m)])
+    u = np.hstack([-np.ones(m), np.inf*np.ones(m)])
+
+    # Create an OSQP object
+    prob = osqp.OSQP()
+
+    # Setup workspace
+    prob.setup(P, q, A, l, u)
+
+    # Solve problem
+    res = prob.solve()
+
+
+Matlab
+------
+
+.. code:: matlab
+
+    % Generate problem data
+    rng(1)
+    n = 10;
+    m = 1000;
+    N = ceil(m/2);
+    gamma = 1;
+    A_upp = sprandn(N, n, 0.5);
+    A_low = sprandn(N, n, 0.5);
+    Ad = [A_upp / sqrt(n) + (A_upp ~= 0) / n;
+          A_low / sqrt(n) - (A_low ~= 0) / n];
+    b = [ones(N, 1); -ones(N,1)];
+
+    % OSQP data
+    P = blkdiag(speye(n), sparse(m, m));
+    q = [zeros(n,1); gamma*ones(m,1)];
+    A = [diag(b)*Ad, -speye(m);
+         sparse(m, n), speye(m)];
+    l = [-inf*ones(m, 1); zeros(m, 1)];
+    u = [-ones(m, 1); inf*ones(m, 1)];
+
+    % Create an OSQP object
+    prob = osqp;
+
+    % Setup workspace
+    prob.setup(P, q, A, l, u);
+
+    % Solve problem
+    res = prob.solve();
+
+
+
+CVXPY
+-----
+
+.. code:: python
+
+    from cvxpy import *
+    import numpy as np
+    import scipy as sp
+    from scipy import sparse
+
+    # Generate problem data
+    sp.random.seed(1)
+    n = 10
+    m = 1000
+    N = int(m / 2)
+    gamma = 1.0
+    b = np.hstack([np.ones(N), -np.ones(N)])
+    A_upp = sparse.random(N, n, density=0.5)
+    A_low = sparse.random(N, n, density=0.5)
+    A = sparse.vstack([
+            A_upp / np.sqrt(n) + (A_upp != 0.).astype(float) / n,
+            A_low / np.sqrt(n) - (A_low != 0.).astype(float) / n
+        ], format='csc')
+
+    # Define problem
+    x = Variable(n)
+    objective = 0.5*sum_squares(x) + gamma*sum(pos(diag(b)*A*x + 1))
+
+    # Solve with OSQP
+    Problem(Minimize(objective)).solve(solver=OSQP)
+    
+
+
+
+YALMIP
+------
+
+.. code:: matlab
+
+    % Generate problem data
+    rng(1)
+    n = 10;
+    m = 1000;
+    N = ceil(m/2);
+    gamma = 1;
+    A_upp = sprandn(N, n, 0.5);
+    A_low = sprandn(N, n, 0.5);
+    A = [A_upp / sqrt(n) + (A_upp ~= 0) / n;
+         A_low / sqrt(n) - (A_low ~= 0) / n];
+    b = [ones(N, 1); -ones(N,1)];
+
+    % Define problem
+    x = sdpvar(n, 1);
+    objective = 0.5*norm(x)^2 + gamma*sum(max(diag(b)*A*x + 1, 0));
+
+    % Solve with OSQP
+    options = sdpsettings('solver','osqp');
+    optimize([],objective,options);
+
diff --git a/docs/examples/update-matrices.rst b/docs/examples/update-matrices.rst
new file mode 100644
index 0000000..b96e304
--- /dev/null
+++ b/docs/examples/update-matrices.rst
@@ -0,0 +1,242 @@
+Update matrices
+===============
+
+
+Consider the following QP
+
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} x^T \begin{bmatrix}4 & 1\\ 1 & 2 \end{bmatrix} x + \begin{bmatrix}1 \\ 1\end{bmatrix}^T x \\
+    \mbox{subject to} & \begin{bmatrix}1 \\ 0 \\ 0\end{bmatrix} \leq \begin{bmatrix} 1 & 1\\ 1 & 0\\ 0 & 1\end{bmatrix} x \leq \begin{bmatrix}1 \\ 0.7 \\ 0.7\end{bmatrix}
+  \end{array}
+
+
+
+We show below how to setup and solve the problem.
+Then we update the matrices :math:`P` and :math:`A` and solve the updated problem
+
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} x^T \begin{bmatrix}5 & 1.5\\ 1.5 & 1 \end{bmatrix} x + \begin{bmatrix}1 \\ 1\end{bmatrix}^T x \\
+    \mbox{subject to} & \begin{bmatrix}1 \\ 0 \\ 0\end{bmatrix} \leq \begin{bmatrix} 1.2 & 1.1\\ 1.5 & 0\\ 0 & 0.8\end{bmatrix} x \leq \begin{bmatrix}1 \\ 0.7 \\ 0.7\end{bmatrix}
+  \end{array}
+  
+
+
+Python
+------
+
+.. code:: python
+
+    import osqp
+    import numpy as np
+    from scipy import sparse
+
+    # Define problem data
+    P = sparse.csc_matrix([[4, 1], [1, 2]])
+    q = np.array([1, 1])
+    A = sparse.csc_matrix([[1, 1], [1, 0], [0, 1]])
+    l = np.array([1, 0, 0])
+    u = np.array([1, 0.7, 0.7])
+
+    # Create an OSQP object
+    prob = osqp.OSQP()
+
+    # Setup workspace
+    prob.setup(P, q, A, l, u)
+
+    # Solve problem
+    res = prob.solve()
+
+    # Update problem
+    # NB: Update only upper triangular part of P
+    P_new = sparse.csc_matrix([[5, 1.5], [1.5, 1]])
+    A_new = sparse.csc_matrix([[1.2, 1.1], [1.5, 0], [0, 0.8]])
+    prob.update(Px=sparse.triu(P_new).data, Ax=A_new.data)
+
+    # Solve updated problem
+    res = prob.solve()
+
+
+
+Matlab
+------
+
+.. code:: matlab
+
+    % Define problem data
+    P = sparse([4, 1; 1, 2]);
+    q = [1; 1];
+    A = sparse([1, 1; 1, 0; 0, 1]);
+    l = [1; 0; 0];
+    u = [1; 0.7; 0.7];
+
+    % Create an OSQP object
+    prob = osqp;
+
+    % Setup workspace
+    prob.setup(P, q, A, l, u);
+
+    % Solve problem
+    res = prob.solve();
+
+    % Update problem
+    % NB: Update only upper triangular part of P
+    P_new = sparse([5, 1.5; 1.5, 1]);
+    A_new = sparse([1.2, 1.1; 1.5, 0; 0, 0.8]);
+    prob.update('Px', nonzeros(triu(P_new)), 'Ax', nonzeros(A_new));
+
+    % Solve updated problem
+    res = prob.solve();
+
+
+
+Julia
+------
+
+.. code:: julia
+
+    using OSQP
+    using Compat.SparseArrays, Compat.LinearAlgebra
+
+    # Define problem data
+    P = sparse([4. 1.; 1. 2.])
+    q = [1.; 1.]
+    A = sparse([1. 1.; 1. 0.; 0. 1.])
+    l = [1.; 0.; 0.]
+    u = [1.; 0.7; 0.7]
+
+    # Crate OSQP object
+    prob = OSQP.Model()
+
+    # Setup workspace
+    OSQP.setup!(prob; P=P, q=q, A=A, l=l, u=u)
+
+    # Solve problem
+    results = OSQP.solve!(prob)
+
+    # Update problem
+    # NB: Update only upper triangular part of P
+    P_new = sparse([5. 1.5; 1.5 1.])
+    A_new = sparse([1.2 1.1; 1.5 0.; 0. 0.8])
+    OSQP.update!(prob, Px=triu(P_new).nzval, Ax=A_new.nzval)
+
+    # Solve updated problem
+    results = OSQP.solve!(prob)
+
+
+
+R
+-
+
+.. code:: r
+
+    library(osqp)
+    library(Matrix)
+
+    # Define problem data
+    P <- Matrix(c(4., 1.,
+                  1., 2.), 2, 2, sparse = TRUE)
+    q <- c(1., 1.)
+    A <- Matrix(c(1., 1., 0.,
+                  1., 0., 1.), 3, 2, sparse = TRUE)
+    l <- c(1., 0., 0.)
+    u <- c(1., 0.7, 0.7)
+
+    # Setup workspace
+    model <- osqp(P, q, A, l, u)
+
+    # Solve problem
+    res <- model$Solve()
+
+    # Update problem
+    # NB: Update only upper triangular part of P
+    P_new <- Matrix(c(5., 1.5,
+                      1.5, 1.), 2, 2, sparse = TRUE)
+    A_new <- Matrix(c(1.2, 1.5, 0.,
+                      1.1, 0., 0.8), 3, 2, sparse = TRUE)
+    model$Update(Px = P_new@x, Ax = A_new@x)
+
+    # Solve updated problem
+    res <- model$Solve()
+
+
+
+C
+-
+
+.. code:: c
+
+    #include "osqp.h"
+
+    int main(int argc, char **argv) {
+        // Load problem data
+        c_float P_x[3] = {4.0, 1.0, 2.0, };
+        c_float P_x_new[3] = {5.0, 1.5, 1.0, };
+        c_int P_nnz = 3;
+        c_int P_i[3] = {0, 0, 1, };
+        c_int P_p[3] = {0, 1, 3, };
+        c_float q[2] = {1.0, 1.0, };
+        c_float q_new[2] = {2.0, 3.0, };
+        c_float A_x[4] = {1.0, 1.0, 1.0, 1.0, };
+        c_float A_x_new[4] = {1.2, 1.5, 1.1, 0.8, };
+        c_int A_nnz = 4;
+        c_int A_i[4] = {0, 1, 0, 2, };
+        c_int A_p[3] = {0, 2, 4, };
+        c_float l[3] = {1.0, 0.0, 0.0, };
+        c_float l_new[3] = {2.0, -1.0, -1.0, };
+        c_float u[3] = {1.0, 0.7, 0.7, };
+        c_float u_new[3] = {2.0, 2.5, 2.5, };
+        c_int n = 2;
+        c_int m = 3;
+
+        // Exitflag
+        c_int exitflag = 0;
+
+        // Workspace structures
+        OSQPWorkspace *work;
+        OSQPSettings  *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+        OSQPData      *data     = (OSQPData *)c_malloc(sizeof(OSQPData));
+
+        // Populate data
+        if (data) {
+            data = (OSQPData *)c_malloc(sizeof(OSQPData));
+            data->n = n;
+            data->m = m;
+            data->P = csc_matrix(data->n, data->n, P_nnz, P_x, P_i, P_p);
+            data->q = q;
+            data->A = csc_matrix(data->m, data->n, A_nnz, A_x, A_i, A_p);
+            data->l = l;
+            data->u = u;
+        }
+
+        // Define Solver settings as default
+        if (settings) osqp_set_default_settings(settings);
+
+        // Setup workspace
+        exitflag = osqp_setup(&work, data, settings);
+
+        // Solve problem
+        osqp_solve(work);
+
+        // Update problem
+        // NB: Update only upper triangular part of P
+        osqp_update_P(work, P_x_new, OSQP_NULL, 3);
+        osqp_update_A(work, A_x_new, OSQP_NULL, 4);
+
+        // Solve updated problem
+        osqp_solve(work);
+
+        // Cleanup
+        osqp_cleanup(work);
+        if (data) {
+            if (data->A) c_free(data->A);
+            if (data->P) c_free(data->P);
+            c_free(data);
+        }
+        if (settings) c_free(settings);
+
+        return exitflag;
+    };
diff --git a/docs/examples/update-vectors.rst b/docs/examples/update-vectors.rst
new file mode 100644
index 0000000..2b7a8ae
--- /dev/null
+++ b/docs/examples/update-vectors.rst
@@ -0,0 +1,236 @@
+Update vectors
+==============
+
+
+Consider the following QP
+
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} x^T \begin{bmatrix}4 & 1\\ 1 & 2 \end{bmatrix} x + \begin{bmatrix}1 \\ 1\end{bmatrix}^T x \\
+    \mbox{subject to} & \begin{bmatrix}1 \\ 0 \\ 0\end{bmatrix} \leq \begin{bmatrix} 1 & 1\\ 1 & 0\\ 0 & 1\end{bmatrix} x \leq \begin{bmatrix}1 \\ 0.7 \\ 0.7\end{bmatrix}
+  \end{array}
+
+
+
+We show below how to setup and solve the problem.
+Then we update the vectors :math:`q`, :math:`l`, and :math:`u` and solve the updated problem
+
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} x^T \begin{bmatrix}4 & 1\\ 1 & 2 \end{bmatrix} x + \begin{bmatrix}2 \\ 3\end{bmatrix}^T x \\
+    \mbox{subject to} & \begin{bmatrix}2 \\ -1 \\ -1\end{bmatrix} \leq \begin{bmatrix} 1 & 1\\ 1 & 0\\ 0 & 1\end{bmatrix} x \leq \begin{bmatrix}2 \\ 2.5 \\ 2.5\end{bmatrix}
+  \end{array}
+  
+
+
+Python
+------
+
+.. code:: python
+
+    import osqp
+    import numpy as np
+    from scipy import sparse
+
+    # Define problem data
+    P = sparse.csc_matrix([[4, 1], [1, 2]])
+    q = np.array([1, 1])
+    A = sparse.csc_matrix([[1, 1], [1, 0], [0, 1]])
+    l = np.array([1, 0, 0])
+    u = np.array([1, 0.7, 0.7])
+
+    # Create an OSQP object
+    prob = osqp.OSQP()
+
+    # Setup workspace
+    prob.setup(P, q, A, l, u)
+
+    # Solve problem
+    res = prob.solve()
+
+    # Update problem
+    q_new = np.array([2, 3])
+    l_new = np.array([2, -1, -1])
+    u_new = np.array([2, 2.5, 2.5])
+    prob.update(q=q_new, l=l_new, u=u_new)
+
+    # Solve updated problem
+    res = prob.solve()
+
+
+
+Matlab
+------
+
+.. code:: matlab
+
+    % Define problem data
+    P = sparse([4, 1; 1, 2]);
+    q = [1; 1];
+    A = sparse([1, 1; 1, 0; 0, 1]);
+    l = [1; 0; 0];
+    u = [1; 0.7; 0.7];
+
+    % Create an OSQP object
+    prob = osqp;
+
+    % Setup workspace
+    prob.setup(P, q, A, l, u);
+
+    % Solve problem
+    res = prob.solve();
+
+    % Update problem
+    q_new = [2; 3];
+    l_new = [2; -1; -1];
+    u_new = [2; 2.5; 2.5];
+    prob.update('q', q_new, 'l', l_new, 'u', u_new);
+
+    % Solve updated problem
+    res = prob.solve();
+
+
+
+Julia
+------
+
+.. code:: julia
+
+    using OSQP
+    using Compat.SparseArrays
+
+    # Define problem data
+    P = sparse([4. 1.; 1. 2.])
+    q = [1.; 1.]
+    A = sparse([1. 1.; 1. 0.; 0. 1.])
+    l = [1.; 0.; 0.]
+    u = [1.; 0.7; 0.7]
+
+    # Crate OSQP object
+    prob = OSQP.Model()
+
+    # Setup workspace
+    OSQP.setup!(prob; P=P, q=q, A=A, l=l, u=u)
+
+    # Solve problem
+    results = OSQP.solve!(prob)
+
+    # Update problem
+    q_new = [2.; 3.]
+    l_new = [2.; -1.; -1.]
+    u_new = [2.; 2.5; 2.5]
+    OSQP.update!(prob, q=q_new, l=l_new, u=u_new)
+
+    # Solve updated problem
+    results = OSQP.solve!(prob)
+
+
+
+R
+-
+
+.. code:: r
+
+    library(osqp)
+    library(Matrix)
+
+    # Define problem data
+    P <- Matrix(c(4., 1.,
+                  1., 2.), 2, 2, sparse = TRUE)
+    q <- c(1., 1.)
+    A <- Matrix(c(1., 1., 0.,
+                  1., 0., 1.), 3, 2, sparse = TRUE)
+    l <- c(1., 0., 0.)
+    u <- c(1., 0.7, 0.7)
+
+    # Setup workspace
+    model <- osqp(P, q, A, l, u)
+
+    # Solve problem
+    res <- model$Solve()
+
+    # Update problem
+    q_new <- c(2., 3.)
+    l_new <- c(2., -1., -1.)
+    u_new <- c(2., 2.5, 2.5)
+    model$Update(q = q_new, l = l_new, u = u_new)
+
+    # Solve updated problem
+    res <- model$Solve()
+
+
+
+C
+-
+
+.. code:: c
+
+    #include "osqp.h"
+
+    int main(int argc, char **argv) {
+        // Load problem data
+        c_float P_x[3] = {4.0, 1.0, 2.0, };
+        c_int P_nnz = 3;
+        c_int P_i[3] = {0, 0, 1, };
+        c_int P_p[3] = {0, 1, 3, };
+        c_float q[2] = {1.0, 1.0, };
+        c_float q_new[2] = {2.0, 3.0, };
+        c_float A_x[4] = {1.0, 1.0, 1.0, 1.0, };
+        c_int A_nnz = 4;
+        c_int A_i[4] = {0, 1, 0, 2, };
+        c_int A_p[3] = {0, 2, 4, };
+        c_float l[3] = {1.0, 0.0, 0.0, };
+        c_float l_new[3] = {2.0, -1.0, -1.0, };
+        c_float u[3] = {1.0, 0.7, 0.7, };
+        c_float u_new[3] = {2.0, 2.5, 2.5, };
+        c_int n = 2;
+        c_int m = 3;
+
+        // Exitflag
+        c_int exitflag = 0;
+
+        // Workspace structures
+        OSQPWorkspace *work;
+        OSQPSettings  *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+        OSQPData      *data     = (OSQPData *)c_malloc(sizeof(OSQPData));
+
+        // Populate data
+        if (data) {
+            data->n = n;
+            data->m = m;
+            data->P = csc_matrix(data->n, data->n, P_nnz, P_x, P_i, P_p);
+            data->q = q;
+            data->A = csc_matrix(data->m, data->n, A_nnz, A_x, A_i, A_p);
+            data->l = l;
+            data->u = u;
+        }
+
+        // Define solver settings as default
+        if (settings) osqp_set_default_settings(settings);
+
+        // Setup workspace
+        exitflag = osqp_setup(&work, data, settings);
+
+        // Solve problem
+        osqp_solve(work);
+
+        // Update problem
+        osqp_update_lin_cost(work, q_new);
+        osqp_update_bounds(work, l_new, u_new);
+
+        // Solve updated problem
+        osqp_solve(work);
+
+        // Cleanup
+        osqp_cleanup(work);
+        if (data) {
+            if (data->A) c_free(data->A);
+            if (data->P) c_free(data->P);
+            c_free(data);
+        }
+        if (settings) c_free(settings);
+
+        return exitflag;
+    };
diff --git a/docs/get_started/C.rst b/docs/get_started/C.rst
new file mode 100644
index 0000000..18afa57
--- /dev/null
+++ b/docs/get_started/C.rst
@@ -0,0 +1,43 @@
+.. _install_osqp_libs:
+
+CC++
+=====
+
+Binaries
+--------
+
+Precompiled platform-dependent shared and static libraries are available on `GitHub <https://github.com/osqp/osqp/releases>`_.
+We here assume that the user uncompressed each archive to :code:`OSQP_FOLDER`.
+
+Each archive contains static :code:`OSQP_FOLDER/lib/libosqp.a` and shared :code:`OSQP_FOLDER/lib/libosqp.ext` libraries to be used to interface OSQP to any C/C++ software.
+The extension :code:`.ext` is platform dependent and is :code:`.so` for Linux, :code:`.dylib` for Mac and :code:`.dll` for Windows.
+The required include files can be found in :code:`OSQP_FOLDER/include`.
+
+Simply compile with the linker option with :code:`-LOSQP_FOLDER/lib` and :code:`-losqp`.
+
+If you are interested in development builds, you can find them on `GitHub <https://github.com/osqp/osqp/releases>`__.
+
+Sources
+-------
+
+The OSQP libraries can also be compiled from sources. For more details see :ref:`build_from_sources`.
+
+
+Including OSQP in CMake projects
+--------------------------------
+If you compiled OSQP from sources and followed the CMake installation instructions in :ref:`install_the_binaries` section, you can include the package in another CMake project with the following lines depending on whether you need a shared or a static library
+
+.. code::
+
+   # Find OSQP library and headers
+   find_package(osqp REQUIRED)
+
+   # Link the OSQP shared library
+   target_link_libraries(yourTarget PRIVATE osqp::osqp)
+
+   # or...
+
+   # Link the OSQP static library
+   target_link_libraries(yourTarget PRIVATE osqp::osqpstatic)
+
+
diff --git a/docs/get_started/cutest.rst b/docs/get_started/cutest.rst
new file mode 100644
index 0000000..c3de773
--- /dev/null
+++ b/docs/get_started/cutest.rst
@@ -0,0 +1,12 @@
+CUTEst
+=======
+
+To be able to use OSQP and `CUTEst <https://github.com/ralna/CUTEst/wiki>`_ you need to
+
+* Install `CUTEst <https://github.com/ralna/CUTEst/wiki>`_
+* Compile :ref:`OSQP from sources <build_from_sources>`
+* Set the environment variable :code:`OSQP` to the main OSQP directory containing source code for which the compiled binary libraries lie in :code:`$OSQP/build/out`.
+
+For more details, see the `README.osqp <https://github.com/ralna/CUTEst/blob/master/src/osqp/README.osqp>`_ file in the CUTEst repository.
+
+
diff --git a/docs/get_started/index.rst b/docs/get_started/index.rst
new file mode 100644
index 0000000..266222a
--- /dev/null
+++ b/docs/get_started/index.rst
@@ -0,0 +1,17 @@
+Get started
+===============
+
+To get started simply choose your language interface and follow the easy installation instructions below:
+
+.. toctree::
+   :maxdepth: 1
+   :glob:
+
+   sources.rst
+   C.rst
+   python.rst
+   julia.rst
+   matlab.rst
+   r.rst
+   cutest.rst
+   linear_system_solvers.rst
diff --git a/docs/get_started/julia.rst b/docs/get_started/julia.rst
new file mode 100644
index 0000000..0acea0d
--- /dev/null
+++ b/docs/get_started/julia.rst
@@ -0,0 +1,12 @@
+Julia
+======
+
+Julia interface can be directly installed from Julia package manager by running
+
+.. code:: julia
+
+   ] add OSQP
+
+
+The interface code is available on `GitHub <https://github.com/osqp/OSQP.jl>`_.
+
diff --git a/docs/get_started/linear_system_solvers.rst b/docs/get_started/linear_system_solvers.rst
new file mode 100644
index 0000000..0cbb1c9
--- /dev/null
+++ b/docs/get_started/linear_system_solvers.rst
@@ -0,0 +1,72 @@
+.. _linear_system_solvers_installation :
+
+Linear System Solvers
+======================
+
+The linear system solver is a core part of the OSQP algorithm.
+Depending on the problem instance, different linear system solvers can greatly speedup or reduce the computation time of OSQP.
+To set the preferred linear system solver, see :ref:`linear_system_solvers_setting`.
+
+Dynamic shared library loading
+------------------------------
+OSQP dynamically loads the shared libraries related to the selected external solver. Thus, there is no need to link it at compile time.
+The only requirement is that the shared library related to the solver is in the library path of the operating system
+
++------------------+---------------------------+----------------+
+| Operating system | Path variable             | Extension      |
++==================+===========================+================+
+| Linux            | :code:`LD_LIBRARY_PATH`   | :code:`.so`    |
++------------------+---------------------------+----------------+
+| Mac              | :code:`DYLD_LIBRARY_PATH` | :code:`.dylib` |
++------------------+---------------------------+----------------+
+| Windows          | :code:`PATH`              | :code:`.dll`   |
++------------------+---------------------------+----------------+
+
+
+
+
+
+QDLDL
+---------------
+OSQP comes with `QDLDL <https://github.com/osqp/qdldl>`_ internally installed.
+It does not require any external shared library.
+QDLDL is a sparse direct solver that works well for most small to medium sized problems.
+However, it becomes not really efficient for large scale problems since it is not multi-threaded.
+
+
+MKL Pardiso
+-----------
+`MKL Pardiso <https://software.intel.com/en-us/mkl-developer-reference-fortran-intel-mkl-pardiso-parallel-direct-sparse-solver-interface>`_ is an efficient multi-threaded linear system solver that works well for large scale problems part of the Intel Math Kernel Library.
+Intel offers `free lincenses <https://software.intel.com/en-us/articles/free-mkl>`_ for MKL for most non-commercial applications.
+
+Install with MKL
+^^^^^^^^^^^^^^^^
+We can install MKL Pardiso by using the standard `MKL installer <https://software.intel.com/en-us/mkl>`_.
+The main library to be loaded is called :code:`libmkl_rt`.
+To add it, together with its dependencies, to your path, just execute the automatic MKL script.
+
++------------------+------------------------------------------------+
+| Operating system | Script                                         |
++==================+================================================+
+| Linux            | :code:`source $MKLROOT/bin/mklvars.sh intel64` |
++------------------+------------------------------------------------+
+| Mac              | :code:`source $MKLROOT/bin/mklvars.sh intel64` |
++------------------+------------------------------------------------+
+| Windows          | :code:`%MKLROOT%\mklvars.bat intel64`          |
++------------------+------------------------------------------------+
+
+where :code:`MKLROOT` is the MKL installation directory.
+
+Install with Anaconda
+^^^^^^^^^^^^^^^^^^^^^
+`Anaconda Python distribution <https://www.anaconda.com/download/>`_ comes with the intel MKL libraries preinstalled including MKL Pardiso.
+To use this version, the Anaconda libraries folders have to be in your system path.
+Anaconda environments should add them automatically so in most cases you do not have to do anything. If you get an error where OSQP cannot find MKL, you can add the right path by adding the output from the following command to your path variable:
+
+.. code::
+
+   echo "`ls -rd ${CONDA_ROOT}/pkgs/*/ | grep mkl-2 | head -n 1`lib:`ls -rd ${CONDA_ROOT}/pkgs/*/ | grep intel-openmp- | head -n 1`lib"
+
+
+where :code:`ANACONDA_ROOT` is the root of your anaconda installation.
+
diff --git a/docs/get_started/matlab.rst b/docs/get_started/matlab.rst
new file mode 100644
index 0000000..e8e58ea
--- /dev/null
+++ b/docs/get_started/matlab.rst
@@ -0,0 +1,60 @@
+Matlab
+======
+OSQP Matlab interface requires Matlab 2015b or newer.
+
+
+Binaries
+--------
+
+Precompiled platform-dependent Matlab binaries are available on `GitHub <https://github.com/osqp/osqp-matlab/releases>`_.
+
+To install the interface, just run the following commands:
+
+.. code:: matlab
+
+    websave('install_osqp.m','https://raw.githubusercontent.com/osqp/osqp-matlab/master/package/install_osqp.m');
+    install_osqp
+
+
+Sources
+-------
+
+You need to install the following (see :ref:`build_from_sources` for more details):
+
+- A supported 64bit `C/C++ compiler <https://www.mathworks.com/support/compilers.html>`_
+- `CMake <https://cmake.org/>`_
+
+
+
+After you install both, check that your compiler is selected by executing
+
+.. code:: matlab
+
+   mex -setup
+
+.. note::
+
+   **Windows**: If Matlab does not find TDM-GCC, you need to set the environment variable :code:`MW_MINGW64_LOC` as follows
+
+   .. code:: matlab
+
+      setenv('MW_MINGW64_LOC', 'C:\TDM-GCC-64')
+
+
+   where :code:`C:\TDM-GCC-64` is the installation folder for TDM-GCC.
+
+You can now build the interface by running inside Matlab
+
+.. code:: matlab
+
+   !git clone --recurse-submodules https://github.com/osqp/osqp-matlab
+   cd osqp-matlab
+   make_osqp
+
+
+Then you can add the interface to the search path by executing from the same directory
+
+.. code:: matlab
+
+   addpath(pwd)
+   savepath
diff --git a/docs/get_started/python.rst b/docs/get_started/python.rst
new file mode 100644
index 0000000..a29e0eb
--- /dev/null
+++ b/docs/get_started/python.rst
@@ -0,0 +1,44 @@
+Python
+======
+
+Python interface supports Python 2.7 and 3.5 or newer.
+
+Pip
+----
+
+.. code:: bash
+
+   pip install osqp
+
+
+Anaconda
+--------
+
+.. code:: bash
+
+   conda install -c conda-forge osqp
+
+
+Sources
+---------
+You need to install the following (see :ref:`build_from_sources` for more details):
+
+- `GCC compiler <https://gcc.gnu.org/>`_
+- `CMake <https://cmake.org/>`_
+
+.. note::
+
+   **Windows**: You need to install **also** the Visual Studio C++ compiler:
+
+   * Python 2: `Visual C++ 9.0 for Python (VC 9.0) <https://www.microsoft.com/en-us/download/details.aspx?id=44266>`_
+
+   * Python 3: `Build Tools for Visual Studio 2017 <https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2017>`_
+
+
+Now you are ready to build OSQP python interface from sources. Run the following in your terminal
+
+.. code:: bash
+
+   git clone --recurse-submodules https://github.com/osqp/osqp-python
+   cd osqp-python
+   python setup.py install
diff --git a/docs/get_started/r.rst b/docs/get_started/r.rst
new file mode 100644
index 0000000..a8fb63f
--- /dev/null
+++ b/docs/get_started/r.rst
@@ -0,0 +1,40 @@
+R
+======
+
+Binaries
+--------
+
+A pre-compiled version of the OSQP R interface can be installed directly from within R.   Note that this will install the OSQP interface from the current CRAN repository, which may not be the most up-to-date version:
+
+.. code:: r
+
+  install.packages("osqp")
+
+
+
+The pre-compiled binaries can also be downloaded `directly from the CRAN server
+<https://cran.r-project.org/web/packages/osqp/>`_.
+
+From Sources
+------------
+
+If you would like to use the most recent version of OSQP-R and have access to git on your machine along with a suitable compiler, then you can do the following from within a terminal:
+
+.. code:: r
+
+  git clone --recursive https://github.com/osqp/osqp-r.git
+  cd osqp-r
+  R CMD install .
+
+From Sources (within R)
+-----------------------
+
+If you would like to install the latest version directly from with R (e.g.\ because you do not have ``git`` installed) and have a suitable compiler, then you can do the following from within R:
+
+.. code:: r
+
+  install.packages("remotes")
+  remotes::install_github("r-lib/remotes#103")
+  remotes::install_git("git://github.com/OxfordControl/osqp-r",submodules = TRUE)
+
+Note that the second line above is necessary because the "remotes" package in R does not currently support recursive cloning of git submodules.
diff --git a/docs/get_started/sources.rst b/docs/get_started/sources.rst
new file mode 100644
index 0000000..760ae3b
--- /dev/null
+++ b/docs/get_started/sources.rst
@@ -0,0 +1,137 @@
+.. _build_from_sources:
+
+
+Build from sources
+==================
+
+Install GCC and CMake
+----------------------
+
+The main compilation directives are specified using
+
+- `GCC compiler <https://gcc.gnu.org/>`_ to build the binaries
+- `CMake <https://cmake.org/>`__ to create the Makefiles
+
+
+Linux
+^^^^^
+Both :code:`gcc` and :code:`cmake` commands are already installed by default.
+
+Mac OS
+^^^^^^
+
+Install Xcode and command line tools
+""""""""""""""""""""""""""""""""""""
+
+#. Install the latest release of `Xcode <https://developer.apple.com/download/>`_.
+
+#. Install the command line tools by executing from the terminal
+
+    .. code:: bash
+
+        xcode-select --install
+
+Install CMake via Homebrew
+"""""""""""""""""""""""""""
+
+#. Install `Homebrew <https://brew.sh/>`_ and update its packages to the latest version.
+
+#. Install cmake by executing
+
+    .. code:: bash
+
+        brew install cmake
+
+
+Windows
+^^^^^^^
+
+#. Install `TDM-GCC <http://tdm-gcc.tdragon.net/download>`_ 32bit or 64bit depending on your platform.
+
+#. Install the latest binaries of `CMake <https://cmake.org/download/#latest>`__.
+
+
+Build the binaries
+------------------
+
+Run the following commands from the terminal
+
+#. You first need to get the sources from one of these two options:
+
+    * Download the compressed source file from the latest OSQP release `at GitHub <https://github.com/osqp/osqp/releases>`_.
+
+    * Clone the repository
+
+        .. code:: bash
+
+            git clone --recursive https://github.com/osqp/osqp
+
+#. Create :code:`build` directory and change directory
+
+        .. code:: bash
+
+            cd osqp
+            mkdir build
+            cd build
+
+#. Create Makefiles
+
+    - In Linux and Mac OS run
+
+        .. code:: bash
+
+            cmake -G "Unix Makefiles" ..
+
+    - In Windows run
+
+        .. code:: bash
+
+            cmake -G "MinGW Makefiles" ..
+
+
+#. Compile OSQP
+
+    .. code:: bash
+
+       cmake --build .
+
+
+Thanks to CMake, it is possible to create projects for a wide variety of IDEs; see `here <https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html>`_ for more details. For example, to create a project for Visual Studio 14 2015, it is just necessary to run
+
+.. code:: bash
+
+   cmake -G "Visual Studio 14 2015" ..
+
+
+The compilation will generate the demo :code:`osqp_demo` and the unittests :code:`osqp_tester` executables. In the case of :code:`Unix` or :code:`MinGW` :code:`Makefiles` option they are located in the :code:`build/out/` directory.  Run them to check that the compilation was correct.
+
+
+Once the sources are built, the generated static :code:`build/out/libosqp.a` and shared :code:`build/out/libosqp.ext` libraries can be used to interface any C/C++ software to OSQP (see :ref:`install_osqp_libs` installation).
+
+.. _install_the_binaries:
+
+Install the binaries
+--------------------
+
+
+
+To install the generated libraries and headers to a system-wide location compatible with `GNU standards <http://www.gnu.org/prep/standards/html_node/Directory-Variables.html>`_ it is just necessary to run
+
+.. code:: bash
+
+   cmake --build . --target install
+
+This code installs the libraries in :code:`libdir` and the headers into :code:`includedir/osqp`. For mode details see the defaults folders on the `GNU standards <http://www.gnu.org/prep/standards/html_node/Directory-Variables.html>`_ website.
+To change the installation prefix, in the "Create Makefiles" step above, you need to specify the destination folder as :code:`cmake -DCMAKE_INSTALL_PREFIX:PATH=myfolder ..`.
+
+.. note:: This step requires write permissions in the destination
+	  folders. You might be able to gain access using the
+	  :code:`sudo` command.
+
+We provided also an uninstall routine to remove the copied files by running
+
+.. code:: bash
+
+   cmake --build . --target uninstall
+
+Note that this corresponds to running :code:`make install` and :code:`make uninstall` on unix machines.
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..ce9bb01
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,110 @@
+OSQP solver documentation
+==========================
+**Join our** `forum <https://osqp.discourse.group/>`_ **for any
+questions related to the solver!**
+
+The OSQP (Operator Splitting Quadratic Program) solver is a numerical
+optimization package for solving convex quadratic programs in the form
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} x^T P x + q^T x \\
+    \mbox{subject to} & l \leq A x \leq u
+  \end{array}
+
+where :math:`x` is the optimization variable and
+:math:`P \in \mathbf{S}^{n}_{+}` a positive semidefinite matrix.
+
+**Code available on** `GitHub <https://github.com/osqp/osqp>`_.
+
+.. rubric:: Citing OSQP
+
+If you are using OSQP for your work, we encourage you to
+
+* :ref:`Cite the related papers <citing>`
+* Put a star on GitHub |github-star|
+
+
+.. |github-star| image:: https://img.shields.io/github/stars/osqp/osqp.svg?style=social&label=Star
+  :target: https://github.com/osqp/osqp
+
+
+**We are looking forward to hearing your success stories with OSQP!** Please `share them with us <bartolomeo.stellato@gmail.com>`_.
+
+.. rubric:: Features
+
+
+.. glossary::
+
+    Efficient
+        It uses a custom ADMM-based first-order method requiring only a single matrix factorization in the setup phase. All the other operations are extremely cheap. It also implements custom sparse linear algebra routines exploiting structures in the problem data.
+
+    Robust
+        The algorithm is absolutely division free after the setup and it requires no assumptions on problem data (the problem only needs to be convex). It just works!
+
+    Detects primal / dual infeasible problems
+        When the problem is primal or dual infeasible, OSQP detects it. It is the first available QP solver based on first-order methods able to do so.
+
+    Embeddable
+        It has an easy interface to generate customized embeddable C code with no memory manager required.
+
+    Library-free
+        It requires no external library to run.
+
+    Efficiently warm started
+        It can be easily warm-started and the matrix factorization can be cached to solve parametrized problems extremely efficiently.
+
+    Interfaces
+        It can be interfaced to C, C++, Fortran (soon!), Python, Julia and Matlab.
+
+
+
+.. rubric:: License
+
+OSQP is distributed under the `Apache 2.0 License <https://www.apache.org/licenses/LICENSE-2.0>`_
+
+
+
+.. rubric:: Credits
+
+The following people have been involved in the development of OSQP:
+
+* `Bartolomeo Stellato <https://stellato.io/>`_ (Princeton University): main development
+* `Goran Banjac <https://people.ee.ethz.ch/~gbanjac/>`_ (ETH Zürich): main development
+* `Nicholas Moehle <https://www.nicholasmoehle.com/>`_ (Stanford University): methods, maths, and code generation
+* `Paul Goulart <http://users.ox.ac.uk/~engs1373/>`_ (University of Oxford): methods, maths, and Matlab interface
+* `Alberto Bemporad <http://cse.lab.imtlucca.it/~bemporad/>`_ (IMT Lucca): methods and maths
+* `Stephen Boyd <https://web.stanford.edu/~boyd/>`_ (Stanford University): methods and maths
+* `Ian McInerney <mailto:i.mcinerney17@imperial.ac.uk>`_ (Imperial College London): code generation, software development
+* `Michel Schubiger <mailto:michel.schubiger@bluewin.ch>`_ (Schindler R&D): GPU implementation
+* `John Lygeros <https://control.ee.ethz.ch/people/profile.john-lygeros.html>`_ (ETH Zurich): methods and maths
+
+Interfaces development
+
+* `Nick Gould <http://www.numerical.rl.ac.uk/people/nimg/nimg.html>`_ (Rutherford Appleton Laboratory): Fortran and CUTEst interfaces
+* `Ed Barnard <eabarnard@gmail.com>`_ (University of Oxford): Rust interface
+
+
+.. rubric:: Bug reports and support
+
+Please report any issues via the `Github issue tracker <https://github.com/osqp/osqp/issues>`_. All types of issues are welcome including bug reports, documentation typos, feature requests and so on.
+
+
+.. rubric:: Numerical benchmarks
+
+Numerical benchmarks against other solvers are available `here <https://github.com/osqp/osqp_benchmarks>`_.
+
+
+.. toctree::
+   :hidden:
+   :maxdepth: 2
+   :caption: User Documentation
+
+   solver/index
+   get_started/index
+   interfaces/index
+   parsers/index
+   codegen/index
+   examples/index
+   contributing/index
+   citing/index
diff --git a/docs/interfaces/C.rst b/docs/interfaces/C.rst
new file mode 100644
index 0000000..ef574fe
--- /dev/null
+++ b/docs/interfaces/C.rst
@@ -0,0 +1,127 @@
+.. _c_interface:
+
+C
+=====
+
+
+
+
+.. _C_main_API:
+
+Main solver API
+---------------
+
+The main C API is imported from the header :code:`osqp.h` and provides the following functions
+
+
+.. doxygenfunction:: osqp_setup
+
+.. doxygenfunction:: osqp_solve
+
+.. doxygenfunction:: osqp_cleanup
+
+
+.. _C_sublevel_API:
+
+Sublevel API
+------------
+Sublevel C API is also imported from the header :code:`osqp.h` and provides the following functions
+
+Warm start
+^^^^^^^^^^
+OSQP automatically warm starts primal and dual variables from the previous QP solution. If you would like to warm start their values manually, you can use
+
+.. doxygenfunction:: osqp_warm_start
+
+.. doxygenfunction:: osqp_warm_start_x
+
+.. doxygenfunction:: osqp_warm_start_y
+
+
+.. _c_cpp_update_data :
+
+Update problem data
+^^^^^^^^^^^^^^^^^^^
+Problem data can be updated without executing the setup again using the following functions.
+
+.. doxygenfunction:: osqp_update_lin_cost
+
+.. doxygenfunction:: osqp_update_lower_bound
+
+.. doxygenfunction:: osqp_update_upper_bound
+
+.. doxygenfunction:: osqp_update_bounds
+
+.. doxygenfunction:: osqp_update_P
+
+.. doxygenfunction:: osqp_update_A
+
+.. doxygenfunction:: osqp_update_P_A
+
+
+
+.. _c_cpp_data_types :
+
+Data types
+----------
+
+The most basic used datatypes are
+
+* :code:`c_int`: can be :code:`long` or :code:`int` if the compiler flag :code:`DLONG` is set or not
+* :code:`c_float`: can be a :code:`float` or a :code:`double` if the compiler flag :code:`DFLOAT` is set or not.
+
+
+
+The relevant structures used in the API are
+
+Data
+^^^^
+
+.. doxygenstruct:: OSQPData
+   :members:
+
+The matrices are defined in `Compressed Sparse Column (CSC) format <https://people.sc.fsu.edu/~jburkardt/data/cc/cc.html>`_ using zero-based indexing.
+
+.. doxygenstruct:: csc
+   :members:
+
+Settings
+^^^^^^^^
+
+.. doxygenstruct:: OSQPSettings
+  :members:
+
+Solution
+^^^^^^^^
+
+.. doxygenstruct:: OSQPSolution
+   :members:
+
+Info
+^^^^^
+
+.. doxygenstruct:: OSQPInfo
+   :members:
+
+Workspace
+^^^^^^^^^
+
+.. doxygenstruct:: OSQPWorkspace
+   :members:
+
+
+Scaling
+^^^^^^^
+
+.. doxygenstruct:: OSQPScaling
+   :members:
+
+Polish
+^^^^^^
+.. doxygenstruct:: OSQPPolish
+  :members:
+
+
+
+.. TODO: Add sublevel API
+.. TODO: Add using your own linear system solver
diff --git a/docs/interfaces/cutest.rst b/docs/interfaces/cutest.rst
new file mode 100644
index 0000000..3b6ce63
--- /dev/null
+++ b/docs/interfaces/cutest.rst
@@ -0,0 +1,22 @@
+.. _cutest_interface:
+
+CUTEst
+======
+
+The command to solve a problem in SIF format contained in the file
+probname.SIF is
+
+.. code::
+
+    runcutest -p osqp -D probname.SIF
+
+See the man page for runcutest for more details or other options.
+
+The default OSQP settings used in CUTEst appear in the `OSQP.SPC <https://github.com/ralna/CUTEst/blob/master/src/osqp/OSQP.SPC>`_ file. 
+Optionally, new parameter values to overwrite the default values can be stored in a file :code:`OSQP.SPC` in the directory where the :code:`runcutest` command is executed.
+The format of the file :code:`OSQP.SPC` is the parameter name starting in the first column followed by one or more spaces and then the parameter value. 
+The parameter names are case sensitive. 
+If the parameter value is :code:`true` or :code:`false`, then use :code:`1` for true and :code:`0` for :code:`false`.
+
+For more details see the `README.osqp <https://github.com/ralna/CUTEst/blob/master/src/osqp/README.osqp>`_ file in the CUTEst repository.
+
diff --git a/docs/interfaces/eigen_google.rst b/docs/interfaces/eigen_google.rst
new file mode 100644
index 0000000..73f466d
--- /dev/null
+++ b/docs/interfaces/eigen_google.rst
@@ -0,0 +1,6 @@
+.. _eigen_google:
+
+C++ Eigen interface (osqp-cpp)
+================================
+
+The Eigen interface developed at Google is documented `here <https://github.com/google/osqp-cpp>`_.
diff --git a/docs/interfaces/eigen_robotology.rst b/docs/interfaces/eigen_robotology.rst
new file mode 100644
index 0000000..876f201
--- /dev/null
+++ b/docs/interfaces/eigen_robotology.rst
@@ -0,0 +1,6 @@
+.. _eigen_robotology:
+
+C++ Eigen interface (osqp-eigen)
+================================
+
+The Eigen interface by Robotology group is documented `here <https://robotology.github.io/osqp-eigen/doxygen/doc/html/index.html>`_.
diff --git a/docs/interfaces/fortran.rst b/docs/interfaces/fortran.rst
new file mode 100644
index 0000000..7d2c465
--- /dev/null
+++ b/docs/interfaces/fortran.rst
@@ -0,0 +1,8 @@
+.. _fortran_interface:
+
+
+Fortran
+========
+
+The interface is still experimental.
+You can find a demo of how it works in the `osqp/osqp-fortran <https://github.com/osqp/osqp-fortran/blob/master/demo/osqp_demo_fortran.F90>`_ repository.
diff --git a/docs/interfaces/index.rst b/docs/interfaces/index.rst
new file mode 100644
index 0000000..800d422
--- /dev/null
+++ b/docs/interfaces/index.rst
@@ -0,0 +1,104 @@
+Interfaces
+============
+
+OSQP has several interfaces. The information about settings, status values and how to assign different linear system solvers appear in the following links
+
+* :ref:`Solver settings <solver_settings>`
+* :ref:`Linear system solvers <linear_system_solvers_setting>`
+* :ref:`Status values <status_values>`
+
+
+
+.. toctree::
+   :maxdepth: 1
+   :glob:
+   :hidden:
+
+   solver_settings.rst
+   linear_systems_solvers.rst
+   status_values.rst
+
+
+Official
+----------
+
++------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| Language                           | Maintainers                                              | Repository                                                                               |
++====================================+==========================================================+==========================================================================================+
+| :ref:`C <c_interface>`             | | `Bartolomeo Stellato <bartolomeo.stellato@gmail.com>`_ | `github.com/osqp/osqp <https://github.com/osqp/osqp>`_                                   |
+|                                    | | `Goran Banjac <gbanjac@control.ee.ethz.ch>`_           |                                                                                          |
+|                                    | | `Paul Goulart <paul.goulart@eng.ox.ac.uk>`_            |                                                                                          |
++------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| :ref:`Python <python_interface>`   | | `Bartolomeo Stellato <bartolomeo.stellato@gmail.com>`_ | `github.com/osqp/osqp-python <https://github.com/osqp/osqp-python>`_                     |
+|                                    | | `Goran Banjac <gbanjac@control.ee.ethz.ch>`_           |                                                                                          |
++------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| :ref:`Matlab <matlab_interface>`   | | `Bartolomeo Stellato <bartolomeo.stellato@gmail.com>`_ | `github.com/osqp/osqp-matlab <https://github.com/osqp/osqp-matlab>`_                     |
+|                                    | | `Goran Banjac <gbanjac@control.ee.ethz.ch>`_           |                                                                                          |
+|                                    | | `Paul Goulart <paul.goulart@eng.ox.ac.uk>`_            |                                                                                          |
++------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| :ref:`Julia <julia_interface>`     | | `Twan Koolen <tkoolen@mit.edu>`_                       | `github.com/osqp/OSQP.jl <https://github.com/osqp/OSQP.jl>`_                             |
+|                                    | | `Benoît Legat <benoit.legat@uclouvain.be>`_            |                                                                                          |
+|                                    | | `Bartolomeo Stellato <bartolomeo.stellato@gmail.com>`_ |                                                                                          |
++------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| :ref:`R <rlang_interface>`         | | `Bartolomeo Stellato <bartolomeo.stellato@gmail.com>`_ | `github.com/osqp/osqp-r <https://github.com/osqp/osqp-r>`_                               |
+|                                    | | `Paul Goulart <paul.goulart@eng.ox.ac.uk>`_            |                                                                                          |
++------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+
+
+
+.. toctree::
+   :maxdepth: 1
+   :glob:
+   :hidden:
+
+   C.rst
+   python.rst
+   matlab.rst
+   julia.rst
+   rlang.rst
+
+
+
+
+
+
+
+
+
+Community Maintained
+--------------------
+
+
++------------------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| Language                                       | Maintainers                                              | Repository                                                                               |
++================================================+==========================================================+==========================================================================================+
+| :ref:`C++/Eigen Google <eigen_google>`         | | `Miles Lubin <miles.lubin@gmail.com>`_                 | `github.com/google/osqp-cpp <https://github.com/google/osqp-cpp>`_                       |
++------------------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| :ref:`C++/Eigen Robotology <eigen_robotology>` | | `Giulio Romualdi <giulio.romualdi@gmail.com>`_         | `github.com/robotology/osqp-eigen <https://github.com/robotology/osqp-eigen>`_           |
++------------------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| :ref:`Rust <rust_interface>`                   | | `Ed Barnard <eabarnard@gmail.com>`_                    | `github.com/osqp/osqp.rs <https://github.com/osqp/osqp.rs>`_                             |
++------------------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| :ref:`Ruby <ruby_interface>`                   | | `Andrew Kane <andrew@chartkick.com>`_                  | `https://github.com/ankane/osqp <https://github.com/ankane/osqp>`_                       |
++------------------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| :ref:`Fortran <fortran_interface>`             | | `Nick Gould <nick.gould@stfc.ac.uk>`_                  | `github.com/osqp/osqp-fortran <https://github.com/osqp/osqp-fortran>`_                   |
+|                                                | | `Bartolomeo Stellato <bartolomeo.stellato@gmail.com>`_ |                                                                                          |
+|                                                | | `Paul Goulart <paul.goulart@eng.ox.ac.uk>`_            |                                                                                          |
++------------------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+| :ref:`Cutest <cutest_interface>`               | | `Nick Gould <nick.gould@stfc.ac.uk>`_                  | `github.com/ralna/CUTEst <https://github.com/ralna/CUTEst/tree/master/src/osqp>`_        |
++------------------------------------------------+----------------------------------------------------------+------------------------------------------------------------------------------------------+
+
+
+
+.. toctree::
+   :maxdepth: 1
+   :glob:
+   :hidden:
+
+   eigen_google.rst
+   eigen_robotology.rst
+   rust.rst
+   ruby.rst
+   fortran.rst
+   cutest.rst
+
+
diff --git a/docs/interfaces/julia.rst b/docs/interfaces/julia.rst
new file mode 100644
index 0000000..37d08e1
--- /dev/null
+++ b/docs/interfaces/julia.rst
@@ -0,0 +1,167 @@
+.. _julia_interface:
+
+Julia
+======
+
+Load the module
+---------------
+The OSQP module can be load with
+
+.. code:: julia
+
+    using OSQP
+
+
+.. _julia_setup:
+
+Setup
+-----
+
+The solver is initialized by creating an OSQP Model
+
+.. code:: julia
+
+    m = OSQP.Model()
+
+The problem is specified in the setup phase by running
+
+.. code:: julia
+
+    OSQP.setup!(m; P=P, q=q, A=A, l=l, u=u, settings...)
+
+
+The arguments :code:`q`, :code:`l` and :code:`u` are :code:`Vector{Float64}`. 
+The elements of :code:`l` and :code:`u` can be :math:`\pm \infty` ( using :code:`Inf`).
+
+The arguments :code:`P` and :code:`A` are sparse matrices of type :code:`SparseMatrixCSC`. 
+Matrix :code:`P` can be either complete or just the upper triangular
+part. OSQP will make use of only the upper triangular part.
+If they are sparse matrices are in another format, the interface will attempt to convert them. 
+There is no need to specify all the arguments. 
+
+The argument :code:`settings` specifies the solver settings. 
+Settings can also be passed as indipendent keyword arguments such as :code:`max_iter=1000`.
+The allowed parameters are defined in :ref:`solver_settings`.
+
+Solve
+-----
+
+The problem can be solved by
+
+.. code:: julia
+
+    results = OSQP.solve!(m)
+
+
+The output :code:`results` contains the primal solution :code:`x`, the dual solution :code:`y`, certificate of primal infeasibility :code:`prim_inf_cert`, certificate of dual infeasibility :code:`dual_inf_cert` and the :code:`info` object containing the solver statistics defined in the following table
+
+
++-----------------------+------------------------------------------------+
+| Member                | Description                                    |
++=======================+================================================+
+| :code:`iter`          | Number of iterations                           |
++-----------------------+------------------------------------------------+
+| :code:`status`        | Solver status                                  |
++-----------------------+------------------------------------------------+
+| :code:`status_val`    | Solver status value as in :ref:`status_values` |
++-----------------------+------------------------------------------------+
+| :code:`status_polish` | Polishing status                               |
++-----------------------+------------------------------------------------+
+| :code:`obj_val`       | Objective value                                |
++-----------------------+------------------------------------------------+
+| :code:`pri_res`       | Primal residual                                |
++-----------------------+------------------------------------------------+
+| :code:`dua_res`       | Dual residual                                  |
++-----------------------+------------------------------------------------+
+| :code:`setup_time`    | Setup time                                     |
++-----------------------+------------------------------------------------+
+| :code:`solve_time`    | Solve time                                     |
++-----------------------+------------------------------------------------+
+| :code:`update_time`   | Update time                                    |
++-----------------------+------------------------------------------------+
+| :code:`polish_time`   | Polish time                                    |
++-----------------------+------------------------------------------------+
+| :code:`run_time`      | Total run time: setup/update + solve + polish  |
++-----------------------+------------------------------------------------+
+| :code:`rho_estimate`  | Optimal rho estimate                           |
++-----------------------+------------------------------------------------+
+| :code:`rho_updates`   | Number of rho updates                          |
++-----------------------+------------------------------------------------+
+
+Note that if multiple solves are executed from single setup, then after the
+first one :code:`run_time` includes :code:`update_time` + :code:`solve_time`
++ :code:`polish_time`.
+
+
+Update
+------
+Part of problem data and settings can be updated without requiring a new problem setup.
+
+Update problem vectors
+^^^^^^^^^^^^^^^^^^^^^^
+Vectors :code:`q`, :code:`l` and :code:`u` can be updated with new values :code:`q_new`, :code:`l_new` and :code:`u_new` by just running
+
+.. code:: julia
+
+    OSQP.update!(m; q=q_new, l=l_new, u=u_new)
+
+
+The user does not have to specify all the keyword arguments.
+
+
+Update problem matrices
+^^^^^^^^^^^^^^^^^^^^^^^^
+Matrices :code:`A` and :code:`P` can be updated by changing the value of their elements but not their sparsity pattern. The interface is designed to mimic the :ref:`C/C++ counterpart <c_cpp_update_data>` with the Julia 1-based indexing. Note that the new values of :code:`P` represent only the upper triangular part while :code:`A` is always represented as a full matrix.
+
+You can update the values of all the elements of :code:`P` by executing
+
+.. code:: julia
+
+    OSQP.update!(m, Px=Px_new)
+
+
+If you want to update only some elements, you can pass
+
+.. code:: julia
+
+    OSQP.update!(m, Px=Px_new, Px_idx=Px_new_idx)
+
+where :code:`Px_new_idx` is the vector of indices of mapping the elements of :code:`Px_new` to the original vector :code:`Px` representing the data of the sparse matrix :code:`P`.
+
+Matrix :code:`A` can be changed in the same way. You can also change both matrices at the same time by running, for example
+
+
+.. code:: julia
+
+    OSQP.update!(m, Px=Px_new, Px_idx=Px_new_idx, Ax=Ax_new, Ax=Ax_new_idx)
+
+
+
+
+
+.. _julia_update_settings:
+
+Update settings
+^^^^^^^^^^^^^^^
+
+Settings can be updated by running
+
+.. code:: julia
+
+    OSQP.update_settings!(m; new_settings)
+
+
+where :code:`new_settings` are the new settings specified as keyword arguments that can be updated which are marked with an * in :ref:`solver_settings`.
+
+
+Warm start
+----------
+
+OSQP automatically warm starts primal and dual variables from the previous QP solution. If you would like to warm start their values manually, you can use
+
+.. code:: julia
+
+    OSQP.warm_start!(m; x=x0, y=y0)
+
+
+where :code:`x0` and :code:`y0` are the new primal and dual variables. 
diff --git a/docs/interfaces/linear_systems_solvers.rst b/docs/interfaces/linear_systems_solvers.rst
new file mode 100644
index 0000000..1aa8475
--- /dev/null
+++ b/docs/interfaces/linear_systems_solvers.rst
@@ -0,0 +1,22 @@
+.. _linear_system_solvers_setting :
+
+Linear Systems Solvers
+-----------------------
+The settings parameter :code:`linsys_solver` defines the solver for the linear system.
+In C it corresponds to an integer :code:`c_int` (see :ref:`c_cpp_data_types`) and in the other high level languages to a string.
+
+
++-----------------+-------------------+--------------------------------+---------------+
+| Solver          | String option     | C     Constant                 | Integer value |
++=================+===================+================================+===============+
+| QDLDL           | "qdldl"           | :code:`QDLDL_SOLVER`           | :code:`0`     |
++-----------------+-------------------+--------------------------------+---------------+
+| MKL Pardiso     | "mkl pardiso"     | :code:`MKL_PARDISO_SOLVER`     | :code:`1`     |
++-----------------+-------------------+--------------------------------+---------------+
+
+
+
+To add new linear system solvers see :ref:`interfacing_new_linear_system_solvers`.
+
+
+
diff --git a/docs/interfaces/matlab.rst b/docs/interfaces/matlab.rst
new file mode 100644
index 0000000..81e7027
--- /dev/null
+++ b/docs/interfaces/matlab.rst
@@ -0,0 +1,168 @@
+.. _matlab_interface:
+
+Matlab
+======
+
+.. _matlab_setup:
+
+Setup
+-----
+The solver is initialized by creating an OSQP object
+
+.. code:: matlab
+
+    m = osqp;
+
+The problem is specified in the setup phase by running
+
+.. code:: matlab
+
+    m.setup(P, q, A, l, u, varargin)
+
+
+The arguments :code:`q`, :code:`l` and :code:`u` are arrays. The elements of :code:`l` and :code:`u` can be :math:`\pm \infty` ( using :code:`Inf`). The arguments :code:`P` and :code:`A` are sparse matrices.
+Matrix :code:`P` can be either complete or just the upper triangular
+part. OSQP will make use of only the upper triangular part.
+
+There is no need to specify all the problem data. They can be omitted by writing :code:`[]`.
+
+The last argument :code:`varargin` specifies the solver options. You can pass the options in two ways. You can either set the individual parameters as field-value pairs, e.g.,
+
+.. code:: matlab
+
+    m.setup(P, q, A, l, u, 'eps_abs', 1e-04, 'eps_rel', 1e-04);
+
+
+Alternatively, you can create a structure containing all the settings, change some of the fields and then pass it as the last argument
+
+.. code:: matlab
+
+    settings = m.default_settings();
+    settings.eps_abs = 1e-04;
+    settings.eps_rel = 1e-04;
+    m.setup(P, q, A, l, u, settings);
+
+The allowed settings are defined in :ref:`solver_settings`.
+
+
+Solve
+-----
+
+The problem can be solved by
+
+.. code:: matlab
+
+   results = m.solve();
+
+The :code:`results` structure contains the primal solution :code:`x`, the dual solution :code:`y`, certificate of primal infeasibility :code:`prim_inf_cert`, certificate of dual infeasibility :code:`dual_inf_cert` and the :code:`info` structure containing the solver statistics defined in the following table
+
+
++-----------------------+------------------------------------------------+
+| Member                | Description                                    |
++=======================+================================================+
+| :code:`iter`          | Number of iterations                           |
++-----------------------+------------------------------------------------+
+| :code:`status`        | Solver status                                  |
++-----------------------+------------------------------------------------+
+| :code:`status_val`    | Solver status value as in :ref:`status_values` |
++-----------------------+------------------------------------------------+
+| :code:`status_polish` | Polishing status                               |
++-----------------------+------------------------------------------------+
+| :code:`obj_val`       | Objective value                                |
++-----------------------+------------------------------------------------+
+| :code:`pri_res`       | Primal residual                                |
++-----------------------+------------------------------------------------+
+| :code:`dua_res`       | Dual residual                                  |
++-----------------------+------------------------------------------------+
+| :code:`setup_time`    | Setup time                                     |
++-----------------------+------------------------------------------------+
+| :code:`solve_time`    | Solve time                                     |
++-----------------------+------------------------------------------------+
+| :code:`update_time`   | Update time                                    |
++-----------------------+------------------------------------------------+
+| :code:`polish_time`   | Polish time                                    |
++-----------------------+------------------------------------------------+
+| :code:`run_time`      | Total run time: setup/update + solve + polish  |
++-----------------------+------------------------------------------------+
+| :code:`rho_estimate`  | Optimal rho estimate                           |
++-----------------------+------------------------------------------------+
+| :code:`rho_updates`   | Number of rho updates                          |
++-----------------------+------------------------------------------------+
+
+Note that if multiple solves are executed from single setup, then after the
+first one :code:`run_time` includes :code:`update_time` + :code:`solve_time`
++ :code:`polish_time`.
+
+
+Update
+------
+Part of problem data and settings can be updated without requiring a new problem setup.
+
+
+
+Update problem vectors
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Vectors :code:`q`, :code:`l` and :code:`u` can be updated with new values :code:`q_new`, :code:`l_new` and :code:`u_new` by just running
+
+.. code:: python
+
+    m.update('q', q_new, 'l', l_new, 'u', u_new);
+
+
+The user does not have to specify all the arguments.
+
+
+Update problem matrices
+^^^^^^^^^^^^^^^^^^^^^^^^
+Matrices :code:`A` and :code:`P` can be updated by changing the value of their elements but not their sparsity pattern.
+The interface is designed to mimic the :ref:`C counterpart <c_cpp_update_data>` with the Matlab 1-based indexing.
+Note that the new values of :code:`P` represent only the upper triangular part while :code:`A` is always represented as a full matrix.
+
+You can update the values of all the elements of :code:`P` by executing
+
+.. code:: matlab
+
+    m.update('Px', Px_new)
+
+
+If you want to update only some elements, you can pass
+
+.. code:: matlab
+
+    m.update('Px', Px_new, 'Px_idx', Px_new_idx)
+
+where :code:`Px_new_idx` is the vector of indices of mapping the elements of :code:`Px_new` to the original vector :code:`Px` representing the data of the sparse matrix :code:`P`.
+
+Matrix :code:`A` can be changed in the same way. You can also change both matrices at the same time by running, for example
+
+
+.. code:: matlab
+
+    m.update('Px', Px_new, 'Px_idx', Px_new_idx, 'Ax' Ax_new, 'Ax', Ax_new_idx)
+
+
+Update settings
+^^^^^^^^^^^^^^^
+
+Settings can be updated by running
+
+.. code:: python
+
+    m.update_settings(varargin);
+
+
+where :code:`varargin` argument is described in :ref:`matlab_setup`. The allowed settings that can be updated are marked with an * in :ref:`solver_settings`.
+
+
+
+
+Warm start
+----------
+OSQP automatically warm starts primal and dual variables from the previous QP solution. If you would like to warm start their values manually, you can use
+
+.. code:: matlab
+
+    m.warm_start('x', x0, 'y', y0)
+
+where :code:`x0` and :code:`y0` are the new primal and dual variables.
diff --git a/docs/interfaces/python.rst b/docs/interfaces/python.rst
new file mode 100644
index 0000000..86b228e
--- /dev/null
+++ b/docs/interfaces/python.rst
@@ -0,0 +1,175 @@
+.. _python_interface:
+
+Python
+======
+
+Import
+------
+The OSQP module can be imported with
+
+.. code:: python
+
+    import osqp
+
+
+.. _python_setup:
+
+Setup
+-----
+
+The solver is initialized by creating an OSQP object
+
+.. code:: python
+
+    m = osqp.OSQP()
+
+The problem is specified in the setup phase by running
+
+.. code:: python
+
+    m.setup(P=P, q=q, A=A, l=l, u=u, **settings)
+
+
+The arguments :code:`q`, :code:`l` and :code:`u` are numpy arrays. The elements of :code:`l` and :code:`u` can be :math:`\pm \infty` ( using :code:`numpy.inf`).
+
+The arguments :code:`P` and :code:`A` are scipy sparse matrices in CSC format.
+Matrix :code:`P` can be either complete or just the upper triangular
+part. OSQP will make use of only the upper triangular part.
+If they are sparse matrices are in another format, the interface will attempt to convert them. There is no need to specify all the arguments.
+
+
+The keyword arguments :code:`**settings` specify the solver settings. The allowed parameters are defined in :ref:`solver_settings`.
+
+Solve
+-----
+
+The problem can be solved by
+
+.. code:: python
+
+   results = m.solve()
+
+
+The :code:`results` object contains the primal solution :code:`x`, the dual solution :code:`y`, certificate of primal infeasibility :code:`prim_inf_cert`, certificate of dual infeasibility :code:`dual_inf_cert` and the :code:`info` object containing the solver statistics defined in the following table
+
+
++-----------------------+------------------------------------------------+
+| Member                | Description                                    |
++=======================+================================================+
+| :code:`iter`          | Number of iterations                           |
++-----------------------+------------------------------------------------+
+| :code:`status`        | Solver status                                  |
++-----------------------+------------------------------------------------+
+| :code:`status_val`    | Solver status value as in :ref:`status_values` |
++-----------------------+------------------------------------------------+
+| :code:`status_polish` | Polishing status                               |
++-----------------------+------------------------------------------------+
+| :code:`obj_val`       | Objective value                                |
++-----------------------+------------------------------------------------+
+| :code:`pri_res`       | Primal residual                                |
++-----------------------+------------------------------------------------+
+| :code:`dua_res`       | Dual residual                                  |
++-----------------------+------------------------------------------------+
+| :code:`setup_time`    | Setup time                                     |
++-----------------------+------------------------------------------------+
+| :code:`solve_time`    | Solve time                                     |
++-----------------------+------------------------------------------------+
+| :code:`update_time`   | Update time                                    |
++-----------------------+------------------------------------------------+
+| :code:`polish_time`   | Polish time                                    |
++-----------------------+------------------------------------------------+
+| :code:`run_time`      | Total run time: setup/update + solve + polish  |
++-----------------------+------------------------------------------------+
+| :code:`rho_estimate`  | Optimal rho estimate                           |
++-----------------------+------------------------------------------------+
+| :code:`rho_updates`   | Number of rho updates                          |
++-----------------------+------------------------------------------------+
+
+Note that if multiple solves are executed from single setup, then after the
+first one :code:`run_time` includes :code:`update_time` + :code:`solve_time`
++ :code:`polish_time`.
+
+
+Solve in just one function (with GIL disabled)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+We have a dedicated solve function that performs :code:`setup` and :code:`solve` operations for you. It also disables the GIL in case you
+need it. Just run it from the main module without creating the object as follows
+
+
+.. code:: python
+
+    results = osqp.solve(P=P, q=q, A=A, l=l, u=u, **settings)
+
+
+Update
+------
+Part of problem data and settings can be updated without requiring a new problem setup.
+
+Update problem vectors
+^^^^^^^^^^^^^^^^^^^^^^
+Vectors :code:`q`, :code:`l` and :code:`u` can be updated with new values :code:`q_new`, :code:`l_new` and :code:`u_new` by just running
+
+.. code:: python
+
+    m.update(q=q_new, l=l_new, u=u_new)
+
+
+The user does not have to specify all the keyword arguments.
+
+
+.. _python_update_settings:
+
+Update problem matrices
+^^^^^^^^^^^^^^^^^^^^^^^^
+Matrices :code:`A` and :code:`P` can be updated by changing the value of their elements but not their sparsity pattern.
+The interface is designed to mimic the :ref:`C counterpart <c_cpp_update_data>`.
+Note that the new values of :code:`P` represent only the upper triangular part while :code:`A` is always represented as a full matrix.
+
+You can update the values of all the elements of :code:`P` by executing
+
+.. code:: python
+
+    m.update(Px=Px_new)
+
+
+If you want to update only some elements, you can pass
+
+.. code:: python
+
+    m.update(Px=Px_new, Px_idx=Px_new_idx)
+
+where :code:`Px_new_idx` is the vector of indices of mapping the elements of :code:`Px_new` to the original vector :code:`Px` representing the data of the sparse matrix :code:`P`.
+
+Matrix :code:`A` can be changed in the same way. You can also change both matrices at the same time by running, for example
+
+
+.. code:: python
+
+    m.update(Px=Px_new, Px_idx=Px_new_idx, Ax=Ax_new, Ax=Ax_new_idx)
+
+
+Update settings
+^^^^^^^^^^^^^^^
+
+Settings can be updated by running
+
+.. code:: python
+
+    m.update_settings(**kwargs)
+
+
+where :code:`kwargs` are the settings that can be updated which are marked with an * in :ref:`solver_settings`.
+
+
+Warm start
+----------
+
+OSQP automatically warm starts primal and dual variables from the previous QP solution. If you would like to warm start their values manually, you can use
+
+.. code:: python
+
+    m.warm_start(x=x0, y=y0)
+
+
+where :code:`x0` and :code:`y0` are the new primal and dual variables.
diff --git a/docs/interfaces/rlang.rst b/docs/interfaces/rlang.rst
new file mode 100644
index 0000000..50ddf74
--- /dev/null
+++ b/docs/interfaces/rlang.rst
@@ -0,0 +1,7 @@
+.. _rlang_interface:
+
+
+R
+==
+
+The R interface is officially on CRAN and documented `here <https://cran.r-project.org/web/packages/osqp/>`_.
diff --git a/docs/interfaces/ruby.rst b/docs/interfaces/ruby.rst
new file mode 100644
index 0000000..195cf19
--- /dev/null
+++ b/docs/interfaces/ruby.rst
@@ -0,0 +1,7 @@
+.. _ruby_interface:
+
+
+Ruby
+========
+
+The ruby interface can be installed as a Ruby gem and is documented `here <https://www.rubydoc.info/gems/osqp/>`_.
diff --git a/docs/interfaces/rust.rst b/docs/interfaces/rust.rst
new file mode 100644
index 0000000..52956a3
--- /dev/null
+++ b/docs/interfaces/rust.rst
@@ -0,0 +1,6 @@
+.. _rust_interface:
+
+Rust
+======
+
+The Rust interface is documented `here <https://docs.rs/osqp/>`_.
diff --git a/docs/interfaces/solver_settings.rst b/docs/interfaces/solver_settings.rst
new file mode 100644
index 0000000..ba3abc8
--- /dev/null
+++ b/docs/interfaces/solver_settings.rst
@@ -0,0 +1,73 @@
+.. _solver_settings :
+
+Solver settings
+---------------
+
+The solver settings are displayed in the following table. The settings marked with * can be changed without running the setup method again.
+
+.. tabularcolumns:: |p{4.5cm}|p{3.5cm}|p{6.5cm}|L|
+
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| Argument                       | Description                                                 | Allowed values                                               | Default value   |
++================================+=============================================================+==============================================================+=================+
+| :code:`rho` *                  | ADMM rho step                                               | 0 < :code:`rho`                                              | 0.1             |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`sigma`                  | ADMM sigma step                                             | 0 < :code:`sigma`                                            | 1e-06           |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`max_iter` *             | Maximum number of iterations                                | 0 < :code:`max_iter` (integer)                               | 4000            |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`eps_abs` *              | Absolute tolerance                                          | 0 <= :code:`eps_abs`                                         | 1e-03           |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`eps_rel` *              | Relative tolerance                                          | 0 <= :code:`eps_rel`                                         | 1e-03           |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`eps_prim_inf` *         | Primal infeasibility tolerance                              | 0 <= :code:`eps_prim_inf`                                    | 1e-04           |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`eps_dual_inf` *         | Dual infeasibility tolerance                                | 0 <= :code:`eps_dual_inf`                                    | 1e-04           |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`alpha` *                | ADMM overrelaxation parameter                               | 0 < :code:`alpha` < 2                                        | 1.6             |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`linsys_solver`          | Linear systems solver type                                  | See :ref:`linear_system_solvers_setting`                     | qdldl           |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`delta` *                | Polishing regularization parameter                          | 0 < :code:`delta`                                            | 1e-06           |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`polish` *               | Perform polishing                                           | True/False                                                   | False           |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`polish_refine_iter` *   | Refinement iterations in polish                             | 0 < :code:`polish_refine_iter` (integer)                     | 3               |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`verbose` *              | Print output                                                | True/False                                                   | True            |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`scaled_termination` *   | Scaled termination conditions                               | True/False                                                   | False           |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`check_termination` *    | Check termination interval                                  | 0 (disabled) or 0 < :code:`check_termination` (integer)      | 25              |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`warm_start` *           | Perform warm starting                                       | True/False                                                   | True            |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`scaling`                | Number of scaling iterations                                | 0 (disabled) or 0 < :code:`scaling` (integer)                | 10              |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`adaptive_rho`           | Adaptive rho                                                | True/False                                                   | True            |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`adaptive_rho_interval`  | Adaptive rho interval                                       | 0 (automatic) or 0 < :code:`adaptive_rho_interval` (integer) | 0               |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`adaptive_rho_tolerance` | Tolerance for adapting rho                                  | 1 <= :code:`adaptive_rho_tolerance`                          | 5               |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`adaptive_rho_fraction`  | Adaptive rho interval as fraction of setup time (auto mode) | 0 < :code:`adaptive_rho_fraction`                            | 0.4             |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+| :code:`time_limit` *           | Run time limit in seconds                                   | 0 (disabled) or 0 <= :code:`time_limit`                      | 0               |
++--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
+
+The boolean values :code:`True/False` are defined as :code:`1/0` in the C interfaces.
+
+
+.. The infinity values correspond to:
+..
+.. +----------+--------------------+
+.. | Language | Value              |
+.. +==========+====================+
+.. | C        | :code:`OSQP_INFTY` |
+.. +----------+--------------------+
+.. | Python   | :code:`numpy.inf`  |
+.. +----------+--------------------+
+.. | Matlab   | :code:`Inf`        |
+.. +----------+--------------------+
+.. | Julia    | :code:`Inf`        |
+.. +----------+--------------------+
diff --git a/docs/interfaces/status_values.rst b/docs/interfaces/status_values.rst
new file mode 100644
index 0000000..3dd53fa
--- /dev/null
+++ b/docs/interfaces/status_values.rst
@@ -0,0 +1,85 @@
+.. _status_values :
+
+
+
+Status values and errors
+========================
+
+Status values
+-------------
+
+These are the exit statuses, their respective constants and values returned by the solver as defined in `constants.h <https://github.com/osqp/osqp/blob/master/include/constants.h>`_.
+The *inaccurate* statuses define when the optimality, primal infeasibility or dual infeasibility conditions are satisfied with tolerances 10 times larger than the ones set.
+
++------------------------------+-----------------------------------+-------+
+| Status                       | Constant                          | Value |
++==============================+===================================+=======+
+| solved                       | OSQP_SOLVED                       | 1     |
++------------------------------+-----------------------------------+-------+
+| solved inaccurate            | OSQP_SOLVED_INACCURATE            | 2     |
++------------------------------+-----------------------------------+-------+
+| maximum iterations reached   | OSQP_MAX_ITER_REACHED             | -2    |
++------------------------------+-----------------------------------+-------+
+| primal infeasible            | OSQP_PRIMAL_INFEASIBLE            | -3    |
++------------------------------+-----------------------------------+-------+
+| primal infeasible inaccurate | OSQP_PRIMAL_INFEASIBLE_INACCURATE | 3     |
++------------------------------+-----------------------------------+-------+
+| dual infeasible              | OSQP_DUAL_INFEASIBLE              | -4    |
++------------------------------+-----------------------------------+-------+
+| dual infeasible inaccurate   | OSQP_DUAL_INFEASIBLE_INACCURATE   | 4     |
++------------------------------+-----------------------------------+-------+
+| interrupted by user          | OSQP_SIGINT                       | -5    |
++------------------------------+-----------------------------------+-------+
+| run time limit reached       | OSQP_TIME_LIMIT_REACHED           | -6    |
++------------------------------+-----------------------------------+-------+
+| unsolved                     | OSQP_UNSOLVED                     | -10   |
++------------------------------+-----------------------------------+-------+
+| problem non convex           | OSQP_NON_CVX                      | -7    |
++------------------------------+-----------------------------------+-------+
+
+.. note::
+
+   We recommend the user to **check the convexity of their problem before
+   passing it to OSQP**! If the user passes a non-convex problem we do not
+   assure the solver will be able to detect it.
+
+   OSQP will try to detect **non-convex** problems by checking if the residuals
+   diverge or if there are any issues in the initial factorization (if a direct
+   method is used). It will detect non-convex problems when one or more of the
+   eigenvalues of :code:`P` are "clearly" negative, i.e., when :code:`P + sigma
+   * I` is not positive semidefinite. However, it might fail to detect
+   non-convexity when :code:`P` has slightly negative eigenvalues, i.e., when
+   :code:`P + sigma * I` is positive semidefinite and :code:`P` is not.
+
+
+
+	 
+Solver Errors
+-------------
+
+OSQP can return errors during the setup and solve steps. Here is a table of the meaning
+and their constant values.
+
+
++------------------------------------------------+-----------------------------------+-------+
+| Errors                                         | Constant                          | Value |
++================================================+===================================+=======+
+| Data validation                                | OSQP_DATA_VALIDATION_ERROR        | 1     |
++------------------------------------------------+-----------------------------------+-------+
+| Settings validation                            | OSQP_SETTINGS_VALIDATION_ERROR    | 2     |
++------------------------------------------------+-----------------------------------+-------+
+| Linear system solver loading                   | OSQP_LINSYS_SOLVER_LOAD_ERROR     | 3     |
++------------------------------------------------+-----------------------------------+-------+
+| Linear system solver initialization            | OSQP_LINSYS_SOLVER_INIT_ERROR     | 4     |
++------------------------------------------------+-----------------------------------+-------+
+| Non convex problem                             | OSQP_NONCVX_ERROR                 | 5     |
++------------------------------------------------+-----------------------------------+-------+
+| Memory allocation                              | OSQP_MEM_ALLOC_ERROR              | 6     |
++------------------------------------------------+-----------------------------------+-------+
+| Workspace not initialized                      | OSQP_WORKSPACE_NOT_INIT           | 7     |
++------------------------------------------------+-----------------------------------+-------+
+
+
+
+
+
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..1d7c66c
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,36 @@
+@ECHO OFF

+

+pushd %~dp0

+

+REM Command file for Sphinx documentation

+

+if "%SPHINXBUILD%" == "" (

+	set SPHINXBUILD=sphinx-build

+)

+set SOURCEDIR=.

+set BUILDDIR=_build

+set SPHINXPROJ=osqp

+

+if "%1" == "" goto help

+

+%SPHINXBUILD% >NUL 2>NUL

+if errorlevel 9009 (

+	echo.

+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx

+	echo.installed, then set the SPHINXBUILD environment variable to point

+	echo.to the full path of the 'sphinx-build' executable. Alternatively you

+	echo.may add the Sphinx directory to PATH.

+	echo.

+	echo.If you don't have Sphinx installed, grab it from

+	echo.http://sphinx-doc.org/

+	exit /b 1

+)

+

+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%

+goto end

+

+:help

+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%

+

+:end

+popd

diff --git a/docs/parsers/cvxpy.rst b/docs/parsers/cvxpy.rst
new file mode 100644
index 0000000..7733c18
--- /dev/null
+++ b/docs/parsers/cvxpy.rst
@@ -0,0 +1,17 @@
+CVXPY
+=====
+
+`CVXPY <http://www.cvxpy.org/>`_ 1.0 supports the OSQP solver by default. After defining your problem, you can solve it with OSQP by just calling
+
+.. code:: python
+
+   problem.solve()
+
+
+OSQP is the default QP solver used. To specify it explicitly together with some options you can execute
+
+.. code:: python
+
+   problem.solve(solver=OSQP, max_iter=2000)
+
+where we set the :code:`max_iter` option to :code:`2000`.
diff --git a/docs/parsers/index.rst b/docs/parsers/index.rst
new file mode 100644
index 0000000..7420f90
--- /dev/null
+++ b/docs/parsers/index.rst
@@ -0,0 +1,10 @@
+Parsers
+=======
+
+.. toctree::
+   :maxdepth: 1
+   :glob:
+
+   yalmip.rst
+   cvxpy.rst
+   jump.rst
diff --git a/docs/parsers/jump.rst b/docs/parsers/jump.rst
new file mode 100644
index 0000000..2d8c93e
--- /dev/null
+++ b/docs/parsers/jump.rst
@@ -0,0 +1,29 @@
+JuMP
+=====
+
+`JuMP <https://github.com/JuliaOpt/JuMP.jl>`_ supports the OSQP solver using the `MathOptInterface interface <https://github.com/JuliaOpt/MathOptInterface.jl>`_. 
+You can define a JuMP model to be solved via OSQP as follows
+
+
+.. code:: julia
+
+   # Load JuMP and OSQP
+   using JuMP, OSQP
+
+   # Create OSQP Solver instance
+   s = OSQPMathProgBaseInterface.OSQPSolver(verbose=false)
+
+   # Create JuMP model
+   model = Model(solver=s)
+
+   ...
+
+Note that here we set the verbosity to :code:`false`.
+After defining your model, you can solve it by just calling
+
+.. code:: julia
+
+   solve(model)
+
+
+For more details on how to create and modify the models, see the `JuMP Documentation <https://www.juliaopt.org/JuMP.jl/stable/>`_.
\ No newline at end of file
diff --git a/docs/parsers/yalmip.rst b/docs/parsers/yalmip.rst
new file mode 100644
index 0000000..3becf57
--- /dev/null
+++ b/docs/parsers/yalmip.rst
@@ -0,0 +1,11 @@
+YALMIP
+======
+
+`YALMIP <https://yalmip.github.io/>`_ supports the OSQP solver. You can easily define problems in high-level format and then specify OSQP by simply setting
+
+.. code:: matlab
+
+   options = sdpsettings('solver', 'osqp', 'osqp.max_iter', 2000);
+
+
+where we set the :code:`max_iter` option to :code:`2000`.
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..cd6467e
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1 @@
+breathe
diff --git a/docs/solver/index.rst b/docs/solver/index.rst
new file mode 100644
index 0000000..ef4119f
--- /dev/null
+++ b/docs/solver/index.rst
@@ -0,0 +1,177 @@
+The solver
+==========
+
+Problem statement
+-----------------
+
+OSQP solves convex quadratic programs (QPs) of the form
+
+.. math::
+  \begin{array}{ll}
+    \mbox{minimize} & \frac{1}{2} x^T P x + q^T x \\
+    \mbox{subject to} & l \leq A x \leq u
+  \end{array}
+
+where :math:`x\in\mathbf{R}^{n}` is the optimization variable.
+The objective function is defined by a positive semidefinite matrix
+:math:`P \in \mathbf{S}^{n}_{+}` and vector :math:`q\in \mathbf{R}^{n}`.
+The linear constraints are defined by matrix :math:`A\in\mathbf{R}^{m \times n}`
+and vectors :math:`l` and :math:`u` so that :math:`l_i \in \mathbf{R} \cup \{-\infty\}` and :math:`u_i \in \mathbf{R} \cup \{+\infty\}` for all :math:`i \in \{1,\ldots,m\}`.
+
+
+Algorithm
+---------
+
+The solver runs the following `ADMM algorithm <http://web.stanford.edu/~boyd/papers/admm_distr_stats.html>`_ (for more details see the related papers at the :ref:`citing` section):
+
+
+.. math::
+
+   (x^{k+1}, \nu^{k+1}) & \gets \text{solve linear system}\\
+   \tilde{z}^{k+1} & \gets z^{k} + \rho^{-1}(\nu^{k+1} - y^{k})\\
+   z^{k+1} &\gets \Pi(\tilde{z}^{k} + \rho^{-1}y^{k})\\
+   y^{k+1} &\gets y^{k} + \rho (\tilde{z}^{k+1} - z^{k+1})
+
+where :math:`\Pi` is the projection onto the hyperbox :math:`[l,u]`. 
+:math:`\rho` is the ADMM step-size. 
+
+
+Linear system solution
+^^^^^^^^^^^^^^^^^^^^^^
+The linear system solution is the core part of the algorithm.
+It can be done using a **direct** or **indirect** method.
+
+With a **direct linear system solver** we solve the following linear system with a quasi-definite matrix 
+
+.. math::
+
+   \begin{bmatrix} P + \sigma I & A^T \\ A & -\rho^{-1}I \end{bmatrix} \begin{bmatrix} x^{k+1} \\ \nu^{k+1} \end{bmatrix}= \begin{bmatrix}\sigma x^{k} - q \\ z^{k} - \rho^{-1} y^k \end{bmatrix}.
+
+With an **indirect linear system solver** we solve the following linear system with a positive definite matrix 
+
+
+.. math::
+
+	\left(P + \sigma I + \rho A^T A \right)x^{k+1} = \sigma x^{k} - q + A^T (\rho z^{k} - y^{k}).
+
+
+OSQP core is designed to support different linear system solvers.
+For their installation see :ref:`this section <linear_system_solvers_installation>`.
+To specify your favorite linear system solver see :ref:`this section <linear_system_solvers_setting>`.
+
+Convergence
+^^^^^^^^^^^
+At each iteration :math:`k` OSQP produces a tuple :math:`(x^{k}, z^{k}, y^{k})` with :math:`x^{k} \in \mathbf{R}^{n}` and :math:`z^{k}, y^{k} \in \mathbf{R}^{m}`.
+
+The primal and and dual residuals associated to :math:`(x^{k}, z^{k}, y^{k})` are
+
+.. math::
+
+   \begin{aligned}
+   r_{\rm prim}^{k} &= Ax^{k} - z^{k}\\
+   r_{\rm dual}^{k} &= Px^{k} + q + A^{T} y^{k}.
+   \end{aligned}
+
+Complementary slackness is satisfied by construction at machine precision. If the problem is feasible, the residuals converge to zero as :math:`k\to\infty`. The algorithm stops when the norms of :math:`r_{\rm prim}^{k}` and :math:`r_{\rm dual}^{k}` are within the specified tolerance levels :math:`\epsilon_{\rm prim}>0` and :math:`\epsilon_{\rm dual}>0` 
+
+.. math::
+
+    \| r_{\rm prim}^{k} \|_{\infty} \le 
+   \epsilon_{\rm prim},
+    \quad
+    \| r_{\rm dual}^{k} \|_{\infty} \le 
+   \epsilon_{\rm dual}.
+
+We set the tolerance levels as
+    
+.. math:: 
+
+    \epsilon_{\rm prim} &= \epsilon_{\rm abs} + \epsilon_{\rm rel} \max\lbrace \|Ax^{k}\|_{\infty}, \| z^{k} \|_{\infty} \rbrace \\
+    \epsilon_{\rm dual} &= \epsilon_{\rm abs} + \epsilon_{\rm rel} \max\lbrace \| P x^{k} \|_{\infty}, \| A^T y^{k} \|_{\infty}, \| q \|_{\infty} \rbrace.
+
+
+.. _rho_step_size :
+
+:math:`\rho` step-size 
+^^^^^^^^^^^^^^^^^^^^^^
+To ensure quick convergence of the algorithm we adapt :math:`\rho` by balancing the residuals. 
+In default mode, the inteval (*i.e.*, number of iterations) at which we update :math:`\rho` is defined by a time measurement.
+When the iterations time becomes greater than a certain fraction of the setup time, *i.e.* :code:`adaptive_rho_fraction`, we set the current number of iterations as the interval to update :math:`\rho`.
+The update happens as follows 
+
+.. math::
+
+    \rho^{k+1} \gets \rho^{k} \sqrt{\frac{\|r_{\rm prim}\|_{\infty}}{\|r_{\rm dual}\|_{\infty}}}.
+
+
+Note that :math:`\rho` is updated only if it is sufficiently different than the current one.
+In particular if it is :code:`adaptive_rho_tolerance` times larger or smaller than the current one.
+
+
+
+Infeasible problems
+-------------------------------
+
+OSQP is able to detect if the problem is primal or dual infeasible.
+
+
+Primal infeasibility
+^^^^^^^^^^^^^^^^^^^^
+
+When the problem is primal infeasible, the algorithm generates a certificate of infeasibility, *i.e.*, a vector :math:`v\in\mathbf{R}^{m}` such that
+
+.. math::
+
+    A^T v = 0, \quad u^T v_{+} + l^T v_{-} < 0,
+
+where :math:`v_+=\max(v,0)` and :math:`v_-=\min(v,0)`.
+
+The primal infeasibility check is then
+
+.. math::
+
+    \left\|A^T v \right\|_{\infty} \le \epsilon_{\rm prim\_inf}, \quad u^T v_{+} + l^T v_{-} \le \epsilon_{\rm prim\_inf}.
+
+
+
+Dual infeasibility
+^^^^^^^^^^^^^^^^^^^^
+
+When the problem is dual infeasible, OSQP generates a vector :math:`s\in\mathbf{R}^{n}` being a certificate of dual infeasibility, *i.e.*,
+
+.. math::
+
+    P s = 0, \quad q^T s < 0, \quad (As)_i = \begin{cases} 0 & l_i \in \mathbf{R}, u_i\in\mathbf{R} \\ \ge 0 & l_i\in\mathbf{R}, u_i=+\infty \\ \le 0 & u_i\in\mathbf{R}, l_i=-\infty. \end{cases}
+
+
+The dual infeasibility check is then
+
+.. math::
+
+    \| P s \|_{\infty} \le \epsilon_{\rm dual\_inf} , \quad
+    q^T s \le \epsilon_{\rm dual\_inf}, \\
+    (A s)_i \begin{cases} \in \left[-\epsilon_{\rm dual\_inf}, \epsilon_{\rm dual\_inf}\right] & u_i, l_i \in \mathbf{R}\\
+    \ge -\epsilon_{\rm dual\_inf} &u_i = +\infty\\
+    \le  \epsilon_{\rm dual\_inf} &l_i = -\infty.\end{cases}
+
+
+
+
+Polishing
+^^^^^^^^^^^
+
+Polishing is an additional algorithm step where OSQP tries to compute a high-accuracy solution. 
+We can enable it by turning the setting :code:`polish` to 1.
+
+Polishing works by guessing the active constraints at the optimum and solving an additional linear system.
+If the guess is correct, OSQP returns a high accuracy solution.
+Otherwise OSQP returns the ADMM solution.
+The status of the polishing phase appears in the information :code:`status_polish`.
+
+Note that polishing requires the solution of an additional linear system and thereby, an additional factorization if the linear system solver is direct.
+However, the linear system is usually much smaller than the one solved during the ADMM iterations.
+
+The chances to have a successful polishing increase if the tolerances :code:`eps_abs` and :code:`eps_rel` are small. 
+However, low tolerances might require a very large number of iterations.
+
+
diff --git a/examples/osqp_demo.c b/examples/osqp_demo.c
new file mode 100644
index 0000000..107f83e
--- /dev/null
+++ b/examples/osqp_demo.c
@@ -0,0 +1,58 @@
+#include "osqp.h"
+
+
+int main(int argc, char **argv) {
+  // Load problem data
+  c_float P_x[3] = { 4.0, 1.0, 2.0, };
+  c_int   P_nnz  = 3;
+  c_int   P_i[3] = { 0, 0, 1, };
+  c_int   P_p[3] = { 0, 1, 3, };
+  c_float q[2]   = { 1.0, 1.0, };
+  c_float A_x[4] = { 1.0, 1.0, 1.0, 1.0, };
+  c_int   A_nnz  = 4;
+  c_int   A_i[4] = { 0, 1, 0, 2, };
+  c_int   A_p[3] = { 0, 2, 4, };
+  c_float l[3]   = { 1.0, 0.0, 0.0, };
+  c_float u[3]   = { 1.0, 0.7, 0.7, };
+  c_int n = 2;
+  c_int m = 3;
+
+  // Exitflag
+  c_int exitflag = 0;
+
+  // Workspace structures
+  OSQPWorkspace *work;
+  OSQPSettings  *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+  OSQPData      *data     = (OSQPData *)c_malloc(sizeof(OSQPData));
+
+  // Populate data
+  if (data) {
+    data->n = n;
+    data->m = m;
+    data->P = csc_matrix(data->n, data->n, P_nnz, P_x, P_i, P_p);
+    data->q = q;
+    data->A = csc_matrix(data->m, data->n, A_nnz, A_x, A_i, A_p);
+    data->l = l;
+    data->u = u;
+  }
+
+  // Define solver settings as default
+  if (settings) osqp_set_default_settings(settings);
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Clean workspace
+  osqp_cleanup(work);
+  if (data) {
+    if (data->A) c_free(data->A);
+    if (data->P) c_free(data->P);
+    c_free(data);
+  }
+  if (settings)  c_free(settings);
+
+  return exitflag;
+}
diff --git a/include/.gitignore b/include/.gitignore
new file mode 100644
index 0000000..87d6c48
--- /dev/null
+++ b/include/.gitignore
@@ -0,0 +1,2 @@
+# Ignore Cmake-generated osqp_configure.h file
+osqp_configure.h
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
new file mode 100644
index 0000000..a543f70
--- /dev/null
+++ b/include/CMakeLists.txt
@@ -0,0 +1,52 @@
+# Add the OSQP headers
+set(
+    osqp_headers
+    "${CMAKE_CURRENT_SOURCE_DIR}/version.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/auxil.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/constants.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/error.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/glob_opts.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/lin_alg.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/osqp.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/osqp_configure.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/proj.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/scaling.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/types.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/util.h"
+)
+
+# Add the KKT update only in normal mode and matrix-updating embedded mode (not mode 1)
+if (NOT (EMBEDDED EQUAL 1))
+    list(
+      APPEND
+      osqp_src
+      "${CMAKE_CURRENT_SOURCE_DIR}/kkt.h"
+    )
+endif()
+
+# Add more files that should only be in non-embedded code
+if (NOT DEFINED EMBEDDED)
+    list(
+      APPEND
+      osqp_headers
+      "${CMAKE_CURRENT_SOURCE_DIR}/cs.h"
+      "${CMAKE_CURRENT_SOURCE_DIR}/polish.h"
+      "${CMAKE_CURRENT_SOURCE_DIR}/lin_sys.h"
+    )
+endif()
+
+# Add the ctrl-c handler if enabled
+if (CTRLC)
+    list(
+      APPEND
+      osqp_headers
+      "${CMAKE_CURRENT_SOURCE_DIR}/ctrlc.h"
+    )
+endif()
+
+# Pass the header list up to the main CMakeLists scope
+set(
+  osqp_headers
+  "${osqp_headers}"
+  PARENT_SCOPE
+)
diff --git a/include/auxil.h b/include/auxil.h
new file mode 100644
index 0000000..d756c2a
--- /dev/null
+++ b/include/auxil.h
@@ -0,0 +1,181 @@
+#ifndef AUXIL_H
+# define AUXIL_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+# include "types.h"
+
+
+/***********************************************************
+* Auxiliary functions needed to compute ADMM iterations * *
+***********************************************************/
+# if EMBEDDED != 1
+
+/**
+ * Compute rho estimate from residuals
+ * @param work Workspace
+ * @return     rho estimate
+ */
+c_float compute_rho_estimate(OSQPWorkspace *work);
+
+/**
+ * Adapt rho value based on current unscaled primal/dual residuals
+ * @param work Workspace
+ * @return     Exitflag
+ */
+c_int   adapt_rho(OSQPWorkspace *work);
+
+/**
+ * Set values of rho vector based on constraint types
+ * @param work Workspace
+ */
+void    set_rho_vec(OSQPWorkspace *work);
+
+/**
+ * Update values of rho vector based on updated constraints.
+ * If the constraints change, update the linear systems solver.
+ *
+ * @param work Workspace
+ * @return     Exitflag
+ */
+c_int   update_rho_vec(OSQPWorkspace *work);
+
+# endif // EMBEDDED
+
+/**
+ * Swap c_float vector pointers
+ * @param a first vector
+ * @param b second vector
+ */
+void swap_vectors(c_float **a,
+                  c_float **b);
+
+
+/**
+ * Cold start workspace variables xz and y
+ * @param work Workspace
+ */
+void cold_start(OSQPWorkspace *work);
+
+
+/**
+ * Update x_tilde and z_tilde variable (first ADMM step)
+ * @param work [description]
+ */
+void update_xz_tilde(OSQPWorkspace *work);
+
+
+/**
+ * Update x (second ADMM step)
+ * Update also delta_x (For for dual infeasibility)
+ * @param work Workspace
+ */
+void update_x(OSQPWorkspace *work);
+
+
+/**
+ * Update z (third ADMM step)
+ * @param work Workspace
+ */
+void update_z(OSQPWorkspace *work);
+
+
+/**
+ * Update y variable (fourth ADMM step)
+ * Update also delta_y to check for primal infeasibility
+ * @param work Workspace
+ */
+void update_y(OSQPWorkspace *work);
+
+
+/**
+ * Compute objective function from data at value x
+ * @param  work OSQPWorkspace structure
+ * @param  x    Value x
+ * @return      Objective function value
+ */
+c_float compute_obj_val(OSQPWorkspace *work,
+                        c_float       *x);
+
+/**
+ * Check whether QP has solution
+ * @param info OSQPInfo
+ */
+c_int has_solution(OSQPInfo *info);
+
+/**
+ * Store the QP solution
+ * @param work Workspace
+ */
+void store_solution(OSQPWorkspace *work);
+
+
+/**
+ * Update solver information
+ * @param work               Workspace
+ * @param iter               Iteration number
+ * @param compute_objective  Boolean (if compute the objective or not)
+ * @param polish             Boolean (if called from polish)
+ */
+void update_info(OSQPWorkspace *work,
+                 c_int          iter,
+                 c_int          compute_objective,
+                 c_int          polish);
+
+
+/**
+ * Reset solver information (after problem updates)
+ * @param info               Information structure
+ */
+void reset_info(OSQPInfo *info);
+
+
+/**
+ * Update solver status (value and string)
+ * @param info OSQPInfo
+ * @param status_val new status value
+ */
+void update_status(OSQPInfo *info,
+                   c_int     status_val);
+
+
+/**
+ * Check if termination conditions are satisfied
+ * If the boolean flag is ON, it checks for approximate conditions (10 x larger
+ * tolerances than the ones set)
+ *
+ * @param  work        Workspace
+ * @param  approximate Boolean
+ * @return      Residuals check
+ */
+c_int check_termination(OSQPWorkspace *work,
+                        c_int          approximate);
+
+
+# ifndef EMBEDDED
+
+/**
+ * Validate problem data
+ * @param  data OSQPData to be validated
+ * @return      Exitflag to check
+ */
+c_int validate_data(const OSQPData *data);
+
+
+/**
+ * Validate problem settings
+ * @param  settings OSQPSettings to be validated
+ * @return          Exitflag to check
+ */
+c_int validate_settings(const OSQPSettings *settings);
+
+
+# endif // #ifndef EMBEDDED
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef AUXIL_H
diff --git a/include/constants.h b/include/constants.h
new file mode 100644
index 0000000..2acbb65
--- /dev/null
+++ b/include/constants.h
@@ -0,0 +1,128 @@
+#ifndef CONSTANTS_H
+# define CONSTANTS_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+
+/*******************
+* OSQP Versioning *
+*******************/
+#include "version.h"
+
+/******************
+* Solver Status  *
+******************/
+# define OSQP_DUAL_INFEASIBLE_INACCURATE (4)
+# define OSQP_PRIMAL_INFEASIBLE_INACCURATE (3)
+# define OSQP_SOLVED_INACCURATE (2)
+# define OSQP_SOLVED (1)
+# define OSQP_MAX_ITER_REACHED (-2)
+# define OSQP_PRIMAL_INFEASIBLE (-3)    /* primal infeasible  */
+# define OSQP_DUAL_INFEASIBLE (-4)      /* dual infeasible */
+# define OSQP_SIGINT (-5)               /* interrupted by user */
+# ifdef PROFILING
+#  define OSQP_TIME_LIMIT_REACHED (-6)
+# endif // ifdef PROFILING
+# define OSQP_NON_CVX (-7)              /* problem non convex */
+# define OSQP_UNSOLVED (-10)            /* Unsolved. Only setup function has been called */
+
+
+/*************************
+* Linear System Solvers *
+*************************/
+enum linsys_solver_type { QDLDL_SOLVER, MKL_PARDISO_SOLVER, UNKNOWN_SOLVER=99 };
+extern const char * LINSYS_SOLVER_NAME[];
+
+
+/******************
+* Solver Errors  *
+******************/
+enum osqp_error_type {
+    OSQP_DATA_VALIDATION_ERROR = 1,  /* Start errors from 1 */
+    OSQP_SETTINGS_VALIDATION_ERROR,
+    OSQP_LINSYS_SOLVER_LOAD_ERROR,
+    OSQP_LINSYS_SOLVER_INIT_ERROR,
+    OSQP_NONCVX_ERROR,
+    OSQP_MEM_ALLOC_ERROR,
+    OSQP_WORKSPACE_NOT_INIT_ERROR,
+};
+extern const char * OSQP_ERROR_MESSAGE[];
+
+
+/**********************************
+* Solver Parameters and Settings *
+**********************************/
+
+# define RHO (0.1)
+# define SIGMA (1E-06)
+# define MAX_ITER (4000)
+# define EPS_ABS (1E-3)
+# define EPS_REL (1E-3)
+# define EPS_PRIM_INF (1E-4)
+# define EPS_DUAL_INF (1E-4)
+# define ALPHA (1.6)
+# define LINSYS_SOLVER (QDLDL_SOLVER)
+
+# define RHO_MIN (1e-06)
+# define RHO_MAX (1e06)
+# define RHO_EQ_OVER_RHO_INEQ (1e03)
+# define RHO_TOL (1e-04) ///< tolerance for detecting if an inequality is set to equality
+
+
+# ifndef EMBEDDED
+#  define DELTA (1E-6)
+#  define POLISH (0)
+#  define POLISH_REFINE_ITER (3)
+#  define VERBOSE (1)
+# endif // ifndef EMBEDDED
+
+# define SCALED_TERMINATION (0)
+# define CHECK_TERMINATION (25)
+# define WARM_START (1)
+# define SCALING (10)
+
+# define MIN_SCALING (1e-04) ///< minimum scaling value
+# define MAX_SCALING (1e+04) ///< maximum scaling value
+
+
+# ifndef OSQP_NULL
+#  define OSQP_NULL 0
+# endif /* ifndef OSQP_NULL */
+
+# ifndef OSQP_NAN
+#  define OSQP_NAN ((c_float)0x7fc00000UL)  // not a number
+# endif /* ifndef OSQP_NAN */
+
+# ifndef OSQP_INFTY
+#  define OSQP_INFTY ((c_float)1e30)        // infinity
+# endif /* ifndef OSQP_INFTY */
+
+# ifndef OSQP_DIVISION_TOL
+#  define OSQP_DIVISION_TOL ((c_float)1.0 / OSQP_INFTY)
+# endif /* ifndef OSQP_DIVISION_TOL */
+
+
+# if EMBEDDED != 1
+#  define ADAPTIVE_RHO (1)
+#  define ADAPTIVE_RHO_INTERVAL (0)
+#  define ADAPTIVE_RHO_FRACTION (0.4)           ///< fraction of setup time after which we update rho
+#  define ADAPTIVE_RHO_MULTIPLE_TERMINATION (4) ///< multiple of check_termination after which we update rho (if PROFILING disabled)
+#  define ADAPTIVE_RHO_FIXED (100)              ///< number of iterations after which we update rho if termination_check  and PROFILING are disabled
+#  define ADAPTIVE_RHO_TOLERANCE (5)            ///< tolerance for adopting new rho; minimum ratio between new rho and the current one
+# endif // if EMBEDDED != 1
+
+# ifdef PROFILING
+#  define TIME_LIMIT (0)                        ///< Disable time limit as default
+# endif // ifdef PROFILING
+
+/* Printing */
+# define PRINT_INTERVAL 200
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef CONSTANTS_H
diff --git a/include/cs.h b/include/cs.h
new file mode 100644
index 0000000..dff0a80
--- /dev/null
+++ b/include/cs.h
@@ -0,0 +1,180 @@
+#ifndef CS_H
+# define CS_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+# include "types.h"   // CSC matrix type
+# include "lin_alg.h" // Vector copy operations
+
+/*****************************************************************************
+* Create and free CSC Matrices                                              *
+*****************************************************************************/
+
+/**
+ * Create Compressed-Column-Sparse matrix from existing arrays
+    (no MALLOC to create inner arrays x, i, p)
+ * @param  m     First dimension
+ * @param  n     Second dimension
+ * @param  nzmax Maximum number of nonzero elements
+ * @param  x     Vector of data
+ * @param  i     Vector of row indices
+ * @param  p     Vector of column pointers
+ * @return       New matrix pointer
+ */
+csc* csc_matrix(c_int    m,
+                c_int    n,
+                c_int    nzmax,
+                c_float *x,
+                c_int   *i,
+                c_int   *p);
+
+
+/**
+ * Create uninitialized CSC matrix atricture
+    (uses MALLOC to create inner arrays x, i, p)
+ * @param  m       First dimension
+ * @param  n       Second dimension
+ * @param  nzmax   Maximum number of nonzero elements
+ * @param  values  Allocate values (0/1)
+ * @param  triplet Allocate CSC or triplet format matrix (1/0)
+ * @return         Matrix pointer
+ */
+csc* csc_spalloc(c_int m,
+                 c_int n,
+                 c_int nzmax,
+                 c_int values,
+                 c_int triplet);
+
+
+/**
+ * Free sparse matrix
+    (uses FREE to free inner arrays x, i, p)
+ * @param  A Matrix in CSC format
+ */
+void csc_spfree(csc *A);
+
+
+/**
+ * free workspace and return a sparse matrix result
+ * @param  C  CSC matrix
+ * @param  w  Workspace vector
+ * @param  x  Workspace vector
+ * @param  ok flag
+ * @return    Return result C if OK, otherwise free it
+ */
+csc* csc_done(csc  *C,
+              void *w,
+              void *x,
+              c_int ok);
+
+/*****************************************************************************
+* Copy Matrices                                                             *
+*****************************************************************************/
+
+/**
+ *  Copy sparse CSC matrix A to output.
+ *  output is allocated by this function (uses MALLOC)
+ */
+csc* copy_csc_mat(const csc *A);
+
+
+/**
+ *  Copy sparse CSC matrix A to B (B is preallocated, NO MALOC)
+ */
+void prea_copy_csc_mat(const csc *A,
+                       csc       *B);
+
+
+/*****************************************************************************
+* Matrices Conversion                                                       *
+*****************************************************************************/
+
+
+/**
+ * C = compressed-column CSC from matrix T in triplet form
+ *
+ * TtoC stores the vector of indices from T to C
+ *  -> C[TtoC[i]] = T[i]
+ *
+ * @param  T    matrix in triplet format
+ * @param  TtoC vector of indices from triplet to CSC format
+ * @return      matrix in CSC format
+ */
+csc* triplet_to_csc(const csc *T,
+                    c_int     *TtoC);
+
+
+/**
+ * C = compressed-row CSR from matrix T in triplet form
+ *
+ * TtoC stores the vector of indices from T to C
+ *  -> C[TtoC[i]] = T[i]
+ *
+ * @param  T    matrix in triplet format
+ * @param  TtoC vector of indices from triplet to CSR format
+ * @return      matrix in CSR format
+ */
+csc* triplet_to_csr(const csc *T,
+                    c_int     *TtoC);
+
+
+/**
+ * Convert sparse to dense
+ */
+c_float* csc_to_dns(csc *M);
+
+
+/**
+ * Convert square CSC matrix into upper triangular one
+ *
+ * @param  M         Matrix to be converted
+ * @return           Upper triangular matrix in CSC format
+ */
+csc* csc_to_triu(csc *M);
+
+
+/*****************************************************************************
+* Extra operations                                                          *
+*****************************************************************************/
+
+/**
+ * p [0..n] = cumulative sum of c [0..n-1], and then copy p [0..n-1] into c
+ *
+ * @param  p Create cumulative sum into p
+ * @param  c Vector of which we compute cumulative sum
+ * @param  n Number of elements
+ * @return   Exitflag
+ */
+c_int csc_cumsum(c_int *p,
+                 c_int *c,
+                 c_int  n);
+
+/**
+ * Compute inverse of permutation matrix stored in the vector p.
+ * The computed inverse is also stored in a vector.
+ */
+c_int* csc_pinv(c_int const *p,
+                c_int        n);
+
+/**
+ * C = A(p,p)= PAP' where A and C are symmetric the upper part stored;
+ *  NB: pinv not p!
+ * @param  A      Original matrix (upper-triangular)
+ * @param  pinv   Inverse of permutation vector
+ * @param  AtoC   Mapping from indices of A-x to C->x
+ * @param  values Are values of A allocated?
+ * @return        New matrix (allocated)
+ */
+csc* csc_symperm(const csc   *A,
+                 const c_int *pinv,
+                 c_int       *AtoC,
+                 c_int        values);
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef CS_H
diff --git a/include/ctrlc.h b/include/ctrlc.h
new file mode 100644
index 0000000..749e8e7
--- /dev/null
+++ b/include/ctrlc.h
@@ -0,0 +1,56 @@
+/*
+ * Interface for OSQP signal handling.
+ */
+
+#ifndef CTRLC_H
+# define CTRLC_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+# include "glob_opts.h"
+
+# if defined MATLAB
+
+/* No header file available here; define the prototypes ourselves */
+bool utIsInterruptPending(void);
+bool utSetInterruptEnabled(bool);
+
+# elif defined IS_WINDOWS
+
+/* Use Windows SetConsoleCtrlHandler for signal handling */
+#  include <windows.h>
+
+# else // if defined MATLAB
+
+/* Use sigaction for signal handling on non-Windows machines */
+#  include <signal.h>
+
+# endif // if defined MATLAB
+
+/* METHODS are the same for both */
+
+/**
+ * Start listener for ctrl-c interrupts
+ */
+void osqp_start_interrupt_listener(void);
+
+/**
+ * End listener for ctrl-c interrupts
+ */
+void osqp_end_interrupt_listener(void);
+
+/**
+ * Check if the solver has been interrupted
+ * @return  Boolean indicating if the solver has been interrupted
+ */
+int osqp_is_interrupted(void);
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+
+#endif /* END IFDEF CTRLC */
diff --git a/include/error.h b/include/error.h
new file mode 100644
index 0000000..9d7879f
--- /dev/null
+++ b/include/error.h
@@ -0,0 +1,38 @@
+#ifndef ERROR_H
+# define ERROR_H
+
+/* OSQP error handling */
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+# include "types.h"
+
+
+/* OSQP error macro */
+# if __STDC_VERSION__ >= 199901L
+/* The C99 standard gives the __func__ macro, which is preferred over __FUNCTION__ */
+#  define osqp_error(error_code) _osqp_error(error_code, __func__);
+#else
+#  define osqp_error(error_code) _osqp_error(error_code, __FUNCTION__);
+#endif
+
+
+
+/**
+ * Internal function to print error description and return error code.
+ * @param  Error code
+ * @param  Function name
+ * @return Error code
+ */
+  c_int _osqp_error(enum osqp_error_type error_code,
+		    const char * function_name);
+
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef ERROR_H
diff --git a/include/glob_opts.h b/include/glob_opts.h
new file mode 100644
index 0000000..e2b5b24
--- /dev/null
+++ b/include/glob_opts.h
@@ -0,0 +1,167 @@
+#ifndef GLOB_OPTS_H
+# define GLOB_OPTS_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* ifdef __cplusplus */
+
+/*
+   Define OSQP compiler flags
+ */
+
+// cmake generated compiler flags
+#include "osqp_configure.h"
+
+/* DATA CUSTOMIZATIONS (depending on memory manager)-----------------------   */
+
+// We do not need memory allocation functions if EMBEDDED is enabled
+# ifndef EMBEDDED
+
+/* define custom printfs and memory allocation (e.g. matlab/python) */
+#  ifdef MATLAB
+    #   include "mex.h"
+static void* c_calloc(size_t num, size_t size) {
+  void *m = mxCalloc(num, size);
+  mexMakeMemoryPersistent(m);
+  return m;
+}
+
+static void* c_malloc(size_t size) {
+  void *m = mxMalloc(size);
+  mexMakeMemoryPersistent(m);
+  return m;
+}
+
+static void* c_realloc(void *ptr, size_t size) {
+  void *m = mxRealloc(ptr, size);
+  mexMakeMemoryPersistent(m);
+  return m;
+}
+    #   define c_free mxFree
+#  elif defined PYTHON
+// Define memory allocation for python. Note that in Python 2 memory manager
+// Calloc is not implemented
+#   include <Python.h>
+#   if PY_MAJOR_VERSION >= 3
+// https://docs.python.org/3/c-api/memory.html
+// The following function sets are wrappers to the system allocator. These functions are thread-safe, the GIL does not need to be held.
+// The default raw memory allocator uses the following functions: malloc(), calloc(), realloc() and free(); call malloc(1) (or calloc(1, 1)) when requesting zero bytes.
+#    define c_malloc PyMem_RawMalloc
+#    define c_calloc PyMem_RawCalloc
+#    define c_free PyMem_RawFree
+#    define c_realloc PyMem_RawRealloc
+#   else  /* if PY_MAJOR_VERSION >= 3 */
+#   define c_malloc PyMem_Malloc
+#   define c_free PyMem_Free
+#   define c_realloc PyMem_Realloc
+static void* c_calloc(size_t num, size_t size) {
+	void *m = PyMem_Malloc(num * size);
+	memset(m, 0, num * size);
+	return m;
+}
+#   endif /* if PY_MAJOR_VERSION >= 3 */
+
+# elif !defined OSQP_CUSTOM_MEMORY
+/* If no custom memory allocator defined, use
+ * standard linux functions. Custom memory allocator definitions
+ * appear in the osqp_configure.h generated file. */
+    #  include <stdlib.h>
+    #  define c_malloc  malloc
+    #  define c_calloc  calloc
+    #  define c_free    free
+    #  define c_realloc realloc
+#  endif /* ifdef MATLAB */
+
+# endif // end ifndef EMBEDDED
+
+
+/* Use customized number representation -----------------------------------   */
+# ifdef DLONG            // long integers
+typedef long long c_int; /* for indices */
+# else // standard integers
+typedef int c_int;       /* for indices */
+# endif /* ifdef DLONG */
+
+
+# ifndef DFLOAT         // Doubles
+typedef double c_float; /* for numerical values  */
+# else                  // Floats
+typedef float c_float;  /* for numerical values  */
+# endif /* ifndef DFLOAT */
+
+
+/* Use customized operations */
+
+# ifndef c_absval
+#  define c_absval(x) (((x) < 0) ? -(x) : (x))
+# endif /* ifndef c_absval */
+
+# ifndef c_max
+#  define c_max(a, b) (((a) > (b)) ? (a) : (b))
+# endif /* ifndef c_max */
+
+# ifndef c_min
+#  define c_min(a, b) (((a) < (b)) ? (a) : (b))
+# endif /* ifndef c_min */
+
+// Round x to the nearest multiple of N
+# ifndef c_roundmultiple
+#  define c_roundmultiple(x, N) ((x) + .5 * (N)-c_fmod((x) + .5 * (N), (N)))
+# endif /* ifndef c_roundmultiple */
+
+
+/* Use customized functions -----------------------------------------------   */
+
+# if EMBEDDED != 1
+
+#  include <math.h>
+#  ifndef DFLOAT // Doubles
+#   define c_sqrt sqrt
+#   define c_fmod fmod
+#  else          // Floats
+#   define c_sqrt sqrtf
+#   define c_fmod fmodf
+#  endif /* ifndef DFLOAT */
+
+# endif // end EMBEDDED
+
+# ifdef PRINTING
+#  include <stdio.h>
+#  include <string.h>
+
+/* informational print function */
+#  ifdef MATLAB
+#   define c_print mexPrintf
+#  elif defined PYTHON
+#   include <Python.h>
+# define c_print(...)                              \
+  {                                                  \
+    PyGILState_STATE gilstate = PyGILState_Ensure(); \
+    PySys_WriteStdout(__VA_ARGS__);                  \
+    PyGILState_Release(gilstate);                    \
+  }
+#  elif defined R_LANG
+#   include <R_ext/Print.h>
+#   define c_print Rprintf
+#  else  /* ifdef MATLAB */
+#   define c_print printf
+#  endif /* c_print configuration */
+
+/* error printing function */
+#  ifdef R_LANG
+    /* Some CRAN builds complain about __VA_ARGS__, so just print */
+    /* out the error messages on R without the __FUNCTION__ trace */
+#   define c_eprint Rprintf
+#  else
+#   define c_eprint(...) c_print("ERROR in %s: ", __FUNCTION__); \
+            c_print(__VA_ARGS__); c_print("\n");
+#  endif /* c_eprint configuration */
+
+# endif  /* PRINTING */
+
+
+# ifdef __cplusplus
+}
+# endif /* ifdef __cplusplus */
+
+#endif /* ifndef GLOB_OPTS_H */
diff --git a/include/kkt.h b/include/kkt.h
new file mode 100644
index 0000000..9560d5e
--- /dev/null
+++ b/include/kkt.h
@@ -0,0 +1,109 @@
+#ifndef KKT_H
+# define KKT_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+# include "types.h"
+
+# ifndef EMBEDDED
+
+#  include "cs.h"
+
+/**
+ * Form square symmetric KKT matrix of the form
+ *
+ * [P + param1 I,            A';
+ *  A             -diag(param2)]
+ *
+ * NB: Only the upper triangular part is stuffed!
+ *
+ *
+ *  If Pdiag_idx is not OSQP_NULL, it saves the index of the diagonal
+ * elements of P there and the number of diagonal elements in Pdiag_n.
+ *
+ * Similarly, if rhotoKKT is not null,
+ * it saves where the values of param2 go in the final KKT matrix
+ *
+ * NB: Pdiag_idx needs to be freed!
+ *
+ * @param  P          cost matrix (already just upper triangular part)
+ * @param  A          linear constraint matrix
+ * @param  format     CSC (0) or CSR (1)
+ * @param  param1     regularization parameter
+ * @param  param2     regularization parameter (vector)
+ * @param  PtoKKT     (modified) index mapping from elements of P to KKT matrix
+ * @param  AtoKKT     (modified) index mapping from elements of A to KKT matrix
+ * @param  Pdiag_idx  (modified) Address of the index of diagonal elements in P
+ * @param  Pdiag_n    (modified) Address to the number of diagonal elements in P
+ * @param  param2toKKT    (modified) index mapping from param2 to elements of
+ *KKT
+ * @return            return status flag
+ */
+csc* form_KKT(const csc  *P,
+              const  csc *A,
+              c_int       format,
+              c_float     param1,
+              c_float    *param2,
+              c_int      *PtoKKT,
+              c_int      *AtoKKT,
+              c_int     **Pdiag_idx,
+              c_int      *Pdiag_n,
+              c_int      *param2toKKT);
+# endif // ifndef EMBEDDED
+
+
+# if EMBEDDED != 1
+
+/**
+ * Update KKT matrix using the elements of P
+ *
+ * @param KKT       KKT matrix in CSC form (upper-triangular)
+ * @param P         P matrix in CSC form (upper-triangular)
+ * @param PtoKKT    Vector of pointers from P->x to KKT->x
+ * @param param1    Parameter added to the diagonal elements of P
+ * @param Pdiag_idx Index of diagonal elements in P->x
+ * @param Pdiag_n   Number of diagonal elements of P
+ */
+void update_KKT_P(csc          *KKT,
+                  const csc    *P,
+                  const c_int  *PtoKKT,
+                  const c_float param1,
+                  const c_int  *Pdiag_idx,
+                  const c_int   Pdiag_n);
+
+
+/**
+ * Update KKT matrix using the elements of A
+ *
+ * @param KKT       KKT matrix in CSC form (upper-triangular)
+ * @param A         A matrix in CSC form (upper-triangular)
+ * @param AtoKKT    Vector of pointers from A->x to KKT->x
+ */
+void update_KKT_A(csc         *KKT,
+                  const csc   *A,
+                  const c_int *AtoKKT);
+
+
+/**
+ * Update KKT matrix with new param2
+ *
+ * @param KKT           KKT matrix
+ * @param param2        Parameter of the KKT matrix (vector)
+ * @param param2toKKT   index where param2 enters in the KKT matrix
+ * @param m             number of constraints
+ */
+void update_KKT_param2(csc           *KKT,
+                       const c_float *param2,
+                       const c_int   *param2toKKT,
+                       const c_int    m);
+
+# endif // EMBEDDED != 1
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef KKT_H
diff --git a/include/lin_alg.h b/include/lin_alg.h
new file mode 100644
index 0000000..e9589e9
--- /dev/null
+++ b/include/lin_alg.h
@@ -0,0 +1,216 @@
+#ifndef LIN_ALG_H
+# define LIN_ALG_H
+
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+# include "types.h"
+
+
+/* VECTOR FUNCTIONS ----------------------------------------------------------*/
+
+# ifndef EMBEDDED
+
+/* copy vector a into output (Uses MALLOC)*/
+c_float* vec_copy(c_float *a,
+                  c_int    n);
+# endif // ifndef EMBEDDED
+
+/* copy vector a into preallocated vector b */
+void prea_vec_copy(const c_float *a,
+                   c_float       *b,
+                   c_int          n);
+
+/* copy integer vector a into preallocated vector b */
+void prea_int_vec_copy(const c_int *a,
+                       c_int       *b,
+                       c_int        n);
+
+/* set float vector to scalar */
+void vec_set_scalar(c_float *a,
+                    c_float  sc,
+                    c_int    n);
+
+/* set integer vector to scalar */
+void int_vec_set_scalar(c_int *a,
+                        c_int  sc,
+                        c_int  n);
+
+/* add scalar to vector*/
+void vec_add_scalar(c_float *a,
+                    c_float  sc,
+                    c_int    n);
+
+/* multiply scalar to vector */
+void vec_mult_scalar(c_float *a,
+                     c_float  sc,
+                     c_int    n);
+
+/* c = a + sc*b */
+void vec_add_scaled(c_float       *c,
+                    const c_float *a,
+                    const c_float *b,
+                    c_int          n,
+                    c_float        sc);
+
+/* ||v||_inf */
+c_float vec_norm_inf(const c_float *v,
+                     c_int          l);
+
+/* ||Sv||_inf */
+c_float vec_scaled_norm_inf(const c_float *S,
+                            const c_float *v,
+                            c_int          l);
+
+/* ||a - b||_inf */
+c_float vec_norm_inf_diff(const c_float *a,
+                          const c_float *b,
+                          c_int          l);
+
+/* mean of vector elements */
+c_float vec_mean(const c_float *a,
+                 c_int          n);
+
+# if EMBEDDED != 1
+
+/* Vector elementwise reciprocal b = 1./a (needed for scaling)*/
+void vec_ew_recipr(const c_float *a,
+                   c_float       *b,
+                   c_int          n);
+# endif // if EMBEDDED != 1
+
+/* Inner product a'b */
+c_float vec_prod(const c_float *a,
+                 const c_float *b,
+                 c_int          n);
+
+/* Elementwise product a.*b stored in c*/
+void vec_ew_prod(const c_float *a,
+                 const c_float *b,
+                 c_float       *c,
+                 c_int          n);
+
+# if EMBEDDED != 1
+
+/* Elementwise sqrt of the vector elements */
+void vec_ew_sqrt(c_float *a,
+                 c_int    n);
+
+/* Elementwise max between each vector component and max_val */
+void vec_ew_max(c_float *a,
+                c_int    n,
+                c_float  max_val);
+
+/* Elementwise min between each vector component and max_val */
+void vec_ew_min(c_float *a,
+                c_int    n,
+                c_float  min_val);
+
+/* Elementwise maximum between vectors c = max(a, b) */
+void vec_ew_max_vec(const c_float *a,
+                    const c_float *b,
+                    c_float       *c,
+                    c_int          n);
+
+/* Elementwise minimum between vectors c = min(a, b) */
+void vec_ew_min_vec(const c_float *a,
+                    const c_float *b,
+                    c_float       *c,
+                    c_int          n);
+
+# endif // if EMBEDDED != 1
+
+
+/* MATRIX FUNCTIONS ----------------------------------------------------------*/
+
+/* multiply scalar to matrix */
+void mat_mult_scalar(csc    *A,
+                     c_float sc);
+
+/* Premultiply matrix A by diagonal matrix with diagonal d,
+   i.e. scale the rows of A by d
+ */
+void mat_premult_diag(csc           *A,
+                      const c_float *d);
+
+/* Premultiply matrix A by diagonal matrix with diagonal d,
+   i.e. scale the columns of A by d
+ */
+void mat_postmult_diag(csc           *A,
+                       const c_float *d);
+
+
+/* Matrix-vector multiplication
+ *    y  =  A*x  (if plus_eq == 0)
+ *    y +=  A*x  (if plus_eq == 1)
+ *    y -=  A*x  (if plus_eq == -1)
+ */
+void mat_vec(const csc     *A,
+             const c_float *x,
+             c_float       *y,
+             c_int          plus_eq);
+
+
+/* Matrix-transpose-vector multiplication
+ *    y  =  A'*x  (if plus_eq == 0)
+ *    y +=  A'*x  (if plus_eq == 1)
+ *    y -=  A'*x  (if plus_eq == -1)
+ * If skip_diag == 1, then diagonal elements of A are assumed to be zero.
+ */
+void mat_tpose_vec(const csc     *A,
+                   const c_float *x,
+                   c_float       *y,
+                   c_int          plus_eq,
+                   c_int          skip_diag);
+
+
+# if EMBEDDED != 1
+
+/**
+ * Infinity norm of each matrix column
+ * @param M	Input matrix
+ * @param E     Vector of infinity norms
+ *
+ */
+void mat_inf_norm_cols(const csc *M,
+                       c_float   *E);
+
+/**
+ * Infinity norm of each matrix row
+ * @param M	Input matrix
+ * @param E     Vector of infinity norms
+ *
+ */
+void mat_inf_norm_rows(const csc *M,
+                       c_float   *E);
+
+/**
+ * Infinity norm of each matrix column
+ * Matrix M is symmetric upper-triangular
+ *
+ * @param M	Input matrix (symmetric, upper-triangular)
+ * @param E     Vector of infinity norms
+ *
+ */
+void mat_inf_norm_cols_sym_triu(const csc *M,
+                                c_float   *E);
+
+# endif // EMBEDDED != 1
+
+/**
+ * Compute quadratic form f(x) = 1/2 x' P x
+ * @param  P quadratic matrix in CSC form (only upper triangular)
+ * @param  x argument float vector
+ * @return   quadratic form value
+ */
+c_float quad_form(const csc     *P,
+                  const c_float *x);
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef LIN_ALG_H
diff --git a/include/lin_sys.h b/include/lin_sys.h
new file mode 100644
index 0000000..69f3bdc
--- /dev/null
+++ b/include/lin_sys.h
@@ -0,0 +1,54 @@
+#ifndef LIN_SYS_H
+# define LIN_SYS_H
+
+/* KKT linear system definition and solution */
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+# include "types.h"
+
+/**
+ * Load linear system solver shared library
+ * @param	linsys_solver  Linear system solver
+ * @return Zero on success, nonzero on failure.
+ */
+c_int load_linsys_solver(enum linsys_solver_type linsys_solver);
+
+
+/**
+ * Unload linear system solver shared library
+ * @param	linsys_solver  Linear system solver
+ * @return Zero on success, nonzero on failure.
+ */
+c_int unload_linsys_solver(enum linsys_solver_type linsys_solver);
+
+
+// NB: Only the upper triangular part of P is stuffed!
+
+/**
+ * Initialize linear system solver structure
+ * @param   s             Pointer to linear system solver structure
+ * @param   P             Cost function matrix
+ * @param	A             Constraint matrix
+ * @param	sigma         Algorithm parameter
+ * @param	rho_vec       Algorithm parameter
+ * @param	linsys_solver Linear system solver
+ * @param	polish        0/1 depending whether we are allocating for
+ *polishing or not
+ * @return                Exitflag for error (0 if no errors)
+ */
+c_int init_linsys_solver(LinSysSolver          **s,
+                         const csc              *P,
+                         const csc              *A,
+                         c_float                 sigma,
+                         const c_float          *rho_vec,
+                         enum linsys_solver_type linsys_solver,
+                         c_int                   polish);
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef LIN_SYS_H
diff --git a/include/osqp.h b/include/osqp.h
new file mode 100644
index 0000000..296aff8
--- /dev/null
+++ b/include/osqp.h
@@ -0,0 +1,430 @@
+#ifndef OSQP_H
+# define OSQP_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+/* Includes */
+# include "types.h"
+# include "util.h" // Needed for osqp_set_default_settings functions
+
+
+// Library to deal with sparse matrices enabled only if embedded not defined
+# ifndef EMBEDDED
+#  include "cs.h"
+# endif // ifndef EMBEDDED
+
+/********************
+* Main Solver API  *
+********************/
+
+/**
+ * @name Main solver API
+ * @{
+ */
+
+/**
+ * Set default settings from constants.h file
+ * assumes settings already allocated in memory
+ * @param settings settings structure
+ */
+void osqp_set_default_settings(OSQPSettings *settings);
+
+
+# ifndef EMBEDDED
+
+/**
+ * Initialize OSQP solver allocating memory.
+ *
+ * All the inputs must be already allocated in memory before calling.
+ *
+ * It performs:
+ * - data and settings validation
+ * - problem data scaling
+ * - automatic parameters tuning (if enabled)
+ * - setup linear system solver:
+ *      - direct solver: KKT matrix factorization is performed here
+ *      - indirect solver: KKT matrix preconditioning is performed here
+ *
+ * NB: This is the only function that allocates dynamic memory and is not used
+ *during code generation
+ *
+ * @param  workp        Solver workspace pointer
+ * @param  data         Problem data
+ * @param  settings     Solver settings
+ * @return              Exitflag for errors (0 if no errors)
+ */
+c_int osqp_setup(OSQPWorkspace** workp, const OSQPData* data, const OSQPSettings* settings);
+
+# endif // #ifndef EMBEDDED
+
+/**
+ * Solve quadratic program
+ *
+ * The final solver information is stored in the \a work->info  structure
+ *
+ * The solution is stored in the  \a work->solution  structure
+ *
+ * If the problem is primal infeasible, the certificate is stored
+ * in \a work->delta_y
+ *
+ * If the problem is dual infeasible, the certificate is stored in \a
+ * work->delta_x
+ *
+ * @param  work Workspace allocated
+ * @return      Exitflag for errors
+ */
+c_int osqp_solve(OSQPWorkspace *work);
+
+
+# ifndef EMBEDDED
+
+/**
+ * Cleanup workspace by deallocating memory
+ *
+ * This function is not used in code generation
+ * @param  work Workspace
+ * @return      Exitflag for errors
+ */
+c_int osqp_cleanup(OSQPWorkspace *work);
+
+# endif // ifndef EMBEDDED
+
+/** @} */
+
+
+/********************************************
+* Sublevel API                             *
+*                                          *
+* Edit data without performing setup again *
+********************************************/
+
+/**
+ * @name Sublevel API
+ * @{
+ */
+
+/**
+ * Update linear cost in the problem
+ * @param  work  Workspace
+ * @param  q_new New linear cost
+ * @return       Exitflag for errors and warnings
+ */
+c_int osqp_update_lin_cost(OSQPWorkspace *work,
+                           const c_float *q_new);
+
+
+/**
+ * Update lower and upper bounds in the problem constraints
+ * @param  work   Workspace
+ * @param  l_new New lower bound
+ * @param  u_new New upper bound
+ * @return        Exitflag: 1 if new lower bound is not <= than new upper bound
+ */
+c_int osqp_update_bounds(OSQPWorkspace *work,
+                         const c_float *l_new,
+                         const c_float *u_new);
+
+
+/**
+ * Update lower bound in the problem constraints
+ * @param  work   Workspace
+ * @param  l_new New lower bound
+ * @return        Exitflag: 1 if new lower bound is not <= than upper bound
+ */
+c_int osqp_update_lower_bound(OSQPWorkspace *work,
+                              const c_float *l_new);
+
+
+/**
+ * Update upper bound in the problem constraints
+ * @param  work   Workspace
+ * @param  u_new New upper bound
+ * @return        Exitflag: 1 if new upper bound is not >= than lower bound
+ */
+c_int osqp_update_upper_bound(OSQPWorkspace *work,
+                              const c_float *u_new);
+
+
+/**
+ * Warm start primal and dual variables
+ * @param  work Workspace structure
+ * @param  x    Primal variable
+ * @param  y    Dual variable
+ * @return      Exitflag
+ */
+c_int osqp_warm_start(OSQPWorkspace *work,
+                      const c_float *x,
+                      const c_float *y);
+
+
+/**
+ * Warm start primal variable
+ * @param  work Workspace structure
+ * @param  x    Primal variable
+ * @return      Exitflag
+ */
+c_int osqp_warm_start_x(OSQPWorkspace *work,
+                        const c_float *x);
+
+
+/**
+ * Warm start dual variable
+ * @param  work Workspace structure
+ * @param  y    Dual variable
+ * @return      Exitflag
+ */
+c_int osqp_warm_start_y(OSQPWorkspace *work,
+                        const c_float *y);
+
+
+# if EMBEDDED != 1
+
+/**
+ * Update elements of matrix P (upper triangular)
+ * without changing sparsity structure.
+ *
+ *
+ *  If Px_new_idx is OSQP_NULL, Px_new is assumed to be as long as P->x
+ *  and the whole P->x is replaced.
+ *
+ * @param  work       Workspace structure
+ * @param  Px_new     Vector of new elements in P->x (upper triangular)
+ * @param  Px_new_idx Index mapping new elements to positions in P->x
+ * @param  P_new_n    Number of new elements to be changed
+ * @return            output flag:  0: OK
+ *                                  1: P_new_n > nnzP
+ *                                 <0: error in the update
+ */
+c_int osqp_update_P(OSQPWorkspace *work,
+                    const c_float *Px_new,
+                    const c_int   *Px_new_idx,
+                    c_int          P_new_n);
+
+
+/**
+ * Update elements of matrix A without changing sparsity structure.
+ *
+ *
+ *  If Ax_new_idx is OSQP_NULL, Ax_new is assumed to be as long as A->x
+ *  and the whole A->x is replaced.
+ *
+ * @param  work       Workspace structure
+ * @param  Ax_new     Vector of new elements in A->x
+ * @param  Ax_new_idx Index mapping new elements to positions in A->x
+ * @param  A_new_n    Number of new elements to be changed
+ * @return            output flag:  0: OK
+ *                                  1: A_new_n > nnzA
+ *                                 <0: error in the update
+ */
+c_int osqp_update_A(OSQPWorkspace *work,
+                    const c_float *Ax_new,
+                    const c_int   *Ax_new_idx,
+                    c_int          A_new_n);
+
+
+/**
+ * Update elements of matrix P (upper triangular) and elements of matrix A
+ * without changing sparsity structure.
+ *
+ *
+ *  If Px_new_idx is OSQP_NULL, Px_new is assumed to be as long as P->x
+ *  and the whole P->x is replaced.
+ *
+ *  If Ax_new_idx is OSQP_NULL, Ax_new is assumed to be as long as A->x
+ *  and the whole A->x is replaced.
+ *
+ * @param  work       Workspace structure
+ * @param  Px_new     Vector of new elements in P->x (upper triangular)
+ * @param  Px_new_idx Index mapping new elements to positions in P->x
+ * @param  P_new_n    Number of new elements to be changed
+ * @param  Ax_new     Vector of new elements in A->x
+ * @param  Ax_new_idx Index mapping new elements to positions in A->x
+ * @param  A_new_n    Number of new elements to be changed
+ * @return            output flag:  0: OK
+ *                                  1: P_new_n > nnzP
+ *                                  2: A_new_n > nnzA
+ *                                 <0: error in the update
+ */
+c_int osqp_update_P_A(OSQPWorkspace *work,
+                      const c_float *Px_new,
+                      const c_int   *Px_new_idx,
+                      c_int          P_new_n,
+                      const c_float *Ax_new,
+                      const c_int   *Ax_new_idx,
+                      c_int          A_new_n);
+
+/**
+ * Update rho. Limit it between RHO_MIN and RHO_MAX.
+ * @param  work         Workspace
+ * @param  rho_new      New rho setting
+ * @return              Exitflag
+ */
+c_int osqp_update_rho(OSQPWorkspace *work,
+                      c_float        rho_new);
+
+# endif // if EMBEDDED != 1
+
+/** @} */
+
+
+/**
+ * @name Update settings
+ * @{
+ */
+
+
+/**
+ * Update max_iter setting
+ * @param  work         Workspace
+ * @param  max_iter_new New max_iter setting
+ * @return              Exitflag
+ */
+c_int osqp_update_max_iter(OSQPWorkspace *work,
+                           c_int          max_iter_new);
+
+
+/**
+ * Update absolute tolernace value
+ * @param  work        Workspace
+ * @param  eps_abs_new New absolute tolerance value
+ * @return             Exitflag
+ */
+c_int osqp_update_eps_abs(OSQPWorkspace *work,
+                          c_float        eps_abs_new);
+
+
+/**
+ * Update relative tolernace value
+ * @param  work        Workspace
+ * @param  eps_rel_new New relative tolerance value
+ * @return             Exitflag
+ */
+c_int osqp_update_eps_rel(OSQPWorkspace *work,
+                          c_float        eps_rel_new);
+
+
+/**
+ * Update primal infeasibility tolerance
+ * @param  work          Workspace
+ * @param  eps_prim_inf_new  New primal infeasibility tolerance
+ * @return               Exitflag
+ */
+c_int osqp_update_eps_prim_inf(OSQPWorkspace *work,
+                               c_float        eps_prim_inf_new);
+
+
+/**
+ * Update dual infeasibility tolerance
+ * @param  work          Workspace
+ * @param  eps_dual_inf_new  New dual infeasibility tolerance
+ * @return               Exitflag
+ */
+c_int osqp_update_eps_dual_inf(OSQPWorkspace *work,
+                               c_float        eps_dual_inf_new);
+
+
+/**
+ * Update relaxation parameter alpha
+ * @param  work  Workspace
+ * @param  alpha_new New relaxation parameter value
+ * @return       Exitflag
+ */
+c_int osqp_update_alpha(OSQPWorkspace *work,
+                        c_float        alpha_new);
+
+
+/**
+ * Update warm_start setting
+ * @param  work           Workspace
+ * @param  warm_start_new New warm_start setting
+ * @return                Exitflag
+ */
+c_int osqp_update_warm_start(OSQPWorkspace *work,
+                             c_int          warm_start_new);
+
+
+/**
+ * Update scaled_termination setting
+ * @param  work                 Workspace
+ * @param  scaled_termination_new  New scaled_termination setting
+ * @return                      Exitflag
+ */
+c_int osqp_update_scaled_termination(OSQPWorkspace *work,
+                                     c_int          scaled_termination_new);
+
+/**
+ * Update check_termination setting
+ * @param  work                   Workspace
+ * @param  check_termination_new  New check_termination setting
+ * @return                        Exitflag
+ */
+c_int osqp_update_check_termination(OSQPWorkspace *work,
+                                    c_int          check_termination_new);
+
+
+# ifndef EMBEDDED
+
+/**
+ * Update regularization parameter in polish
+ * @param  work      Workspace
+ * @param  delta_new New regularization parameter
+ * @return           Exitflag
+ */
+c_int osqp_update_delta(OSQPWorkspace *work,
+                        c_float        delta_new);
+
+
+/**
+ * Update polish setting
+ * @param  work          Workspace
+ * @param  polish_new New polish setting
+ * @return               Exitflag
+ */
+c_int osqp_update_polish(OSQPWorkspace *work,
+                         c_int          polish_new);
+
+
+/**
+ * Update number of iterative refinement steps in polish
+ * @param  work                Workspace
+ * @param  polish_refine_iter_new New iterative reginement steps
+ * @return                     Exitflag
+ */
+c_int osqp_update_polish_refine_iter(OSQPWorkspace *work,
+                                     c_int          polish_refine_iter_new);
+
+
+/**
+ * Update verbose setting
+ * @param  work        Workspace
+ * @param  verbose_new New verbose setting
+ * @return             Exitflag
+ */
+c_int osqp_update_verbose(OSQPWorkspace *work,
+                          c_int          verbose_new);
+
+
+# endif // #ifndef EMBEDDED
+
+# ifdef PROFILING
+
+/**
+ * Update time_limit setting
+ * @param  work            Workspace
+ * @param  time_limit_new  New time_limit setting
+ * @return                 Exitflag
+ */
+c_int osqp_update_time_limit(OSQPWorkspace *work,
+                             c_float        time_limit_new);
+# endif // ifdef PROFILING
+
+/** @} */
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef OSQP_H
diff --git a/include/polish.h b/include/polish.h
new file mode 100644
index 0000000..5a8dc28
--- /dev/null
+++ b/include/polish.h
@@ -0,0 +1,25 @@
+/* Solution polish based on assuming the active set */
+#ifndef POLISH_H
+# define POLISH_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+
+# include "types.h"
+
+/**
+ * Solution polish: Solve equality constrained QP with assumed active
+ *constraints
+ * @param  work Workspace
+ * @return      Exitflag
+ */
+c_int polish(OSQPWorkspace *work);
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef POLISH_H
diff --git a/include/proj.h b/include/proj.h
new file mode 100644
index 0000000..ec0066a
--- /dev/null
+++ b/include/proj.h
@@ -0,0 +1,37 @@
+#ifndef PROJ_H
+# define PROJ_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+# include "types.h"
+
+
+/* Define Projections onto set C involved in the ADMM algorithm */
+
+/**
+ * Project z onto \f$C = [l, u]\f$
+ * @param z    Vector to project
+ * @param work Workspace
+ */
+void project(OSQPWorkspace *work,
+             c_float       *z);
+
+
+/**
+ * Ensure z satisfies box constraints and y is is normal cone of z
+ * @param work Workspace
+ * @param z    Primal variable z
+ * @param y    Dual variable y
+ */
+void project_normalcone(OSQPWorkspace *work,
+                        c_float       *z,
+                        c_float       *y);
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef PROJ_H
diff --git a/include/scaling.h b/include/scaling.h
new file mode 100644
index 0000000..0df9c04
--- /dev/null
+++ b/include/scaling.h
@@ -0,0 +1,44 @@
+#ifndef SCALING_H
+# define SCALING_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+// Functions to scale problem data
+# include "types.h"
+# include "lin_alg.h"
+# include "constants.h"
+
+// Enable data scaling if EMBEDDED is disabled or if EMBEDDED == 2
+# if EMBEDDED != 1
+
+/**
+ * Scale problem matrices
+ * @param  work Workspace
+ * @return      exitflag
+ */
+c_int scale_data(OSQPWorkspace *work);
+# endif // if EMBEDDED != 1
+
+
+/**
+ * Unscale problem matrices
+ * @param  work Workspace
+ * @return      exitflag
+ */
+c_int unscale_data(OSQPWorkspace *work);
+
+
+/**
+ * Unscale solution
+ * @param  work Workspace
+ * @return      exitflag
+ */
+c_int unscale_solution(OSQPWorkspace *work);
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef SCALING_H
diff --git a/include/types.h b/include/types.h
new file mode 100644
index 0000000..c1954ca
--- /dev/null
+++ b/include/types.h
@@ -0,0 +1,326 @@
+#ifndef OSQP_TYPES_H
+# define OSQP_TYPES_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+# include "glob_opts.h"
+# include "constants.h"
+
+
+/******************
+* Internal types *
+******************/
+
+/**
+ *  Matrix in compressed-column form.
+ *  The structure is used internally to store matrices in the triplet form as well,
+ *  but the API requires that the matrices are in the CSC format.
+ */
+typedef struct {
+  c_int    nzmax; ///< maximum number of entries
+  c_int    m;     ///< number of rows
+  c_int    n;     ///< number of columns
+  c_int   *p;     ///< column pointers (size n+1); col indices (size nzmax) start from 0 when using triplet format (direct KKT matrix formation)
+  c_int   *i;     ///< row indices, size nzmax starting from 0
+  c_float *x;     ///< numerical values, size nzmax
+  c_int    nz;    ///< number of entries in triplet matrix, -1 for csc
+} csc;
+
+/**
+ * Linear system solver structure (sublevel objects initialize it differently)
+ */
+
+typedef struct linsys_solver LinSysSolver;
+
+/**
+ * OSQP Timer for statistics
+ */
+typedef struct OSQP_TIMER OSQPTimer;
+
+/**
+ * Problem scaling matrices stored as vectors
+ */
+typedef struct {
+  c_float  c;    ///< cost function scaling
+  c_float *D;    ///< primal variable scaling
+  c_float *E;    ///< dual variable scaling
+  c_float  cinv; ///< cost function rescaling
+  c_float *Dinv; ///< primal variable rescaling
+  c_float *Einv; ///< dual variable rescaling
+} OSQPScaling;
+
+/**
+ * Solution structure
+ */
+typedef struct {
+  c_float *x; ///< primal solution
+  c_float *y; ///< Lagrange multiplier associated to \f$l <= Ax <= u\f$
+} OSQPSolution;
+
+
+/**
+ * Solver return information
+ */
+typedef struct {
+  c_int iter;          ///< number of iterations taken
+  char  status[32];    ///< status string, e.g. 'solved'
+  c_int status_val;    ///< status as c_int, defined in constants.h
+
+# ifndef EMBEDDED
+  c_int status_polish; ///< polish status: successful (1), unperformed (0), (-1) unsuccessful
+# endif // ifndef EMBEDDED
+
+  c_float obj_val;     ///< primal objective
+  c_float pri_res;     ///< norm of primal residual
+  c_float dua_res;     ///< norm of dual residual
+
+# ifdef PROFILING
+  c_float setup_time;  ///< time taken for setup phase (seconds)
+  c_float solve_time;  ///< time taken for solve phase (seconds)
+  c_float update_time; ///< time taken for update phase (seconds)
+  c_float polish_time; ///< time taken for polish phase (seconds)
+  c_float run_time;    ///< total time  (seconds)
+# endif // ifdef PROFILING
+
+# if EMBEDDED != 1
+  c_int   rho_updates;  ///< number of rho updates
+  c_float rho_estimate; ///< best rho estimate so far from residuals
+# endif // if EMBEDDED != 1
+} OSQPInfo;
+
+
+# ifndef EMBEDDED
+
+/**
+ * Polish structure
+ */
+typedef struct {
+  csc *Ared;          ///< active rows of A
+  ///<    Ared = vstack[Alow, Aupp]
+  c_int    n_low;     ///< number of lower-active rows
+  c_int    n_upp;     ///< number of upper-active rows
+  c_int   *A_to_Alow; ///< Maps indices in A to indices in Alow
+  c_int   *A_to_Aupp; ///< Maps indices in A to indices in Aupp
+  c_int   *Alow_to_A; ///< Maps indices in Alow to indices in A
+  c_int   *Aupp_to_A; ///< Maps indices in Aupp to indices in A
+  c_float *x;         ///< optimal x-solution obtained by polish
+  c_float *z;         ///< optimal z-solution obtained by polish
+  c_float *y;         ///< optimal y-solution obtained by polish
+  c_float  obj_val;   ///< objective value at polished solution
+  c_float  pri_res;   ///< primal residual at polished solution
+  c_float  dua_res;   ///< dual residual at polished solution
+} OSQPPolish;
+# endif // ifndef EMBEDDED
+
+
+/**********************************
+* Main structures and Data Types *
+**********************************/
+
+/**
+ * Data structure
+ */
+typedef struct {
+  c_int    n; ///< number of variables n
+  c_int    m; ///< number of constraints m
+  csc     *P; ///< the upper triangular part of the quadratic cost matrix P in csc format (size n x n).
+  csc     *A; ///< linear constraints matrix A in csc format (size m x n)
+  c_float *q; ///< dense array for linear part of cost function (size n)
+  c_float *l; ///< dense array for lower bound (size m)
+  c_float *u; ///< dense array for upper bound (size m)
+} OSQPData;
+
+
+/**
+ * Settings struct
+ */
+typedef struct {
+  c_float rho;                    ///< ADMM step rho
+  c_float sigma;                  ///< ADMM step sigma
+  c_int   scaling;                ///< heuristic data scaling iterations; if 0, then disabled.
+
+# if EMBEDDED != 1
+  c_int   adaptive_rho;           ///< boolean, is rho step size adaptive?
+  c_int   adaptive_rho_interval;  ///< number of iterations between rho adaptations; if 0, then it is automatic
+  c_float adaptive_rho_tolerance; ///< tolerance X for adapting rho. The new rho has to be X times larger or 1/X times smaller than the current one to trigger a new factorization.
+#  ifdef PROFILING
+  c_float adaptive_rho_fraction;  ///< interval for adapting rho (fraction of the setup time)
+#  endif // Profiling
+# endif // EMBEDDED != 1
+
+  c_int                   max_iter;      ///< maximum number of iterations
+  c_float                 eps_abs;       ///< absolute convergence tolerance
+  c_float                 eps_rel;       ///< relative convergence tolerance
+  c_float                 eps_prim_inf;  ///< primal infeasibility tolerance
+  c_float                 eps_dual_inf;  ///< dual infeasibility tolerance
+  c_float                 alpha;         ///< relaxation parameter
+  enum linsys_solver_type linsys_solver; ///< linear system solver to use
+
+# ifndef EMBEDDED
+  c_float delta;                         ///< regularization parameter for polishing
+  c_int   polish;                        ///< boolean, polish ADMM solution
+  c_int   polish_refine_iter;            ///< number of iterative refinement steps in polishing
+
+  c_int verbose;                         ///< boolean, write out progress
+# endif // ifndef EMBEDDED
+
+  c_int scaled_termination;              ///< boolean, use scaled termination criteria
+  c_int check_termination;               ///< integer, check termination interval; if 0, then termination checking is disabled
+  c_int warm_start;                      ///< boolean, warm start
+
+# ifdef PROFILING
+  c_float time_limit;                    ///< maximum number of seconds allowed to solve the problem; if 0, then disabled
+# endif // ifdef PROFILING
+} OSQPSettings;
+
+
+/**
+ * OSQP Workspace
+ */
+typedef struct {
+  /// Problem data to work on (possibly scaled)
+  OSQPData *data;
+
+  /// Linear System solver structure
+  LinSysSolver *linsys_solver;
+
+# ifndef EMBEDDED
+  /// Polish structure
+  OSQPPolish *pol;
+# endif // ifndef EMBEDDED
+
+  /**
+   * @name Vector used to store a vectorized rho parameter
+   * @{
+   */
+  c_float *rho_vec;     ///< vector of rho values
+  c_float *rho_inv_vec; ///< vector of inv rho values
+
+  /** @} */
+
+# if EMBEDDED != 1
+  c_int *constr_type; ///< Type of constraints: loose (-1), equality (1), inequality (0)
+# endif // if EMBEDDED != 1
+
+  /**
+   * @name Iterates
+   * @{
+   */
+  c_float *x;        ///< Iterate x
+  c_float *y;        ///< Iterate y
+  c_float *z;        ///< Iterate z
+  c_float *xz_tilde; ///< Iterate xz_tilde
+
+  c_float *x_prev;   ///< Previous x
+
+  /**< NB: Used also as workspace vector for dual residual */
+  c_float *z_prev;   ///< Previous z
+
+  /**< NB: Used also as workspace vector for primal residual */
+
+  /**
+   * @name Primal and dual residuals workspace variables
+   *
+   * Needed for residuals computation, tolerances computation,
+   * approximate tolerances computation and adapting rho
+   * @{
+   */
+  c_float *Ax;  ///< scaled A * x
+  c_float *Px;  ///< scaled P * x
+  c_float *Aty; ///< scaled A' * y
+
+  /** @} */
+
+  /**
+   * @name Primal infeasibility variables
+   * @{
+   */
+  c_float *delta_y;   ///< difference between consecutive dual iterates
+  c_float *Atdelta_y; ///< A' * delta_y
+
+  /** @} */
+
+  /**
+   * @name Dual infeasibility variables
+   * @{
+   */
+  c_float *delta_x;  ///< difference between consecutive primal iterates
+  c_float *Pdelta_x; ///< P * delta_x
+  c_float *Adelta_x; ///< A * delta_x
+
+  /** @} */
+
+  /**
+   * @name Temporary vectors used in scaling
+   * @{
+   */
+
+  c_float *D_temp;   ///< temporary primal variable scaling vectors
+  c_float *D_temp_A; ///< temporary primal variable scaling vectors storing norms of A columns
+  c_float *E_temp;   ///< temporary constraints scaling vectors storing norms of A' columns
+
+
+  /** @} */
+
+  OSQPSettings *settings; ///< problem settings
+  OSQPScaling  *scaling;  ///< scaling vectors
+  OSQPSolution *solution; ///< problem solution
+  OSQPInfo     *info;     ///< solver information
+
+# ifdef PROFILING
+  OSQPTimer *timer;       ///< timer object
+
+  /// flag indicating whether the solve function has been run before
+  c_int first_run;
+
+  /// flag indicating whether the update_time should be cleared
+  c_int clear_update_time;
+
+  /// flag indicating that osqp_update_rho is called from osqp_solve function
+  c_int rho_update_from_solve;
+# endif // ifdef PROFILING
+
+# ifdef PRINTING
+  c_int summary_printed; ///< Has last summary been printed? (true/false)
+# endif // ifdef PRINTING
+
+} OSQPWorkspace;
+
+
+/**
+ * Define linsys_solver prototype structure
+ *
+ * NB: The details are defined when the linear solver is initialized depending
+ *      on the choice
+ */
+struct linsys_solver {
+  enum linsys_solver_type type;                 ///< linear system solver type functions
+  c_int (*solve)(LinSysSolver *self,
+                 c_float      *b);              ///< solve linear system
+
+# ifndef EMBEDDED
+  void (*free)(LinSysSolver *self);             ///< free linear system solver (only in desktop version)
+# endif // ifndef EMBEDDED
+
+# if EMBEDDED != 1
+  c_int (*update_matrices)(LinSysSolver *s,
+                           const csc *P,            ///< update matrices P
+                           const csc *A);           //   and A in the solver
+
+  c_int (*update_rho_vec)(LinSysSolver  *s,
+                          const c_float *rho_vec);  ///< Update rho_vec
+# endif // if EMBEDDED != 1
+
+# ifndef EMBEDDED
+  c_int nthreads; ///< number of threads active
+# endif // ifndef EMBEDDED
+};
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef OSQP_TYPES_H
diff --git a/include/util.h b/include/util.h
new file mode 100644
index 0000000..6b2aac3
--- /dev/null
+++ b/include/util.h
@@ -0,0 +1,222 @@
+#ifndef UTIL_H
+# define UTIL_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif // ifdef __cplusplus
+
+# include "types.h"
+# include "constants.h"
+
+/******************
+* Versioning     *
+******************/
+
+/**
+ * Return OSQP version
+ * @return  OSQP version
+ */
+const char* osqp_version(void);
+
+
+/**********************
+* Utility Functions  *
+**********************/
+
+# ifndef EMBEDDED
+
+/**
+ * Copy settings creating a new settings structure (uses MALLOC)
+ * @param  settings Settings to be copied
+ * @return          New settings structure
+ */
+OSQPSettings* copy_settings(const OSQPSettings *settings);
+
+# endif // #ifndef EMBEDDED
+
+/**
+ * Custom string copy to avoid string.h library
+ * @param dest   destination string
+ * @param source source string
+ */
+void c_strcpy(char       dest[],
+              const char source[]);
+
+
+# ifdef PRINTING
+
+/**
+ * Print Header before running the algorithm
+ * @param work     osqp workspace
+ */
+void print_setup_header(const OSQPWorkspace *work);
+
+/**
+ * Print header with data to be displayed per iteration
+ */
+void print_header(void);
+
+/**
+ * Print iteration summary
+ * @param work current workspace
+ */
+void print_summary(OSQPWorkspace *work);
+
+/**
+ * Print information after polish
+ * @param work current workspace
+ */
+void print_polish(OSQPWorkspace *work);
+
+/**
+ * Print footer when algorithm terminates
+ * @param info   info structure
+ * @param polish is polish enabled?
+ */
+void print_footer(OSQPInfo *info,
+                  c_int     polish);
+
+
+# endif // ifdef PRINTING
+
+
+/*********************************
+* Timer Structs and Functions * *
+*********************************/
+
+/*! \cond PRIVATE */
+
+# ifdef PROFILING
+
+// Windows
+#  ifdef IS_WINDOWS
+
+  // Some R packages clash with elements
+  // of the windows.h header, so use a
+  // slimmer version for conflict avoidance
+# ifdef R_LANG
+#define NOGDI
+# endif
+
+#   include <windows.h>
+
+struct OSQP_TIMER {
+  LARGE_INTEGER tic;
+  LARGE_INTEGER toc;
+  LARGE_INTEGER freq;
+};
+
+// Mac
+#  elif defined IS_MAC
+
+#   include <mach/mach_time.h>
+
+/* Use MAC OSX  mach_time for timing */
+struct OSQP_TIMER {
+  uint64_t                  tic;
+  uint64_t                  toc;
+  mach_timebase_info_data_t tinfo;
+};
+
+// Linux
+#  else // ifdef IS_WINDOWS
+
+/* Use POSIX clock_gettime() for timing on non-Windows machines */
+#   include <time.h>
+#   include <sys/time.h>
+
+
+struct OSQP_TIMER {
+  struct timespec tic;
+  struct timespec toc;
+};
+
+#  endif // ifdef IS_WINDOWS
+
+/*! \endcond */
+
+/**
+ * Timer Methods
+ */
+
+/**
+ * Start timer
+ * @param t Timer object
+ */
+void    osqp_tic(OSQPTimer *t);
+
+/**
+ * Report time
+ * @param  t Timer object
+ * @return   Reported time
+ */
+c_float osqp_toc(OSQPTimer *t);
+
+# endif /* END #ifdef PROFILING */
+
+
+/* ================================= DEBUG FUNCTIONS ======================= */
+
+/*! \cond PRIVATE */
+
+
+# ifndef EMBEDDED
+
+/* Compare CSC matrices */
+c_int is_eq_csc(csc    *A,
+                csc    *B,
+                c_float tol);
+
+/* Convert sparse CSC to dense */
+c_float* csc_to_dns(csc *M);
+
+# endif // #ifndef EMBEDDED
+
+
+# ifdef PRINTING
+#  include <stdio.h>
+
+
+/* Print a csc sparse matrix */
+void print_csc_matrix(csc        *M,
+                      const char *name);
+
+/* Dump csc sparse matrix to file */
+void dump_csc_matrix(csc        *M,
+                     const char *file_name);
+
+/* Print a triplet format sparse matrix */
+void print_trip_matrix(csc        *M,
+                       const char *name);
+
+/* Print a dense matrix */
+void print_dns_matrix(c_float    *M,
+                      c_int       m,
+                      c_int       n,
+                      const char *name);
+
+/* Print vector  */
+void print_vec(c_float    *v,
+               c_int       n,
+               const char *name);
+
+/* Dump vector to file */
+void dump_vec(c_float    *v,
+              c_int       len,
+              const char *file_name);
+
+// Print int array
+void print_vec_int(c_int      *x,
+                   c_int       n,
+                   const char *name);
+
+# endif // ifdef PRINTING
+
+/*! \endcond */
+
+
+# ifdef __cplusplus
+}
+# endif // ifdef __cplusplus
+
+#endif // ifndef UTIL_H
diff --git a/include/version.h b/include/version.h
new file mode 100644
index 0000000..5532e5d
--- /dev/null
+++ b/include/version.h
@@ -0,0 +1,9 @@
+/**
+This file is replaced by an auto-generated version.h
+with an OSQP_VERSION obtained from a variable supplied
+to cmake
+*/
+
+#ifndef OSQP_VERSION
+#define OSQP_VERSION "0.0.0"
+#endif
diff --git a/lin_sys/CMakeLists.txt b/lin_sys/CMakeLists.txt
new file mode 100644
index 0000000..8f8998e
--- /dev/null
+++ b/lin_sys/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Add subdirectory for each linear systems solver
+
+if (NOT DEFINED EMBEDDED)
+# Include this directory for library handler
+# NB Needed for subfolders
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+endif()
+
+# Direct solver
+add_subdirectory(direct)
+
+# Indirect solver
+# TODO: Add!
+
+# Add linsys handler if not embedded
+if (NOT DEFINED EMBEDDED)
+set(linsys_lib_handler
+    ${CMAKE_CURRENT_SOURCE_DIR}/lib_handler.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/lib_handler.h)
+endif()
+
+
+
+# Combine solvers
+# TODO: Add indirect ones
+# Add library handler if desktop version
+set(linsys_solvers
+    ${direct_linsys_solvers}
+    ${linsys_lib_handler}
+    PARENT_SCOPE)
+
+
+
+# Combine solvers external libraries
+set(linsys_solvers_includes
+    ${direct_linsys_solvers_includes}
+    PARENT_SCOPE)
diff --git a/lin_sys/direct/CMakeLists.txt b/lin_sys/direct/CMakeLists.txt
new file mode 100644
index 0000000..c36c375
--- /dev/null
+++ b/lin_sys/direct/CMakeLists.txt
@@ -0,0 +1,44 @@
+# Add direct linear systems solvers
+
+# QDLDL (Default)
+# -------------------------
+add_subdirectory(qdldl)
+
+# Need to add qdldlobject only here because it cannot be
+# included in another object library such as linsys_qdldl
+set(direct_linsys_solvers $<TARGET_OBJECTS:linsys_qdldl> $<TARGET_OBJECTS:qdldlobject>)
+
+# NB. The second directory is added because we need to include the "qdldl_types.h" file in "qdldl_interface.h"
+set(direct_linsys_solvers_includes
+	"${CMAKE_CURRENT_SOURCE_DIR}/qdldl/"
+	"${CMAKE_CURRENT_SOURCE_DIR}/qdldl/qdldl_sources/include")
+
+
+# Add other solvers if embedded option is false
+if(NOT DEFINED EMBEDDED)
+
+# MKL Pardiso MKL
+# -----------
+# If MKL Pardiso is enabled, include pardiso directory
+if (ENABLE_MKL_PARDISO)
+	# Add Pardiso interface
+	add_subdirectory(pardiso)
+
+	add_library(linsys_pardiso OBJECT ${pardiso_sources})
+
+	# Add parent directory for library handler
+	target_include_directories(linsys_pardiso PRIVATE  ${pardiso_includes} ${PROJECT_SOURCE_DIR}/include)
+
+	set(direct_linsys_solvers ${direct_linsys_solvers} $<TARGET_OBJECTS:linsys_pardiso>)
+
+	set(direct_linsys_solvers_includes "${direct_linsys_solvers_includes};${CMAKE_CURRENT_SOURCE_DIR}/pardiso/")
+endif()
+
+
+endif()
+
+# Make direct_linsys_solvers list visible from parent directory
+set(direct_linsys_solvers ${direct_linsys_solvers} PARENT_SCOPE)
+
+# Make external linsys solvers includes visible from parent directory
+set(direct_linsys_solvers_includes ${direct_linsys_solvers_includes} PARENT_SCOPE)
diff --git a/lin_sys/direct/pardiso/CMakeLists.txt b/lin_sys/direct/pardiso/CMakeLists.txt
new file mode 100644
index 0000000..83a3f86
--- /dev/null
+++ b/lin_sys/direct/pardiso/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(pardiso_includes
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    PARENT_SCOPE
+)
+
+# Set source files
+set(
+    pardiso_sources
+    ${CMAKE_CURRENT_SOURCE_DIR}/pardiso_interface.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/pardiso_interface.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/pardiso_loader.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/pardiso_loader.c
+    PARENT_SCOPE
+)
diff --git a/lin_sys/direct/pardiso/pardiso_interface.c b/lin_sys/direct/pardiso/pardiso_interface.c
new file mode 100644
index 0000000..738334a
--- /dev/null
+++ b/lin_sys/direct/pardiso/pardiso_interface.c
@@ -0,0 +1,300 @@
+#include "pardiso_interface.h"
+
+#define MKL_INT c_int
+
+// Single Dynamic library interface
+#define MKL_INTERFACE_LP64  0x0
+#define MKL_INTERFACE_ILP64 0x1
+
+// Solver Phases
+#define PARDISO_SYMBOLIC  (11)
+#define PARDISO_NUMERIC   (22)
+#define PARDISO_SOLVE     (33)
+#define PARDISO_CLEANUP   (-1)
+
+
+// Prototypes for Pardiso functions
+void pardiso(void**,         // pt
+             const c_int*,   // maxfct
+             const c_int*,   // mnum
+             const c_int*,   // mtype
+             const c_int*,   // phase
+             const c_int*,   // n
+             const c_float*, // a
+             const c_int*,   // ia
+             const c_int*,   // ja
+             c_int*,         // perm
+             const c_int*,   //nrhs
+             c_int*,         // iparam
+             const c_int*,   //msglvl
+             c_float*,       // b
+             c_float*,       // x
+             c_int*          // error
+             );
+c_int mkl_set_interface_layer(c_int);
+c_int mkl_get_max_threads();
+
+// Free LDL Factorization structure
+void free_linsys_solver_pardiso(pardiso_solver *s) {
+    if (s) {
+
+        // Free pardiso solver using internal function
+        s->phase = PARDISO_CLEANUP;
+        pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase),
+                 &(s->nKKT), &(s->fdum), s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs),
+                 s->iparm, &(s->msglvl), &(s->fdum), &(s->fdum), &(s->error));
+
+      if ( s->error != 0 ){
+#ifdef PRINTING
+          c_eprint("Error during MKL Pardiso cleanup: %d", (int)s->error);
+#endif
+      }
+        // Check each attribute of the structure and free it if it exists
+        if (s->KKT)         csc_spfree(s->KKT);
+        if (s->KKT_i)       c_free(s->KKT_i);
+        if (s->KKT_p)       c_free(s->KKT_p);
+        if (s->bp)          c_free(s->bp);
+        if (s->sol)         c_free(s->sol);
+        if (s->rho_inv_vec) c_free(s->rho_inv_vec);
+
+        // These are required for matrix updates
+        if (s->Pdiag_idx) c_free(s->Pdiag_idx);
+        if (s->PtoKKT)    c_free(s->PtoKKT);
+        if (s->AtoKKT)    c_free(s->AtoKKT);
+        if (s->rhotoKKT)  c_free(s->rhotoKKT);
+
+        c_free(s);
+
+    }
+}
+
+
+// Initialize factorization structure
+c_int init_linsys_solver_pardiso(pardiso_solver ** sp, const csc * P, const csc * A, c_float sigma, const c_float * rho_vec, c_int polish){
+    c_int i;                     // loop counter
+    c_int nnzKKT;                // Number of nonzeros in KKT
+    // Define Variables
+    c_int n_plus_m;              // n_plus_m dimension
+
+
+    // Allocate private structure to store KKT factorization
+    pardiso_solver *s;
+    s = c_calloc(1, sizeof(pardiso_solver));
+    *sp = s;
+
+    // Size of KKT
+    s->n = P->n;
+    s->m = A->m;
+    n_plus_m = s->n + s->m;
+    s->nKKT = n_plus_m;
+
+    // Sigma parameter
+    s->sigma = sigma;
+
+    // Polishing flag
+    s->polish = polish;
+
+    // Link Functions
+    s->solve = &solve_linsys_pardiso;
+    s->free = &free_linsys_solver_pardiso;
+    s->update_matrices = &update_linsys_solver_matrices_pardiso;
+    s->update_rho_vec = &update_linsys_solver_rho_vec_pardiso;
+
+    // Assign type
+    s->type = MKL_PARDISO_SOLVER;
+
+    // Working vector
+    s->bp = (c_float *)c_malloc(sizeof(c_float) * n_plus_m);
+
+    // Solution vector
+    s->sol  = (c_float *)c_malloc(sizeof(c_float) * n_plus_m);
+
+    // Parameter vector
+    s->rho_inv_vec = (c_float *)c_malloc(sizeof(c_float) * n_plus_m);
+
+    // Form KKT matrix
+    if (polish){ // Called from polish()
+        // Use s->rho_inv_vec for storing param2 = vec(delta)
+        for (i = 0; i < A->m; i++){
+            s->rho_inv_vec[i] = sigma;
+        }
+
+        s->KKT = form_KKT(P, A, 1, sigma, s->rho_inv_vec, OSQP_NULL, OSQP_NULL, OSQP_NULL, OSQP_NULL, OSQP_NULL);
+    }
+    else { // Called from ADMM algorithm
+
+        // Allocate vectors of indices
+        s->PtoKKT = c_malloc((P->p[P->n]) * sizeof(c_int));
+        s->AtoKKT = c_malloc((A->p[A->n]) * sizeof(c_int));
+        s->rhotoKKT = c_malloc((A->m) * sizeof(c_int));
+
+        // Use s->rho_inv_vec for storing param2 = rho_inv_vec
+        for (i = 0; i < A->m; i++){
+            s->rho_inv_vec[i] = 1. / rho_vec[i];
+        }
+
+        s->KKT = form_KKT(P, A, 1, sigma, s->rho_inv_vec,
+                             s->PtoKKT, s->AtoKKT,
+                             &(s->Pdiag_idx), &(s->Pdiag_n), s->rhotoKKT);
+    }
+
+    // Check if matrix has been created
+    if (!(s->KKT)) {
+#ifdef PRINTING
+	    c_eprint("Error in forming KKT matrix");
+#endif
+        free_linsys_solver_pardiso(s);
+        return OSQP_LINSYS_SOLVER_INIT_ERROR;
+    } else {
+	    // Adjust indexing for Pardiso
+	    nnzKKT = s->KKT->p[s->KKT->m];
+	    s->KKT_i = c_malloc((nnzKKT) * sizeof(c_int));
+	    s->KKT_p = c_malloc((s->KKT->m + 1) * sizeof(c_int));
+
+	    for(i = 0; i < nnzKKT; i++){
+	    	s->KKT_i[i] = s->KKT->i[i] + 1;
+	    }
+	    for(i = 0; i < n_plus_m+1; i++){
+	    	s->KKT_p[i] = s->KKT->p[i] + 1;
+	    }
+
+    }
+
+    // Set MKL interface layer (Long integers if activated)
+#ifdef DLONG
+    mkl_set_interface_layer(MKL_INTERFACE_ILP64);
+#else
+    mkl_set_interface_layer(MKL_INTERFACE_LP64);
+#endif
+
+    // Set Pardiso variables
+    s->mtype = -2;        // Real symmetric indefinite matrix
+    s->nrhs = 1;          // Number of right hand sides
+    s->maxfct = 1;        // Maximum number of numerical factorizations
+    s->mnum = 1;          // Which factorization to use
+    s->msglvl = 0;        // Do not print statistical information
+    s->error = 0;         // Initialize error flag
+    for ( i = 0; i < 64; i++ ) {
+        s->iparm[i] = 0;  // Setup Pardiso control parameters
+        s->pt[i] = 0;     // Initialize the internal solver memory pointer
+    }
+    s->iparm[0] = 1;      // No solver default
+    s->iparm[1] = 3;      // Fill-in reordering from OpenMP
+    if (polish) {
+        s->iparm[5] = 1;  // Write solution into b
+    } else {
+        s->iparm[5] = 0;  // Do NOT write solution into b
+    }
+    /* s->iparm[7] = 2;      // Max number of iterative refinement steps */
+    s->iparm[7] = 0;      // Number of iterative refinement steps (auto, performs them only if perturbed pivots are obtained)
+    s->iparm[9] = 13;     // Perturb the pivot elements with 1E-13
+    s->iparm[34] = 0;     // Use Fortran-style indexing for indices
+    /* s->iparm[34] = 1;     // Use C-style indexing for indices */
+
+    // Print number of threads
+    s->nthreads = mkl_get_max_threads();
+
+    // Reordering and symbolic factorization
+    s->phase = PARDISO_SYMBOLIC;
+    pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase),
+             &(s->nKKT), s->KKT->x, s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs),
+             s->iparm, &(s->msglvl), &(s->fdum), &(s->fdum), &(s->error));
+    if ( s->error != 0 ){
+#ifdef PRINTING
+        c_eprint("Error during symbolic factorization: %d", (int)s->error);
+#endif
+        free_linsys_solver_pardiso(s);
+        *sp = OSQP_NULL;
+        return OSQP_LINSYS_SOLVER_INIT_ERROR;
+    }
+
+    // Numerical factorization
+    s->phase = PARDISO_NUMERIC;
+    pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase),
+             &(s->nKKT), s->KKT->x, s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs),
+             s->iparm, &(s->msglvl), &(s->fdum), &(s->fdum), &(s->error));
+    if ( s->error ){
+#ifdef PRINTING
+        c_eprint("Error during numerical factorization: %d", (int)s->error);
+#endif
+        free_linsys_solver_pardiso(s);
+        *sp = OSQP_NULL;
+        return OSQP_LINSYS_SOLVER_INIT_ERROR;
+    }
+
+
+    // No error
+    return 0;
+}
+
+// Returns solution to linear system  Ax = b with solution stored in b
+c_int solve_linsys_pardiso(pardiso_solver * s, c_float * b) {
+    c_int j;
+
+    // Back substitution and iterative refinement
+    s->phase = PARDISO_SOLVE;
+    pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase),
+             &(s->nKKT), s->KKT->x, s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs),
+             s->iparm, &(s->msglvl), b, s->sol, &(s->error));
+    if ( s->error != 0 ){
+#ifdef PRINTING
+        c_eprint("Error during linear system solution: %d", (int)s->error);
+#endif
+        return 1;
+    }
+
+    if (!(s->polish)) {
+        /* copy x_tilde from s->sol */
+        for (j = 0 ; j < s->n ; j++) {
+            b[j] = s->sol[j];
+        }
+
+        /* compute z_tilde from b and s->sol */
+        for (j = 0 ; j < s->m ; j++) {
+            b[j + s->n] += s->rho_inv_vec[j] * s->sol[j + s->n];
+        }
+    }
+
+    return 0;
+}
+
+// Update solver structure with new P and A
+c_int update_linsys_solver_matrices_pardiso(pardiso_solver * s, const csc *P, const csc *A) {
+
+    // Update KKT matrix with new P
+    update_KKT_P(s->KKT, P, s->PtoKKT, s->sigma, s->Pdiag_idx, s->Pdiag_n);
+
+    // Update KKT matrix with new A
+    update_KKT_A(s->KKT, A, s->AtoKKT);
+
+    // Perform numerical factorization
+    s->phase = PARDISO_NUMERIC;
+    pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase),
+             &(s->nKKT), s->KKT->x, s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs),
+             s->iparm, &(s->msglvl), &(s->fdum), &(s->fdum), &(s->error));
+
+    // Return exit flag
+    return s->error;
+}
+
+
+c_int update_linsys_solver_rho_vec_pardiso(pardiso_solver * s, const c_float * rho_vec) {
+    c_int i;
+
+    // Update internal rho_inv_vec
+    for (i = 0; i < s->m; i++){
+        s->rho_inv_vec[i] = 1. / rho_vec[i];
+    }
+
+    // Update KKT matrix with new rho_vec
+    update_KKT_param2(s->KKT, s->rho_inv_vec, s->rhotoKKT, s->m);
+
+    // Perform numerical factorization
+    s->phase = PARDISO_NUMERIC;
+    pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase),
+             &(s->nKKT), s->KKT->x, s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs),
+             s->iparm, &(s->msglvl), &(s->fdum), &(s->fdum), &(s->error));
+
+    // Return exit flag
+    return s->error;
+}
diff --git a/lin_sys/direct/pardiso/pardiso_interface.h b/lin_sys/direct/pardiso/pardiso_interface.h
new file mode 100644
index 0000000..c0e334d
--- /dev/null
+++ b/lin_sys/direct/pardiso/pardiso_interface.h
@@ -0,0 +1,127 @@
+#ifndef PARDISO_H
+#define PARDISO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lin_alg.h"
+#include "kkt.h"
+
+/**
+ * Pardiso solver structure
+ *
+ * NB: If we use Pardiso, we suppose that EMBEDDED is not enabled
+ */
+typedef struct pardiso pardiso_solver;
+
+struct pardiso {
+    enum linsys_solver_type type;
+
+    /**
+     * @name Functions
+     * @{
+     */
+    c_int (*solve)(struct pardiso * self, c_float * b);
+
+    void (*free)(struct pardiso * self); ///< Free workspace (only if desktop)
+
+    c_int (*update_matrices)(struct pardiso * self, const csc *P, const csc *A);    ///< Update solver matrices
+    c_int (*update_rho_vec)(struct pardiso * self, const c_float * rho_vec);        ///< Update rho_vec parameter
+
+    c_int nthreads;
+    /** @} */
+
+
+    /**
+     * @name Attributes
+     * @{
+     */
+    // Attributes
+    csc *KKT;               ///< KKT matrix (in CSR format!)
+    c_int *KKT_i;           ///< KKT column indices in 1-indexing for Pardiso
+    c_int *KKT_p;           ///< KKT row pointers in 1-indexing for Pardiso
+    c_float *bp;            ///< workspace memory for solves (rhs)
+    c_float *sol;           ///< solution to the KKT system
+    c_float *rho_inv_vec;   ///< parameter vector
+    c_float sigma;          ///< scalar parameter
+    c_int polish;           ///< polishing flag
+    c_int n;                ///< number of QP variables
+    c_int m;                ///< number of QP constraints
+
+    // Pardiso variables
+    void *pt[64];     ///< internal solver memory pointer pt
+    c_int iparm[64];  ///< Pardiso control parameters
+    c_int nKKT;       ///< dimension of the linear system
+    c_int mtype;      ///< matrix type (-2 for real and symmetric indefinite)
+    c_int nrhs;       ///< number of right-hand sides (1 for our needs)
+    c_int maxfct;     ///< maximum number of factors (1 for our needs)
+    c_int mnum;       ///< indicates matrix for the solution phase (1 for our needs)
+    c_int phase;      ///< control the execution phases of the solver
+    c_int error;      ///< the error indicator (0 for no error)
+    c_int msglvl;     ///< Message level information (0 for no output)
+    c_int idum;       ///< dummy integer
+    c_float fdum;     ///< dummy float
+
+    // These are required for matrix updates
+    c_int * Pdiag_idx, Pdiag_n;  ///< index and number of diagonal elements in P
+    c_int * PtoKKT, * AtoKKT;    ///< Index of elements from P and A to KKT matrix
+    c_int * rhotoKKT;            ///< Index of rho places in KKT matrix
+
+    /** @} */
+};
+
+
+/**
+ * Initialize Pardiso Solver
+ *
+ * @param  s         Pointer to a private structure
+ * @param  P         Cost function matrix (upper triangular form)
+ * @param  A         Constraints matrix
+ * @param  sigma     Algorithm parameter. If polish, then sigma = delta.
+ * @param  rho_vec   Algorithm parameter. If polish, then rho_vec = OSQP_NULL.
+ * @param  polish    Flag whether we are initializing for polish or not
+ * @return           Exitflag for error (0 if no errors)
+ */
+c_int init_linsys_solver_pardiso(pardiso_solver ** sp, const csc * P, const csc * A, c_float sigma, const c_float * rho_vec, c_int polish);
+
+
+/**
+ * Solve linear system and store result in b
+ * @param  s        Linear system solver structure
+ * @param  b        Right-hand side
+ * @return          Exitflag
+ */
+c_int solve_linsys_pardiso(pardiso_solver * s, c_float * b);
+
+
+/**
+ * Update linear system solver matrices
+ * @param  s        Linear system solver structure
+ * @param  P        Matrix P
+ * @param  A        Matrix A
+ * @return          Exitflag
+ */
+c_int update_linsys_solver_matrices_pardiso(pardiso_solver * s, const csc *P, const csc *A);
+
+
+/**
+ * Update rho parameter in linear system solver structure
+ * @param  s        Linear system solver structure
+ * @param  rho_vec  new rho_vec value
+ * @return          exitflag
+ */
+c_int update_linsys_solver_rho_vec_pardiso(pardiso_solver * s, const c_float * rho_vec);
+
+
+/**
+ * Free linear system solver
+ * @param s linear system solver object
+ */
+void free_linsys_solver_pardiso(pardiso_solver * s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lin_sys/direct/pardiso/pardiso_loader.c b/lin_sys/direct/pardiso/pardiso_loader.c
new file mode 100644
index 0000000..f120e47
--- /dev/null
+++ b/lin_sys/direct/pardiso/pardiso_loader.c
@@ -0,0 +1,102 @@
+#include "lib_handler.h"
+#include "pardiso_loader.h"
+
+#include "glob_opts.h"
+#include "constants.h"
+
+#ifdef IS_WINDOWS
+#define PARDISOLIBNAME "mkl_rt." SHAREDLIBEXT
+#else
+#define PARDISOLIBNAME "libmkl_rt." SHAREDLIBEXT
+#endif
+
+typedef void (*voidfun)(void);
+
+voidfun lh_load_sym (soHandle_t h, const char *symName);
+
+
+// Interfaces for Pardiso functions
+typedef void (*pardiso_t)(void**, const c_int*, const c_int*, const c_int*,
+                          const c_int*, const c_int*, const c_float*,
+                          const c_int*, const c_int*, c_int*,
+                          const c_int*, c_int*, const c_int*, c_float*,
+                          c_float*, c_int*);
+typedef int (*mkl_set_ifl_t)(int);
+typedef int (*mkl_get_mt_t)();
+
+
+// Handlers are static variables
+static soHandle_t Pardiso_handle = OSQP_NULL;
+static pardiso_t func_pardiso = OSQP_NULL;
+static mkl_set_ifl_t func_mkl_set_interface_layer = OSQP_NULL;
+static mkl_get_mt_t func_mkl_get_max_threads = OSQP_NULL;
+
+// Wrappers for loaded Pardiso function handlers
+void pardiso(void** pt, const c_int* maxfct, const c_int* mnum,
+                  const c_int* mtype, const c_int* phase, const c_int* n,
+                  const c_float* a, const c_int* ia, const c_int* ja,
+                  c_int* perm, const c_int* nrhs, c_int* iparm,
+                  const c_int* msglvl, c_float* b, c_float* x,
+                  c_int* error) {
+	if(func_pardiso){
+            // Call function pardiso only if it has been initialized
+	    func_pardiso(pt, maxfct, mnum, mtype, phase, n, a, ia, ja,
+			 perm, nrhs, iparm, msglvl, b, x, error);
+	}
+	else
+	{
+#ifdef PRINTING
+		c_eprint("Pardiso not loaded correctly");
+#endif
+	}
+}
+
+c_int mkl_set_interface_layer(c_int code) {
+    return (c_int)func_mkl_set_interface_layer((int)code);
+}
+
+c_int mkl_get_max_threads() {
+    return (c_int)func_mkl_get_max_threads();
+}
+
+
+c_int lh_load_pardiso(const char* libname) {
+    // DEBUG
+    // if (Pardiso_handle) return 0;
+
+    // Load Pardiso library
+    if (libname) {
+        Pardiso_handle = lh_load_lib(libname);
+    } else { /* try a default library name */
+        Pardiso_handle = lh_load_lib(PARDISOLIBNAME);
+    }
+    if (!Pardiso_handle) return 1;
+
+    // Load Pardiso functions
+    func_pardiso = (pardiso_t)lh_load_sym(Pardiso_handle, "pardiso");
+    if (!func_pardiso) return 1;
+
+    func_mkl_set_interface_layer = (mkl_set_ifl_t)lh_load_sym(Pardiso_handle,
+                                                    "MKL_Set_Interface_Layer");
+    if (!func_mkl_set_interface_layer) return 1;
+
+    func_mkl_get_max_threads = (mkl_get_mt_t)lh_load_sym(Pardiso_handle,
+                                                    "MKL_Get_Max_Threads");
+    if (!func_mkl_get_max_threads) return 1;
+
+    return 0;
+}
+
+c_int lh_unload_pardiso() {
+
+    if (Pardiso_handle == OSQP_NULL) return 0;
+
+    return lh_unload_lib(Pardiso_handle);
+
+    /* If multiple OSQP objects are laoded, the lines below cause a crash */
+    // Pardiso_handle = OSQP_NULL;
+    // func_pardiso = OSQP_NULL;
+    // func_mkl_set_interface_layer = OSQP_NULL;
+    // func_mkl_get_max_threads = OSQP_NULL;
+
+}
diff --git a/lin_sys/direct/pardiso/pardiso_loader.h b/lin_sys/direct/pardiso/pardiso_loader.h
new file mode 100644
index 0000000..aa96b24
--- /dev/null
+++ b/lin_sys/direct/pardiso/pardiso_loader.h
@@ -0,0 +1,29 @@
+#ifndef PARDISOLOADER_H
+#define PARDISOLOADER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+/**
+ * Tries to load a shared library with Pardiso.
+ * Return a failure if the library cannot be loaded or not all Pardiso symbols are found.
+ * @param libname The name under which the Pardiso lib can be found, or OSQP_NULL to use a default name (mkl_rt.SHAREDLIBEXT).
+ * @return Zero on success, nonzero on failure.
+ */
+c_int lh_load_pardiso(const char* libname);
+
+/**
+ * Unloads the loaded Pardiso shared library.
+ * @return Zero on success, nonzero on failure.
+ */
+c_int lh_unload_pardiso();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*PARADISOLOADER_H*/
diff --git a/lin_sys/direct/qdldl/CMakeLists.txt b/lin_sys/direct/qdldl/CMakeLists.txt
new file mode 100644
index 0000000..d78c4a3
--- /dev/null
+++ b/lin_sys/direct/qdldl/CMakeLists.txt
@@ -0,0 +1,41 @@
+# Add qdldl
+add_subdirectory(qdldl_sources)
+
+
+if(NOT DEFINED EMBEDDED)
+set(
+    amd_sources
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/include/amd_internal.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/include/amd.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/include/SuiteSparse_config.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_1.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_2.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_aat.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_control.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_defaults.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_info.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_order.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_post_tree.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_postorder.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_preprocess.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/amd_valid.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/src/SuiteSparse_config.c
+)
+endif()
+
+
+set(qdldl_interface_includes
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    ${CMAKE_CURRENT_SOURCE_DIR}/amd/include
+    ${CMAKE_CURRENT_SOURCE_DIR}/qdldl_sources/include
+)
+
+set(qdldl_interface_src
+    ${amd_sources}
+    ${CMAKE_CURRENT_SOURCE_DIR}/qdldl_interface.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/qdldl_interface.c
+)
+
+# Create object library for linear system solver interface
+add_library(linsys_qdldl OBJECT ${qdldl_interface_src})
+target_include_directories(linsys_qdldl PRIVATE ${qdldl_interface_includes} ${PROJECT_SOURCE_DIR}/include)
diff --git a/lin_sys/direct/qdldl/amd/LICENSE b/lin_sys/direct/qdldl/amd/LICENSE
new file mode 100644
index 0000000..36de6d8
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/LICENSE
@@ -0,0 +1,36 @@
+AMD, Copyright (c), 1996-2015, Timothy A. Davis,
+Patrick R. Amestoy, and Iain S. Duff.  All Rights Reserved.
+
+Availability:
+
+    http://www.suitesparse.com
+
+-------------------------------------------------------------------------------
+AMD License: BSD 3-clause:
+-------------------------------------------------------------------------------
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in the
+          documentation and/or other materials provided with the distribution.
+        * Neither the name of the organizations to which the authors are
+          affiliated, 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 HOLDERS BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+    DAMAGE.
+
+
diff --git a/lin_sys/direct/qdldl/amd/include/SuiteSparse_config.h b/lin_sys/direct/qdldl/amd/include/SuiteSparse_config.h
new file mode 100644
index 0000000..abeb7b9
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/include/SuiteSparse_config.h
@@ -0,0 +1,246 @@
+/* ========================================================================== */
+/* === SuiteSparse_config =================================================== */
+/* ========================================================================== */
+
+/* Configuration file for SuiteSparse: a Suite of Sparse matrix packages
+ * (AMD, COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, and others).
+ *
+ * SuiteSparse_config.h provides the definition of the long integer.  On most
+ * systems, a C program can be compiled in LP64 mode, in which long's and
+ * pointers are both 64-bits, and int's are 32-bits.  Windows 64, however, uses
+ * the LLP64 model, in which int's and long's are 32-bits, and long long's and
+ * pointers are 64-bits.
+ *
+ * SuiteSparse packages that include long integer versions are
+ * intended for the LP64 mode.  However, as a workaround for Windows 64
+ * (and perhaps other systems), the long integer can be redefined.
+ *
+ * If _WIN64 is defined, then the __int64 type is used instead of long.
+ *
+ * The long integer can also be defined at compile time.  For example, this
+ * could be added to SuiteSparse_config.mk:
+ *
+ * CFLAGS = -O -D'SuiteSparse_long=long long' \
+ *  -D'SuiteSparse_long_max=9223372036854775801' -D'SuiteSparse_long_idd="lld"'
+ *
+ * This file defines SuiteSparse_long as either long (on all but _WIN64) or
+ * __int64 on Windows 64.  The intent is that a SuiteSparse_long is always a
+ * 64-bit integer in a 64-bit code.  ptrdiff_t might be a better choice than
+ * long; it is always the same size as a pointer.
+ *
+ * This file also defines the SUITESPARSE_VERSION and related definitions.
+ *
+ * Copyright (c) 2012, Timothy A. Davis.  No licensing restrictions apply
+ * to this file or to the SuiteSparse_config directory.
+ * Author: Timothy A. Davis.
+ */
+
+#ifndef SUITESPARSE_CONFIG_H
+#define SUITESPARSE_CONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "glob_opts.h"
+#include <limits.h>
+#include <stdlib.h>
+
+/* ========================================================================== */
+/* === SuiteSparse_long ===================================================== */
+/* ========================================================================== */
+
+#ifndef SuiteSparse_long
+
+#define SuiteSparse_long long long
+#define SuiteSparse_long_max LONG_MAX
+#define SuiteSparse_long_idd "ld"
+
+#define SuiteSparse_long_id "%" SuiteSparse_long_idd
+#endif
+
+/* ========================================================================== */
+/* === SuiteSparse_config parameters and functions ========================== */
+/* ========================================================================== */
+
+/* SuiteSparse-wide parameters are placed in this struct.  It is meant to be
+   an extern, globally-accessible struct.  It is not meant to be updated
+   frequently by multiple threads.  Rather, if an application needs to modify
+   SuiteSparse_config, it should do it once at the beginning of the application,
+   before multiple threads are launched.
+
+   The intent of these function pointers is that they not be used in your
+   application directly, except to assign them to the desired user-provided
+   functions.  Rather, you should use the
+ */
+
+struct SuiteSparse_config_struct
+{
+    void *(*malloc_func) (size_t) ;             /* pointer to malloc */
+    void *(*realloc_func) (void *, size_t) ;    /* pointer to realloc */
+    void (*free_func) (void *) ;                /* pointer to free */
+#if defined PYTHON
+    void (*printf_func) (const char *, ...) ;    /* pointer to printf (in Python it returns void)*/
+#elif defined R_LANG
+    void (*printf_func) (const char *, ...) ;    /* pointer to printf (in R it returns void)*/
+#else
+    int (*printf_func) (const char *, ...) ;    /* pointer to printf */
+#endif
+    c_float (*hypot_func) (c_float, c_float) ;     /* pointer to hypot */
+    int (*divcomplex_func) (c_float, c_float, c_float, c_float, c_float *, c_float *);
+} ;
+
+extern struct SuiteSparse_config_struct SuiteSparse_config ;
+
+void *SuiteSparse_malloc    /* pointer to allocated block of memory */
+(
+    size_t nitems,          /* number of items to malloc (>=1 is enforced) */
+    size_t size_of_item     /* sizeof each item */
+) ;
+
+void *SuiteSparse_calloc    /* pointer to allocated block of memory */
+(
+    size_t nitems,          /* number of items to calloc (>=1 is enforced) */
+    size_t size_of_item     /* sizeof each item */
+) ;
+
+void *SuiteSparse_realloc   /* pointer to reallocated block of memory, or
+                               to original block if the realloc failed. */
+(
+    size_t nitems_new,      /* new number of items in the object */
+    size_t nitems_old,      /* old number of items in the object */
+    size_t size_of_item,    /* sizeof each item */
+    void *p,                /* old object to reallocate */
+    int *ok                 /* 1 if successful, 0 otherwise */
+) ;
+
+void *SuiteSparse_free      /* always returns NULL */
+(
+    void *p                 /* block to free */
+) ;
+
+void SuiteSparse_tic    /* start the timer */
+(
+    c_float tic [2]      /* output, contents undefined on input */
+) ;
+
+c_float SuiteSparse_toc  /* return time in seconds since last tic */
+(
+    c_float tic [2]      /* input: from last call to SuiteSparse_tic */
+) ;
+
+c_float SuiteSparse_time  /* returns current wall clock time in seconds */
+(
+    void
+) ;
+
+/* returns sqrt (x^2 + y^2), computed reliably */
+c_float SuiteSparse_hypot (c_float x, c_float y) ;
+
+/* complex division of c = a/b */
+int SuiteSparse_divcomplex
+(
+    c_float ar, c_float ai,	/* real and imaginary parts of a */
+    c_float br, c_float bi,	/* real and imaginary parts of b */
+    c_float *cr, c_float *ci	/* real and imaginary parts of c */
+) ;
+
+/* OSQP: disabling this timing code */
+#define NTIMER
+
+/* OSQP: disabling Suitesparse printing */
+#define NPRINT
+
+/* determine which timer to use, if any */
+#ifndef NTIMER
+#ifdef _POSIX_C_SOURCE
+#if    _POSIX_C_SOURCE >= 199309L
+#define SUITESPARSE_TIMER_ENABLED
+#endif
+#endif
+#endif
+
+/* SuiteSparse printf macro */
+#define SUITESPARSE_PRINTF(params) \
+{ \
+    if (SuiteSparse_config.printf_func != NULL) \
+    { \
+        (void) (SuiteSparse_config.printf_func) params ; \
+    } \
+}
+
+/* ========================================================================== */
+/* === SuiteSparse version ================================================== */
+/* ========================================================================== */
+
+/* SuiteSparse is not a package itself, but a collection of packages, some of
+ * which must be used together (UMFPACK requires AMD, CHOLMOD requires AMD,
+ * COLAMD, CAMD, and CCOLAMD, etc).  A version number is provided here for the
+ * collection itself.  The versions of packages within each version of
+ * SuiteSparse are meant to work together.  Combining one package from one
+ * version of SuiteSparse, with another package from another version of
+ * SuiteSparse, may or may not work.
+ *
+ * SuiteSparse contains the following packages:
+ *
+ *  SuiteSparse_config version 4.5.3 (version always the same as SuiteSparse)
+ *  AMD             version 2.4.6
+ *  BTF             version 1.2.6
+ *  CAMD            version 2.4.6
+ *  CCOLAMD         version 2.9.6
+ *  CHOLMOD         version 3.0.11
+ *  COLAMD          version 2.9.6
+ *  CSparse         version 3.1.9
+ *  CXSparse        version 3.1.9
+ *  GPUQREngine     version 1.0.5
+ *  KLU             version 1.3.8
+ *  LDL             version 2.2.6
+ *  RBio            version 2.2.6
+ *  SPQR            version 2.0.7
+ *  SuiteSparse_GPURuntime  version 1.0.5
+ *  UMFPACK         version 5.7.6
+ *  MATLAB_Tools    various packages & M-files
+ *  xerbla          version 1.0.3
+ *
+ * Other package dependencies:
+ *  BLAS            required by CHOLMOD and UMFPACK
+ *  LAPACK          required by CHOLMOD
+ *  METIS 5.1.0     required by CHOLMOD (optional) and KLU (optional)
+ *  CUBLAS, CUDART  NVIDIA libraries required by CHOLMOD and SPQR when
+ *                  they are compiled with GPU acceleration.
+ */
+
+int SuiteSparse_version     /* returns SUITESPARSE_VERSION */
+(
+    /* output, not defined on input.  Not used if NULL.  Returns
+       the three version codes in version [0..2]:
+       version [0] is SUITESPARSE_MAIN_VERSION
+       version [1] is SUITESPARSE_SUB_VERSION
+       version [2] is SUITESPARSE_SUBSUB_VERSION
+       */
+    int version [3]
+) ;
+
+/* Versions prior to 4.2.0 do not have the above function.  The following
+   code fragment will work with any version of SuiteSparse:
+
+   #ifdef SUITESPARSE_HAS_VERSION_FUNCTION
+   v = SuiteSparse_version (NULL) ;
+   #else
+   v = SUITESPARSE_VERSION ;
+   #endif
+*/
+#define SUITESPARSE_HAS_VERSION_FUNCTION
+
+#define SUITESPARSE_DATE "May 4, 2016"
+#define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub))
+#define SUITESPARSE_MAIN_VERSION 4
+#define SUITESPARSE_SUB_VERSION 5
+#define SUITESPARSE_SUBSUB_VERSION 3
+#define SUITESPARSE_VERSION \
+    SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION)
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lin_sys/direct/qdldl/amd/include/amd.h b/lin_sys/direct/qdldl/amd/include/amd.h
new file mode 100644
index 0000000..cd81e6e
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/include/amd.h
@@ -0,0 +1,401 @@
+/* ========================================================================= */
+/* === AMD:  approximate minimum degree ordering =========================== */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD Version 2.4, Copyright (c) 1996-2013 by Timothy A. Davis,             */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* AMD finds a symmetric ordering P of a matrix A so that the Cholesky
+ * factorization of P*A*P' has fewer nonzeros and takes less work than the
+ * Cholesky factorization of A.  If A is not symmetric, then it performs its
+ * ordering on the matrix A+A'.  Two sets of user-callable routines are
+ * provided, one for int integers and the other for SuiteSparse_long integers.
+ *
+ * The method is based on the approximate minimum degree algorithm, discussed
+ * in Amestoy, Davis, and Duff, "An approximate degree ordering algorithm",
+ * SIAM Journal of Matrix Analysis and Applications, vol. 17, no. 4, pp.
+ * 886-905, 1996.  This package can perform both the AMD ordering (with
+ * aggressive absorption), and the AMDBAR ordering (without aggressive
+ * absorption) discussed in the above paper.  This package differs from the
+ * Fortran codes discussed in the paper:
+ *
+ *       (1) it can ignore "dense" rows and columns, leading to faster run times
+ *       (2) it computes the ordering of A+A' if A is not symmetric
+ *       (3) it is followed by a depth-first post-ordering of the assembly tree
+ *           (or supernodal elimination tree)
+ *
+ * For historical reasons, the Fortran versions, amd.f and amdbar.f, have
+ * been left (nearly) unchanged.  They compute the identical ordering as
+ * described in the above paper.
+ */
+
+#ifndef AMD_H
+#define AMD_H
+
+/* make it easy for C++ programs to include AMD */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "SuiteSparse_config.h"
+
+/* get the definition of size_t: */
+#include <stddef.h>
+
+
+int amd_order                  /* returns AMD_OK, AMD_OK_BUT_JUMBLED,
+                                * AMD_INVALID, or AMD_OUT_OF_MEMORY */
+(
+    int n,                     /* A is n-by-n.  n must be >= 0. */
+    const int Ap [ ],          /* column pointers for A, of size n+1 */
+    const int Ai [ ],          /* row indices of A, of size nz = Ap [n] */
+    int P [ ],                 /* output permutation, of size n */
+    c_float Control [ ],        /* input Control settings, of size AMD_CONTROL */
+    c_float Info [ ]            /* output Info statistics, of size AMD_INFO */
+) ;
+
+SuiteSparse_long amd_l_order    /* see above for description of arguments */
+(
+    SuiteSparse_long n,
+    const SuiteSparse_long Ap [ ],
+    const SuiteSparse_long Ai [ ],
+    SuiteSparse_long P [ ],
+    c_float Control [ ],
+    c_float Info [ ]
+) ;
+
+/* Input arguments (not modified):
+ *
+ *       n: the matrix A is n-by-n.
+ *       Ap: an int/SuiteSparse_long array of size n+1, containing column
+ *              pointers of A.
+ *       Ai: an int/SuiteSparse_long array of size nz, containing the row
+ *              indices of A, where nz = Ap [n].
+ *       Control:  a c_float array of size AMD_CONTROL, containing control
+ *           parameters.  Defaults are used if Control is NULL.
+ *
+ * Output arguments (not defined on input):
+ *
+ *       P: an int/SuiteSparse_long array of size n, containing the output
+ *           permutation. If row i is the kth pivot row, then P [k] = i.  In
+ *           MATLAB notation, the reordered matrix is A (P,P).
+ *       Info: a c_float array of size AMD_INFO, containing statistical
+ *           information.  Ignored if Info is NULL.
+ *
+ * On input, the matrix A is stored in column-oriented form.  The row indices
+ * of nonzero entries in column j are stored in Ai [Ap [j] ... Ap [j+1]-1].
+ *
+ * If the row indices appear in ascending order in each column, and there
+ * are no duplicate entries, then amd_order is slightly more efficient in
+ * terms of time and memory usage.  If this condition does not hold, a copy
+ * of the matrix is created (where these conditions do hold), and the copy is
+ * ordered.  This feature is new to v2.0 (v1.2 and earlier required this
+ * condition to hold for the input matrix).
+ *
+ * Row indices must be in the range 0 to
+ * n-1.  Ap [0] must be zero, and thus nz = Ap [n] is the number of nonzeros
+ * in A.  The array Ap is of size n+1, and the array Ai is of size nz = Ap [n].
+ * The matrix does not need to be symmetric, and the diagonal does not need to
+ * be present (if diagonal entries are present, they are ignored except for
+ * the output statistic Info [AMD_NZDIAG]).  The arrays Ai and Ap are not
+ * modified.  This form of the Ap and Ai arrays to represent the nonzero
+ * pattern of the matrix A is the same as that used internally by MATLAB.
+ * If you wish to use a more flexible input structure, please see the
+ * umfpack_*_triplet_to_col routines in the UMFPACK package, at
+ * http://www.suitesparse.com.
+ *
+ * Restrictions:  n >= 0.  Ap [0] = 0.  Ap [j] <= Ap [j+1] for all j in the
+ *       range 0 to n-1.  nz = Ap [n] >= 0.  Ai [0..nz-1] must be in the range 0
+ *       to n-1.  Finally, Ai, Ap, and P must not be NULL.  If any of these
+ *       restrictions are not met, AMD returns AMD_INVALID.
+ *
+ * AMD returns:
+ *
+ *       AMD_OK if the matrix is valid and sufficient memory can be allocated to
+ *           perform the ordering.
+ *
+ *       AMD_OUT_OF_MEMORY if not enough memory can be allocated.
+ *
+ *       AMD_INVALID if the input arguments n, Ap, Ai are invalid, or if P is
+ *           NULL.
+ *
+ *       AMD_OK_BUT_JUMBLED if the matrix had unsorted columns, and/or duplicate
+ *           entries, but was otherwise valid.
+ *
+ * The AMD routine first forms the pattern of the matrix A+A', and then
+ * computes a fill-reducing ordering, P.  If P [k] = i, then row/column i of
+ * the original is the kth pivotal row.  In MATLAB notation, the permuted
+ * matrix is A (P,P), except that 0-based indexing is used instead of the
+ * 1-based indexing in MATLAB.
+ *
+ * The Control array is used to set various parameters for AMD.  If a NULL
+ * pointer is passed, default values are used.  The Control array is not
+ * modified.
+ *
+ *       Control [AMD_DENSE]:  controls the threshold for "dense" rows/columns.
+ *           A dense row/column in A+A' can cause AMD to spend a lot of time in
+ *           ordering the matrix.  If Control [AMD_DENSE] >= 0, rows/columns
+ *           with more than Control [AMD_DENSE] * sqrt (n) entries are ignored
+ *           during the ordering, and placed last in the output order.  The
+ *           default value of Control [AMD_DENSE] is 10.  If negative, no
+ *           rows/columns are treated as "dense".  Rows/columns with 16 or
+ *           fewer off-diagonal entries are never considered "dense".
+ *
+ *       Control [AMD_AGGRESSIVE]: controls whether or not to use aggressive
+ *           absorption, in which a prior element is absorbed into the current
+ *           element if is a subset of the current element, even if it is not
+ *           adjacent to the current pivot element (refer to Amestoy, Davis,
+ *           & Duff, 1996, for more details).  The default value is nonzero,
+ *           which means to perform aggressive absorption.  This nearly always
+ *           leads to a better ordering (because the approximate degrees are
+ *           more accurate) and a lower execution time.  There are cases where
+ *           it can lead to a slightly worse ordering, however.  To turn it off,
+ *           set Control [AMD_AGGRESSIVE] to 0.
+ *
+ *       Control [2..4] are not used in the current version, but may be used in
+ *           future versions.
+ *
+ * The Info array provides statistics about the ordering on output.  If it is
+ * not present, the statistics are not returned.  This is not an error
+ * condition.
+ *
+ *       Info [AMD_STATUS]:  the return value of AMD, either AMD_OK,
+ *           AMD_OK_BUT_JUMBLED, AMD_OUT_OF_MEMORY, or AMD_INVALID.
+ *
+ *       Info [AMD_N]: n, the size of the input matrix
+ *
+ *       Info [AMD_NZ]: the number of nonzeros in A, nz = Ap [n]
+ *
+ *       Info [AMD_SYMMETRY]:  the symmetry of the matrix A.  It is the number
+ *           of "matched" off-diagonal entries divided by the total number of
+ *           off-diagonal entries.  An entry A(i,j) is matched if A(j,i) is also
+ *           an entry, for any pair (i,j) for which i != j.  In MATLAB notation,
+ *                S = spones (A) ;
+ *                B = tril (S, -1) + triu (S, 1) ;
+ *                symmetry = nnz (B & B') / nnz (B) ;
+ *
+ *       Info [AMD_NZDIAG]: the number of entries on the diagonal of A.
+ *
+ *       Info [AMD_NZ_A_PLUS_AT]:  the number of nonzeros in A+A', excluding the
+ *           diagonal.  If A is perfectly symmetric (Info [AMD_SYMMETRY] = 1)
+ *           with a fully nonzero diagonal, then Info [AMD_NZ_A_PLUS_AT] = nz-n
+ *           (the smallest possible value).  If A is perfectly unsymmetric
+ *           (Info [AMD_SYMMETRY] = 0, for an upper triangular matrix, for
+ *           example) with no diagonal, then Info [AMD_NZ_A_PLUS_AT] = 2*nz
+ *           (the largest possible value).
+ *
+ *       Info [AMD_NDENSE]: the number of "dense" rows/columns of A+A' that were
+ *           removed from A prior to ordering.  These are placed last in the
+ *           output order P.
+ *
+ *       Info [AMD_MEMORY]: the amount of memory used by AMD, in bytes.  In the
+ *           current version, this is 1.2 * Info  [AMD_NZ_A_PLUS_AT] + 9*n
+ *           times the size of an integer.  This is at most 2.4nz + 9n.  This
+ *           excludes the size of the input arguments Ai, Ap, and P, which have
+ *           a total size of nz + 2*n + 1 integers.
+ *
+ *       Info [AMD_NCMPA]: the number of garbage collections performed.
+ *
+ *       Info [AMD_LNZ]: the number of nonzeros in L (excluding the diagonal).
+ *           This is a slight upper bound because mass elimination is combined
+ *           with the approximate degree update.  It is a rough upper bound if
+ *           there are many "dense" rows/columns.  The rest of the statistics,
+ *           below, are also slight or rough upper bounds, for the same reasons.
+ *           The post-ordering of the assembly tree might also not exactly
+ *           correspond to a true elimination tree postordering.
+ *
+ *       Info [AMD_NDIV]: the number of divide operations for a subsequent LDL'
+ *           or LU factorization of the permuted matrix A (P,P).
+ *
+ *       Info [AMD_NMULTSUBS_LDL]:  the number of multiply-subtract pairs for a
+ *           subsequent LDL' factorization of A (P,P).
+ *
+ *       Info [AMD_NMULTSUBS_LU]:  the number of multiply-subtract pairs for a
+ *           subsequent LU factorization of A (P,P), assuming that no numerical
+ *           pivoting is required.
+ *
+ *       Info [AMD_DMAX]:  the maximum number of nonzeros in any column of L,
+ *           including the diagonal.
+ *
+ *       Info [14..19] are not used in the current version, but may be used in
+ *           future versions.
+ */
+
+/* ------------------------------------------------------------------------- */
+/* direct interface to AMD */
+/* ------------------------------------------------------------------------- */
+
+/* amd_2 is the primary AMD ordering routine.  It is not meant to be
+ * user-callable because of its restrictive inputs and because it destroys
+ * the user's input matrix.  It does not check its inputs for errors, either.
+ * However, if you can work with these restrictions it can be faster than
+ * amd_order and use less memory (assuming that you can create your own copy
+ * of the matrix for AMD to destroy).  Refer to AMD/Source/amd_2.c for a
+ * description of each parameter. */
+
+void amd_2
+(
+    int n,
+    int Pe [ ],
+    int Iw [ ],
+    int Len [ ],
+    int iwlen,
+    int pfree,
+    int Nv [ ],
+    int Next [ ],
+    int Last [ ],
+    int Head [ ],
+    int Elen [ ],
+    int Degree [ ],
+    int W [ ],
+    c_float Control [ ],
+    c_float Info [ ]
+) ;
+
+void amd_l2
+(
+    SuiteSparse_long n,
+    SuiteSparse_long Pe [ ],
+    SuiteSparse_long Iw [ ],
+    SuiteSparse_long Len [ ],
+    SuiteSparse_long iwlen,
+    SuiteSparse_long pfree,
+    SuiteSparse_long Nv [ ],
+    SuiteSparse_long Next [ ],
+    SuiteSparse_long Last [ ],
+    SuiteSparse_long Head [ ],
+    SuiteSparse_long Elen [ ],
+    SuiteSparse_long Degree [ ],
+    SuiteSparse_long W [ ],
+    c_float Control [ ],
+    c_float Info [ ]
+) ;
+
+/* ------------------------------------------------------------------------- */
+/* amd_valid */
+/* ------------------------------------------------------------------------- */
+
+/* Returns AMD_OK or AMD_OK_BUT_JUMBLED if the matrix is valid as input to
+ * amd_order; the latter is returned if the matrix has unsorted and/or
+ * duplicate row indices in one or more columns.  Returns AMD_INVALID if the
+ * matrix cannot be passed to amd_order.  For amd_order, the matrix must also
+ * be square.  The first two arguments are the number of rows and the number
+ * of columns of the matrix.  For its use in AMD, these must both equal n.
+ *
+ * NOTE: this routine returned TRUE/FALSE in v1.2 and earlier.
+ */
+
+int amd_valid
+(
+    int n_row,                 /* # of rows */
+    int n_col,                 /* # of columns */
+    const int Ap [ ],          /* column pointers, of size n_col+1 */
+    const int Ai [ ]           /* row indices, of size Ap [n_col] */
+) ;
+
+SuiteSparse_long amd_l_valid
+(
+    SuiteSparse_long n_row,
+    SuiteSparse_long n_col,
+    const SuiteSparse_long Ap [ ],
+    const SuiteSparse_long Ai [ ]
+) ;
+
+/* ------------------------------------------------------------------------- */
+/* AMD memory manager and printf routines */
+/* ------------------------------------------------------------------------- */
+
+    /* moved to SuiteSparse_config.c */
+
+/* ------------------------------------------------------------------------- */
+/* AMD Control and Info arrays */
+/* ------------------------------------------------------------------------- */
+
+/* amd_defaults:  sets the default control settings */
+void amd_defaults   (c_float Control [ ]) ;
+void amd_l_defaults (c_float Control [ ]) ;
+
+/* amd_control: prints the control settings */
+void amd_control    (c_float Control [ ]) ;
+void amd_l_control  (c_float Control [ ]) ;
+
+/* amd_info: prints the statistics */
+void amd_info       (c_float Info [ ]) ;
+void amd_l_info     (c_float Info [ ]) ;
+
+#define AMD_CONTROL 5          /* size of Control array */
+#define AMD_INFO 20            /* size of Info array */
+
+/* contents of Control */
+#define AMD_DENSE 0            /* "dense" if degree > Control [0] * sqrt (n) */
+#define AMD_AGGRESSIVE 1    /* do aggressive absorption if Control [1] != 0 */
+
+/* default Control settings */
+#define AMD_DEFAULT_DENSE 10.0          /* default "dense" degree 10*sqrt(n) */
+#define AMD_DEFAULT_AGGRESSIVE 1    /* do aggressive absorption by default */
+
+/* contents of Info */
+#define AMD_STATUS 0           /* return value of amd_order and amd_l_order */
+#define AMD_N 1                /* A is n-by-n */
+#define AMD_NZ 2      /* number of nonzeros in A */
+#define AMD_SYMMETRY 3         /* symmetry of pattern (1 is sym., 0 is unsym.) */
+#define AMD_NZDIAG 4           /* # of entries on diagonal */
+#define AMD_NZ_A_PLUS_AT 5  /* nz in A+A' */
+#define AMD_NDENSE 6           /* number of "dense" rows/columns in A */
+#define AMD_MEMORY 7           /* amount of memory used by AMD */
+#define AMD_NCMPA 8            /* number of garbage collections in AMD */
+#define AMD_LNZ 9     /* approx. nz in L, excluding the diagonal */
+#define AMD_NDIV 10            /* number of fl. point divides for LU and LDL' */
+#define AMD_NMULTSUBS_LDL 11 /* number of fl. point (*,-) pairs for LDL' */
+#define AMD_NMULTSUBS_LU 12  /* number of fl. point (*,-) pairs for LU */
+#define AMD_DMAX 13             /* max nz. in any column of L, incl. diagonal */
+
+/* ------------------------------------------------------------------------- */
+/* return values of AMD */
+/* ------------------------------------------------------------------------- */
+
+#define AMD_OK 0           /* success */
+#define AMD_OUT_OF_MEMORY -1        /* malloc failed, or problem too large */
+#define AMD_INVALID -2              /* input arguments are not valid */
+#define AMD_OK_BUT_JUMBLED 1        /* input matrix is OK for amd_order, but
+    * columns were not sorted, and/or duplicate entries were present.  AMD had
+    * to do extra work before ordering the matrix.  This is a warning, not an
+    * error.  */
+
+/* ========================================================================== */
+/* === AMD version ========================================================== */
+/* ========================================================================== */
+
+/* AMD Version 1.2 and later include the following definitions.
+ * As an example, to test if the version you are using is 1.2 or later:
+ *
+ * #ifdef AMD_VERSION
+ *       if (AMD_VERSION >= AMD_VERSION_CODE (1,2)) ...
+ * #endif
+ *
+ * This also works during compile-time:
+ *
+ *       #if defined(AMD_VERSION) && (AMD_VERSION >= AMD_VERSION_CODE (1,2))
+ *           printf ("This is version 1.2 or later\n") ;
+ *       #else
+ *           printf ("This is an early version\n") ;
+ *       #endif
+ *
+ * Versions 1.1 and earlier of AMD do not include a #define'd version number.
+ */
+
+#define AMD_DATE "May 4, 2016"
+#define AMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub))
+#define AMD_MAIN_VERSION 2
+#define AMD_SUB_VERSION 4
+#define AMD_SUBSUB_VERSION 6
+#define AMD_VERSION AMD_VERSION_CODE(AMD_MAIN_VERSION,AMD_SUB_VERSION)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lin_sys/direct/qdldl/amd/include/amd_internal.h b/lin_sys/direct/qdldl/amd/include/amd_internal.h
new file mode 100644
index 0000000..7b7cea1
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/include/amd_internal.h
@@ -0,0 +1,328 @@
+/* ========================================================================= */
+/* === amd_internal.h ====================================================== */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,                                      */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* This file is for internal use in AMD itself, and does not normally need to
+ * be included in user code (it is included in UMFPACK, however).   All others
+ * should use amd.h instead.
+ */
+
+/* ========================================================================= */
+/* === NDEBUG ============================================================== */
+/* ========================================================================= */
+
+/*
+ * Turning on debugging takes some work (see below).   If you do not edit this
+ * file, then debugging is always turned off, regardless of whether or not
+ * -DNDEBUG is specified in your compiler options.
+ *
+ * If AMD is being compiled as a mexFunction, then MATLAB is defined,
+ * and mxAssert is used instead of assert.  If debugging is not enabled, no
+ * MATLAB include files or functions are used.  Thus, the AMD library libamd.a
+ * can be safely used in either a stand-alone C program or in another
+ * mexFunction, without any change.
+ */
+
+/*
+    AMD will be exceedingly slow when running in debug mode.  The next three
+    lines ensure that debugging is turned off.
+*/
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+
+#include "glob_opts.h"
+
+/*
+    To enable debugging, uncomment the following line:
+#undef NDEBUG
+*/
+
+/* ------------------------------------------------------------------------- */
+/* ANSI include files */
+/* ------------------------------------------------------------------------- */
+
+/* from stdlib.h:  size_t, malloc, free, realloc, and calloc */
+#include <stdlib.h>
+
+#if !defined(NPRINT) || !defined(NDEBUG)
+/* from stdio.h:  printf.  Not included if NPRINT is defined at compile time.
+ * fopen and fscanf are used when debugging. */
+#include <stdio.h>
+#endif
+
+/* from limits.h:  INT_MAX and LONG_MAX */
+#include <limits.h>
+
+/* from math.h: sqrt */
+#include <math.h>
+
+/* ------------------------------------------------------------------------- */
+/* MATLAB include files (only if being used in or via MATLAB) */
+/* ------------------------------------------------------------------------- */
+
+#ifdef MATLAB
+#include "matrix.h"
+#include "mex.h"
+#endif
+
+/* ------------------------------------------------------------------------- */
+/* basic definitions */
+/* ------------------------------------------------------------------------- */
+
+#ifdef FLIP
+#undef FLIP
+#endif
+
+#ifdef MAX
+#undef MAX
+#endif
+
+#ifdef MIN
+#undef MIN
+#endif
+
+#ifdef EMPTY
+#undef EMPTY
+#endif
+
+#ifdef GLOBAL
+#undef GLOBAL
+#endif
+
+#ifdef PRIVATE
+#undef PRIVATE
+#endif
+
+/* FLIP is a "negation about -1", and is used to mark an integer i that is
+ * normally non-negative.  FLIP (EMPTY) is EMPTY.  FLIP of a number > EMPTY
+ * is negative, and FLIP of a number < EMTPY is positive.  FLIP (FLIP (i)) = i
+ * for all integers i.  UNFLIP (i) is >= EMPTY. */
+#define EMPTY (-1)
+#define FLIP(i) (-(i)-2)
+#define UNFLIP(i) ((i < EMPTY) ? FLIP (i) : (i))
+
+/* for integer MAX/MIN, or for c_floats when we don't care how NaN's behave: */
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+/* logical expression of p implies q: */
+#define IMPLIES(p,q) (!(p) || (q))
+
+/* Note that the IBM RS 6000 xlc predefines TRUE and FALSE in <types.h>. */
+/* The Compaq Alpha also predefines TRUE and FALSE. */
+#ifdef TRUE
+#undef TRUE
+#endif
+#ifdef FALSE
+#undef FALSE
+#endif
+
+#define TRUE (1)
+#define FALSE (0)
+#define PRIVATE static
+#define GLOBAL
+#define EMPTY (-1)
+
+/* Note that Linux's gcc 2.96 defines NULL as ((void *) 0), but other */
+/* compilers (even gcc 2.95.2 on Solaris) define NULL as 0 or (0).  We */
+/* need to use the ANSI standard value of 0. */
+#ifdef NULL
+#undef NULL
+#endif
+
+#define NULL 0
+
+/* largest value of size_t */
+#ifndef SIZE_T_MAX
+#ifdef SIZE_MAX
+/* C99 only */
+#define SIZE_T_MAX SIZE_MAX
+#else
+#define SIZE_T_MAX ((size_t) (-1))
+#endif
+#endif
+
+/* ------------------------------------------------------------------------- */
+/* integer type for AMD: int or SuiteSparse_long */
+/* ------------------------------------------------------------------------- */
+
+#include "amd.h"
+
+#if defined (DLONG) || defined (ZLONG)
+
+#define Int SuiteSparse_long
+#define ID  SuiteSparse_long_id
+#define Int_MAX SuiteSparse_long_max
+
+#define AMD_order amd_l_order
+#define AMD_defaults amd_l_defaults
+#define AMD_control amd_l_control
+#define AMD_info amd_l_info
+#define AMD_1 amd_l1
+#define AMD_2 amd_l2
+#define AMD_valid amd_l_valid
+#define AMD_aat amd_l_aat
+#define AMD_postorder amd_l_postorder
+#define AMD_post_tree amd_l_post_tree
+#define AMD_dump amd_l_dump
+#define AMD_debug amd_l_debug
+#define AMD_debug_init amd_l_debug_init
+#define AMD_preprocess amd_l_preprocess
+
+#else
+
+#define Int int
+#define ID "%d"
+#define Int_MAX INT_MAX
+
+#define AMD_order amd_order
+#define AMD_defaults amd_defaults
+#define AMD_control amd_control
+#define AMD_info amd_info
+#define AMD_1 amd_1
+#define AMD_2 amd_2
+#define AMD_valid amd_valid
+#define AMD_aat amd_aat
+#define AMD_postorder amd_postorder
+#define AMD_post_tree amd_post_tree
+#define AMD_dump amd_dump
+#define AMD_debug amd_debug
+#define AMD_debug_init amd_debug_init
+#define AMD_preprocess amd_preprocess
+
+#endif
+
+/* ------------------------------------------------------------------------- */
+/* AMD routine definitions (not user-callable) */
+/* ------------------------------------------------------------------------- */
+
+GLOBAL size_t AMD_aat
+(
+    Int n,
+    const Int Ap [ ],
+    const Int Ai [ ],
+    Int Len [ ],
+    Int Tp [ ],
+    c_float Info [ ]
+) ;
+
+GLOBAL void AMD_1
+(
+    Int n,
+    const Int Ap [ ],
+    const Int Ai [ ],
+    Int P [ ],
+    Int Pinv [ ],
+    Int Len [ ],
+    Int slen,
+    Int S [ ],
+    c_float Control [ ],
+    c_float Info [ ]
+) ;
+
+GLOBAL void AMD_postorder
+(
+    Int nn,
+    Int Parent [ ],
+    Int Npiv [ ],
+    Int Fsize [ ],
+    Int Order [ ],
+    Int Child [ ],
+    Int Sibling [ ],
+    Int Stack [ ]
+) ;
+
+GLOBAL Int AMD_post_tree
+(
+    Int root,
+    Int k,
+    Int Child [ ],
+    const Int Sibling [ ],
+    Int Order [ ],
+    Int Stack [ ]
+#ifndef NDEBUG
+    , Int nn
+#endif
+) ;
+
+GLOBAL void AMD_preprocess
+(
+    Int n,
+    const Int Ap [ ],
+    const Int Ai [ ],
+    Int Rp [ ],
+    Int Ri [ ],
+    Int W [ ],
+    Int Flag [ ]
+) ;
+
+/* ------------------------------------------------------------------------- */
+/* debugging definitions */
+/* ------------------------------------------------------------------------- */
+
+#ifndef NDEBUG
+
+/* from assert.h:  assert macro */
+#include <assert.h>
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+EXTERN Int AMD_debug ;
+
+GLOBAL void AMD_debug_init ( char *s ) ;
+
+GLOBAL void AMD_dump
+(
+    Int n,
+    Int Pe [ ],
+    Int Iw [ ],
+    Int Len [ ],
+    Int iwlen,
+    Int pfree,
+    Int Nv [ ],
+    Int Next [ ],
+    Int Last [ ],
+    Int Head [ ],
+    Int Elen [ ],
+    Int Degree [ ],
+    Int W [ ],
+    Int nel
+) ;
+
+#ifdef ASSERT
+#undef ASSERT
+#endif
+
+/* Use mxAssert if AMD is compiled into a mexFunction */
+#ifdef MATLAB
+#define ASSERT(expression) (mxAssert ((expression), ""))
+#else
+#define ASSERT(expression) (assert (expression))
+#endif
+
+#define AMD_DEBUG0(params) { SUITESPARSE_PRINTF (params) ; }
+#define AMD_DEBUG1(params) { if (AMD_debug >= 1) SUITESPARSE_PRINTF (params) ; }
+#define AMD_DEBUG2(params) { if (AMD_debug >= 2) SUITESPARSE_PRINTF (params) ; }
+#define AMD_DEBUG3(params) { if (AMD_debug >= 3) SUITESPARSE_PRINTF (params) ; }
+#define AMD_DEBUG4(params) { if (AMD_debug >= 4) SUITESPARSE_PRINTF (params) ; }
+
+#else
+
+/* no debugging */
+#define ASSERT(expression)
+#define AMD_DEBUG0(params)
+#define AMD_DEBUG1(params)
+#define AMD_DEBUG2(params)
+#define AMD_DEBUG3(params)
+#define AMD_DEBUG4(params)
+
+#endif
diff --git a/lin_sys/direct/qdldl/amd/src/SuiteSparse_config.c b/lin_sys/direct/qdldl/amd/src/SuiteSparse_config.c
new file mode 100644
index 0000000..cebdff0
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/SuiteSparse_config.c
@@ -0,0 +1,407 @@
+/* ========================================================================== */
+/* === SuiteSparse_config =================================================== */
+/* ========================================================================== */
+
+/* SuiteSparse configuration : memory manager and printf functions. */
+
+/* Copyright (c) 2013, Timothy A. Davis.  No licensing restrictions
+ * apply to this file or to the SuiteSparse_config directory.
+ * Author: Timothy A. Davis.
+ */
+
+// Include OSQP Global options for memory management
+#include "glob_opts.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+
+
+#ifndef NPRINT
+#include <stdio.h>
+#endif
+
+#ifdef MATLAB
+#include "mex.h"
+#include "matrix.h"
+#endif
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#include "SuiteSparse_config.h"
+
+/* -------------------------------------------------------------------------- */
+/* SuiteSparse_config : a global extern struct */
+/* -------------------------------------------------------------------------- */
+
+/* The SuiteSparse_config struct is available to all SuiteSparse functions and
+    to all applications that use those functions.  It must be modified with
+    care, particularly in a multithreaded context.  Normally, the application
+    will initialize this object once, via SuiteSparse_start, possibily followed
+    by application-specific modifications if the applications wants to use
+    alternative memory manager functions.
+
+    The user can redefine these global pointers at run-time to change the
+    memory manager and printf function used by SuiteSparse.
+
+    If -DNMALLOC is defined at compile-time, then no memory-manager is
+    specified.  You must define them at run-time, after calling
+    SuiteSparse_start.
+
+    If -DPRINT is defined a compile time, then printf is disabled, and
+    SuiteSparse will not use printf.
+ */
+
+struct SuiteSparse_config_struct SuiteSparse_config =
+{
+    // Memory allocation from glob_opts.h in OSQP
+    c_malloc, c_realloc, c_free,
+    // Ignore printing
+    NULL,
+    SuiteSparse_hypot,
+    SuiteSparse_divcomplex
+
+} ;
+
+
+/* -------------------------------------------------------------------------- */
+/* SuiteSparse_malloc: malloc wrapper */
+/* -------------------------------------------------------------------------- */
+
+void *SuiteSparse_malloc    /* pointer to allocated block of memory */
+(
+    size_t nitems,          /* number of items to malloc */
+    size_t size_of_item     /* sizeof each item */
+)
+{
+    void *p ;
+    size_t size ;
+    if (nitems < 1) nitems = 1 ;
+    if (size_of_item < 1) size_of_item = 1 ;
+    size = nitems * size_of_item  ;
+
+    if (size != ((c_float) nitems) * size_of_item)
+    {
+        /* size_t overflow */
+        p = NULL ;
+    }
+    else
+    {
+        p = (void *) (SuiteSparse_config.malloc_func) (size) ;
+        // p = (void *) c_malloc(size) ;
+    }
+    return (p) ;
+}
+
+
+
+/* -------------------------------------------------------------------------- */
+/* SuiteSparse_realloc: realloc wrapper */
+/* -------------------------------------------------------------------------- */
+
+/* If p is non-NULL on input, it points to a previously allocated object of
+   size nitems_old * size_of_item.  The object is reallocated to be of size
+   nitems_new * size_of_item.  If p is NULL on input, then a new object of that
+   size is allocated.  On success, a pointer to the new object is returned,
+   and ok is returned as 1.  If the allocation fails, ok is set to 0 and a
+   pointer to the old (unmodified) object is returned.
+ */
+
+void *SuiteSparse_realloc   /* pointer to reallocated block of memory, or
+                               to original block if the realloc failed. */
+(
+    size_t nitems_new,      /* new number of items in the object */
+    size_t nitems_old,      /* old number of items in the object */
+    size_t size_of_item,    /* sizeof each item */
+    void *p,                /* old object to reallocate */
+    int *ok                 /* 1 if successful, 0 otherwise */
+)
+{
+    size_t size ;
+    if (nitems_old < 1) nitems_old = 1 ;
+    if (nitems_new < 1) nitems_new = 1 ;
+    if (size_of_item < 1) size_of_item = 1 ;
+    size = nitems_new * size_of_item  ;
+
+    if (size != ((c_float) nitems_new) * size_of_item)
+    {
+        /* size_t overflow */
+        (*ok) = 0 ;
+    }
+    else if (p == NULL)
+    {
+        /* a fresh object is being allocated */
+        p = SuiteSparse_malloc (nitems_new, size_of_item) ;
+        (*ok) = (p != NULL) ;
+    }
+    else if (nitems_old == nitems_new)
+    {
+        /* the object does not change; do nothing */
+        (*ok) = 1 ;
+    }
+    else
+    {
+        /* change the size of the object from nitems_old to nitems_new */
+        void *pnew ;
+        // pnew = (void *) (SuiteSparse_config.realloc_func) (p, size) ;
+        pnew = (void *) c_realloc (p, size) ;
+        if (pnew == NULL)
+        {
+            if (nitems_new < nitems_old)
+            {
+                /* the attempt to reduce the size of the block failed, but
+                   the old block is unchanged.  So pretend to succeed. */
+                (*ok) = 1 ;
+            }
+            else
+            {
+                /* out of memory */
+                (*ok) = 0 ;
+            }
+        }
+        else
+        {
+            /* success */
+            p = pnew ;
+            (*ok) = 1 ;
+        }
+    }
+    return (p) ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* SuiteSparse_free: free wrapper */
+/* -------------------------------------------------------------------------- */
+
+void *SuiteSparse_free      /* always returns NULL */
+(
+    void *p                 /* block to free */
+)
+{
+    if (p)
+    {
+        (SuiteSparse_config.free_func) (p) ;
+        // c_free(p) ;
+    }
+    return (NULL) ;
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* SuiteSparse_tic: return current wall clock time */
+/* -------------------------------------------------------------------------- */
+
+/* Returns the number of seconds (tic [0]) and nanoseconds (tic [1]) since some
+ * unspecified but fixed time in the past.  If no timer is installed, zero is
+ * returned.  A scalar c_float precision value for 'tic' could be used, but this
+ * might cause loss of precision because clock_getttime returns the time from
+ * some distant time in the past.  Thus, an array of size 2 is used.
+ *
+ * The timer is enabled by default.  To disable the timer, compile with
+ * -DNTIMER.  If enabled on a POSIX C 1993 system, the timer requires linking
+ * with the -lrt library.
+ *
+ * example:
+ *
+ *      c_float tic [2], r, s, t ;
+ *      SuiteSparse_tic (tic) ;     // start the timer
+ *      // do some work A
+ *      t = SuiteSparse_toc (tic) ; // t is time for work A, in seconds
+ *      // do some work B
+ *      s = SuiteSparse_toc (tic) ; // s is time for work A and B, in seconds
+ *      SuiteSparse_tic (tic) ;     // restart the timer
+ *      // do some work C
+ *      r = SuiteSparse_toc (tic) ; // s is time for work C, in seconds
+ *
+ * A c_float array of size 2 is used so that this routine can be more easily
+ * ported to non-POSIX systems.  The caller does not rely on the POSIX
+ * <time.h> include file.
+ */
+
+#ifdef SUITESPARSE_TIMER_ENABLED
+
+#include <time.h>
+
+void SuiteSparse_tic
+(
+    c_float tic [2]      /* output, contents undefined on input */
+)
+{
+    /* POSIX C 1993 timer, requires -librt */
+    struct timespec t ;
+    clock_gettime (CLOCK_MONOTONIC, &t) ;
+    tic [0] = (c_float) (t.tv_sec) ;
+    tic [1] = (c_float) (t.tv_nsec) ;
+}
+
+#else
+
+void SuiteSparse_tic
+(
+    c_float tic [2]      /* output, contents undefined on input */
+)
+{
+    /* no timer installed */
+    tic [0] = 0 ;
+    tic [1] = 0 ;
+}
+
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+/* SuiteSparse_toc: return time since last tic */
+/* -------------------------------------------------------------------------- */
+
+/* Assuming SuiteSparse_tic is accurate to the nanosecond, this function is
+ * accurate down to the nanosecond for 2^53 nanoseconds since the last call to
+ * SuiteSparse_tic, which is sufficient for SuiteSparse (about 104 days).  If
+ * additional accuracy is required, the caller can use two calls to
+ * SuiteSparse_tic and do the calculations differently.
+ */
+
+c_float SuiteSparse_toc  /* returns time in seconds since last tic */
+(
+    c_float tic [2]  /* input, not modified from last call to SuiteSparse_tic */
+)
+{
+    c_float toc [2] ;
+    SuiteSparse_tic (toc) ;
+    return ((toc [0] - tic [0]) + 1e-9 * (toc [1] - tic [1])) ;
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* SuiteSparse_time: return current wallclock time in seconds */
+/* -------------------------------------------------------------------------- */
+
+/* This function might not be accurate down to the nanosecond. */
+
+c_float SuiteSparse_time  /* returns current wall clock time in seconds */
+(
+    void
+)
+{
+    c_float toc [2] ;
+    SuiteSparse_tic (toc) ;
+    return (toc [0] + 1e-9 * toc [1]) ;
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* SuiteSparse_version: return the current version of SuiteSparse */
+/* -------------------------------------------------------------------------- */
+
+int SuiteSparse_version
+(
+    int version [3]
+)
+{
+    if (version != NULL)
+    {
+        version [0] = SUITESPARSE_MAIN_VERSION ;
+        version [1] = SUITESPARSE_SUB_VERSION ;
+        version [2] = SUITESPARSE_SUBSUB_VERSION ;
+    }
+    return (SUITESPARSE_VERSION) ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* SuiteSparse_hypot */
+/* -------------------------------------------------------------------------- */
+
+/* There is an equivalent routine called hypot in <math.h>, which conforms
+ * to ANSI C99.  However, SuiteSparse does not assume that ANSI C99 is
+ * available.  You can use the ANSI C99 hypot routine with:
+ *
+ *      #include <math.h>
+ *i     SuiteSparse_config.hypot_func = hypot ;
+ *
+ * Default value of the SuiteSparse_config.hypot_func pointer is
+ * SuiteSparse_hypot, defined below.
+ *
+ * s = hypot (x,y) computes s = sqrt (x*x + y*y) but does so more accurately.
+ * The NaN cases for the c_float relops x >= y and x+y == x are safely ignored.
+ *
+ * Source: Algorithm 312, "Absolute value and square root of a complex number,"
+ * P. Friedland, Comm. ACM, vol 10, no 10, October 1967, page 665.
+ */
+
+c_float SuiteSparse_hypot (c_float x, c_float y)
+{
+    c_float s, r ;
+    x = fabs (x) ;
+    y = fabs (y) ;
+    if (x >= y)
+    {
+        if (x + y == x)
+        {
+            s = x ;
+        }
+        else
+        {
+            r = y / x ;
+            s = x * sqrt (1.0 + r*r) ;
+        }
+    }
+    else
+    {
+        if (y + x == y)
+        {
+            s = y ;
+        }
+        else
+        {
+            r = x / y ;
+            s = y * sqrt (1.0 + r*r) ;
+        }
+    }
+    return (s) ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* SuiteSparse_divcomplex */
+/* -------------------------------------------------------------------------- */
+
+/* c = a/b where c, a, and b are complex.  The real and imaginary parts are
+ * passed as separate arguments to this routine.  The NaN case is ignored
+ * for the c_float relop br >= bi.  Returns 1 if the denominator is zero,
+ * 0 otherwise.
+ *
+ * This uses ACM Algo 116, by R. L. Smith, 1962, which tries to avoid
+ * underflow and overflow.
+ *
+ * c can be the same variable as a or b.
+ *
+ * Default value of the SuiteSparse_config.divcomplex_func pointer is
+ * SuiteSparse_divcomplex.
+ */
+
+int SuiteSparse_divcomplex
+(
+    c_float ar, c_float ai,       /* real and imaginary parts of a */
+    c_float br, c_float bi,       /* real and imaginary parts of b */
+    c_float *cr, c_float *ci      /* real and imaginary parts of c */
+)
+{
+    c_float tr, ti, r, den ;
+    if (fabs (br) >= fabs (bi))
+    {
+        r = bi / br ;
+        den = br + r * bi ;
+        tr = (ar + ai * r) / den ;
+        ti = (ai - ar * r) / den ;
+    }
+    else
+    {
+        r = br / bi ;
+        den = r * br + bi ;
+        tr = (ar * r + ai) / den ;
+        ti = (ai * r - ar) / den ;
+    }
+    *cr = tr ;
+    *ci = ti ;
+    return (den == 0.) ;
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_1.c b/lin_sys/direct/qdldl/amd/src/amd_1.c
new file mode 100644
index 0000000..06c8be6
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_1.c
@@ -0,0 +1,180 @@
+/* ========================================================================= */
+/* === AMD_1 =============================================================== */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* AMD_1: Construct A+A' for a sparse matrix A and perform the AMD ordering.
+ *
+ * The n-by-n sparse matrix A can be unsymmetric.  It is stored in MATLAB-style
+ * compressed-column form, with sorted row indices in each column, and no
+ * duplicate entries.  Diagonal entries may be present, but they are ignored.
+ * Row indices of column j of A are stored in Ai [Ap [j] ... Ap [j+1]-1].
+ * Ap [0] must be zero, and nz = Ap [n] is the number of entries in A.  The
+ * size of the matrix, n, must be greater than or equal to zero.
+ *
+ * This routine must be preceded by a call to AMD_aat, which computes the
+ * number of entries in each row/column in A+A', excluding the diagonal.
+ * Len [j], on input, is the number of entries in row/column j of A+A'.  This
+ * routine constructs the matrix A+A' and then calls AMD_2.  No error checking
+ * is performed (this was done in AMD_valid).
+ */
+
+#include "amd_internal.h"
+
+GLOBAL void AMD_1
+(
+    Int n,		/* n > 0 */
+    const Int Ap [ ],	/* input of size n+1, not modified */
+    const Int Ai [ ],	/* input of size nz = Ap [n], not modified */
+    Int P [ ],		/* size n output permutation */
+    Int Pinv [ ],	/* size n output inverse permutation */
+    Int Len [ ],	/* size n input, undefined on output */
+    Int slen,		/* slen >= sum (Len [0..n-1]) + 7n,
+			 * ideally slen = 1.2 * sum (Len) + 8n */
+    Int S [ ],		/* size slen workspace */
+    c_float Control [ ],	/* input array of size AMD_CONTROL */
+    c_float Info [ ]	/* output array of size AMD_INFO */
+)
+{
+    Int i, j, k, p, pfree, iwlen, pj, p1, p2, pj2, *Iw, *Pe, *Nv, *Head,
+	*Elen, *Degree, *s, *W, *Sp, *Tp ;
+
+    /* --------------------------------------------------------------------- */
+    /* construct the matrix for AMD_2 */
+    /* --------------------------------------------------------------------- */
+
+    ASSERT (n > 0) ;
+
+    iwlen = slen - 6*n ;
+    s = S ;
+    Pe = s ;	    s += n ;
+    Nv = s ;	    s += n ;
+    Head = s ;	    s += n ;
+    Elen = s ;	    s += n ;
+    Degree = s ;    s += n ;
+    W = s ;	    s += n ;
+    Iw = s ;	    s += iwlen ;
+
+    ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ;
+
+    /* construct the pointers for A+A' */
+    Sp = Nv ;			/* use Nv and W as workspace for Sp and Tp [ */
+    Tp = W ;
+    pfree = 0 ;
+    for (j = 0 ; j < n ; j++)
+    {
+	Pe [j] = pfree ;
+	Sp [j] = pfree ;
+	pfree += Len [j] ;
+    }
+
+    /* Note that this restriction on iwlen is slightly more restrictive than
+     * what is strictly required in AMD_2.  AMD_2 can operate with no elbow
+     * room at all, but it will be very slow.  For better performance, at
+     * least size-n elbow room is enforced. */
+    ASSERT (iwlen >= pfree + n) ;
+
+#ifndef NDEBUG
+    for (p = 0 ; p < iwlen ; p++) Iw [p] = EMPTY ;
+#endif
+
+    for (k = 0 ; k < n ; k++)
+    {
+	AMD_DEBUG1 (("Construct row/column k= "ID" of A+A'\n", k))  ;
+	p1 = Ap [k] ;
+	p2 = Ap [k+1] ;
+
+	/* construct A+A' */
+	for (p = p1 ; p < p2 ; )
+	{
+	    /* scan the upper triangular part of A */
+	    j = Ai [p] ;
+	    ASSERT (j >= 0 && j < n) ;
+	    if (j < k)
+	    {
+		/* entry A (j,k) in the strictly upper triangular part */
+		ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ;
+		ASSERT (Sp [k] < (k == n-1 ? pfree : Pe [k+1])) ;
+		Iw [Sp [j]++] = k ;
+		Iw [Sp [k]++] = j ;
+		p++ ;
+	    }
+	    else if (j == k)
+	    {
+		/* skip the diagonal */
+		p++ ;
+		break ;
+	    }
+	    else /* j > k */
+	    {
+		/* first entry below the diagonal */
+		break ;
+	    }
+	    /* scan lower triangular part of A, in column j until reaching
+	     * row k.  Start where last scan left off. */
+	    ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ;
+	    pj2 = Ap [j+1] ;
+	    for (pj = Tp [j] ; pj < pj2 ; )
+	    {
+		i = Ai [pj] ;
+		ASSERT (i >= 0 && i < n) ;
+		if (i < k)
+		{
+		    /* A (i,j) is only in the lower part, not in upper */
+		    ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ;
+		    ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ;
+		    Iw [Sp [i]++] = j ;
+		    Iw [Sp [j]++] = i ;
+		    pj++ ;
+		}
+		else if (i == k)
+		{
+		    /* entry A (k,j) in lower part and A (j,k) in upper */
+		    pj++ ;
+		    break ;
+		}
+		else /* i > k */
+		{
+		    /* consider this entry later, when k advances to i */
+		    break ;
+		}
+	    }
+	    Tp [j] = pj ;
+	}
+	Tp [k] = p ;
+    }
+
+    /* clean up, for remaining mismatched entries */
+    for (j = 0 ; j < n ; j++)
+    {
+	for (pj = Tp [j] ; pj < Ap [j+1] ; pj++)
+	{
+	    i = Ai [pj] ;
+	    ASSERT (i >= 0 && i < n) ;
+	    /* A (i,j) is only in the lower part, not in upper */
+	    ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ;
+	    ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ;
+	    Iw [Sp [i]++] = j ;
+	    Iw [Sp [j]++] = i ;
+	}
+    }
+
+#ifndef NDEBUG
+    for (j = 0 ; j < n-1 ; j++) ASSERT (Sp [j] == Pe [j+1]) ;
+    ASSERT (Sp [n-1] == pfree) ;
+#endif
+
+    /* Tp and Sp no longer needed ] */
+
+    /* --------------------------------------------------------------------- */
+    /* order the matrix */
+    /* --------------------------------------------------------------------- */
+
+    AMD_2 (n, Pe, Iw, Len, iwlen, pfree,
+	Nv, Pinv, P, Head, Elen, Degree, W, Control, Info) ;
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_2.c b/lin_sys/direct/qdldl/amd/src/amd_2.c
new file mode 100644
index 0000000..fe06e75
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_2.c
@@ -0,0 +1,1842 @@
+/* ========================================================================= */
+/* === AMD_2 =============================================================== */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* AMD_2:  performs the AMD ordering on a symmetric sparse matrix A, followed
+ * by a postordering (via depth-first search) of the assembly tree using the
+ * AMD_postorder routine.
+ */
+
+#include "amd_internal.h"
+
+/* ========================================================================= */
+/* === clear_flag ========================================================== */
+/* ========================================================================= */
+
+static Int clear_flag (Int wflg, Int wbig, Int W [ ], Int n)
+{
+    Int x ;
+    if (wflg < 2 || wflg >= wbig)
+    {
+	for (x = 0 ; x < n ; x++)
+	{
+	    if (W [x] != 0) W [x] = 1 ;
+	}
+	wflg = 2 ;
+    }
+    /*  at this point, W [0..n-1] < wflg holds */
+    return (wflg) ;
+}
+
+
+/* ========================================================================= */
+/* === AMD_2 =============================================================== */
+/* ========================================================================= */
+
+GLOBAL void AMD_2
+(
+    Int n,		/* A is n-by-n, where n > 0 */
+    Int Pe [ ],		/* Pe [0..n-1]: index in Iw of row i on input */
+    Int Iw [ ],		/* workspace of size iwlen. Iw [0..pfree-1]
+			 * holds the matrix on input */
+    Int Len [ ],	/* Len [0..n-1]: length for row/column i on input */
+    Int iwlen,		/* length of Iw. iwlen >= pfree + n */
+    Int pfree,		/* Iw [pfree ... iwlen-1] is empty on input */
+
+    /* 7 size-n workspaces, not defined on input: */
+    Int Nv [ ],		/* the size of each supernode on output */
+    Int Next [ ],	/* the output inverse permutation */
+    Int Last [ ],	/* the output permutation */
+    Int Head [ ],
+    Int Elen [ ],	/* the size columns of L for each supernode */
+    Int Degree [ ],
+    Int W [ ],
+
+    /* control parameters and output statistics */
+    c_float Control [ ],	/* array of size AMD_CONTROL */
+    c_float Info [ ]	/* array of size AMD_INFO */
+)
+{
+
+/*
+ * Given a representation of the nonzero pattern of a symmetric matrix, A,
+ * (excluding the diagonal) perform an approximate minimum (UMFPACK/MA38-style)
+ * degree ordering to compute a pivot order such that the introduction of
+ * nonzeros (fill-in) in the Cholesky factors A = LL' is kept low.  At each
+ * step, the pivot selected is the one with the minimum UMFAPACK/MA38-style
+ * upper-bound on the external degree.  This routine can optionally perform
+ * aggresive absorption (as done by MC47B in the Harwell Subroutine
+ * Library).
+ *
+ * The approximate degree algorithm implemented here is the symmetric analog of
+ * the degree update algorithm in MA38 and UMFPACK (the Unsymmetric-pattern
+ * MultiFrontal PACKage, both by Davis and Duff).  The routine is based on the
+ * MA27 minimum degree ordering algorithm by Iain Duff and John Reid.
+ *
+ * This routine is a translation of the original AMDBAR and MC47B routines,
+ * in Fortran, with the following modifications:
+ *
+ * (1) dense rows/columns are removed prior to ordering the matrix, and placed
+ *	last in the output order.  The presence of a dense row/column can
+ *	increase the ordering time by up to O(n^2), unless they are removed
+ *	prior to ordering.
+ *
+ * (2) the minimum degree ordering is followed by a postordering (depth-first
+ *	search) of the assembly tree.  Note that mass elimination (discussed
+ *	below) combined with the approximate degree update can lead to the mass
+ *	elimination of nodes with lower exact degree than the current pivot
+ *	element.  No additional fill-in is caused in the representation of the
+ *	Schur complement.  The mass-eliminated nodes merge with the current
+ *	pivot element.  They are ordered prior to the current pivot element.
+ *	Because they can have lower exact degree than the current element, the
+ *	merger of two or more of these nodes in the current pivot element can
+ *	lead to a single element that is not a "fundamental supernode".  The
+ *	diagonal block can have zeros in it.  Thus, the assembly tree used here
+ *	is not guaranteed to be the precise supernodal elemination tree (with
+ *	"funadmental" supernodes), and the postordering performed by this
+ *	routine is not guaranteed to be a precise postordering of the
+ *	elimination tree.
+ *
+ * (3) input parameters are added, to control aggressive absorption and the
+ *	detection of "dense" rows/columns of A.
+ *
+ * (4) additional statistical information is returned, such as the number of
+ *	nonzeros in L, and the flop counts for subsequent LDL' and LU
+ *	factorizations.  These are slight upper bounds, because of the mass
+ *	elimination issue discussed above.
+ *
+ * (5) additional routines are added to interface this routine to MATLAB
+ *	to provide a simple C-callable user-interface, to check inputs for
+ *	errors, compute the symmetry of the pattern of A and the number of
+ *	nonzeros in each row/column of A+A', to compute the pattern of A+A',
+ *	to perform the assembly tree postordering, and to provide debugging
+ *	ouput.  Many of these functions are also provided by the Fortran
+ *	Harwell Subroutine Library routine MC47A.
+ *
+ * (6) both int and SuiteSparse_long versions are provided.  In the
+ *      descriptions below and integer is and int or SuiteSparse_long depending
+ *      on which version is being used.
+
+ **********************************************************************
+ ***** CAUTION:  ARGUMENTS ARE NOT CHECKED FOR ERRORS ON INPUT.  ******
+ **********************************************************************
+ ** If you want error checking, a more versatile input format, and a **
+ ** simpler user interface, use amd_order or amd_l_order instead.    **
+ ** This routine is not meant to be user-callable.                   **
+ **********************************************************************
+
+ * ----------------------------------------------------------------------------
+ * References:
+ * ----------------------------------------------------------------------------
+ *
+ *  [1] Timothy A. Davis and Iain Duff, "An unsymmetric-pattern multifrontal
+ *	method for sparse LU factorization", SIAM J. Matrix Analysis and
+ *	Applications, vol. 18, no. 1, pp. 140-158.  Discusses UMFPACK / MA38,
+ *	which first introduced the approximate minimum degree used by this
+ *	routine.
+ *
+ *  [2] Patrick Amestoy, Timothy A. Davis, and Iain S. Duff, "An approximate
+ *	minimum degree ordering algorithm," SIAM J. Matrix Analysis and
+ *	Applications, vol. 17, no. 4, pp. 886-905, 1996.  Discusses AMDBAR and
+ *	MC47B, which are the Fortran versions of this routine.
+ *
+ *  [3] Alan George and Joseph Liu, "The evolution of the minimum degree
+ *	ordering algorithm," SIAM Review, vol. 31, no. 1, pp. 1-19, 1989.
+ *	We list below the features mentioned in that paper that this code
+ *	includes:
+ *
+ *	mass elimination:
+ *	    Yes.  MA27 relied on supervariable detection for mass elimination.
+ *
+ *	indistinguishable nodes:
+ *	    Yes (we call these "supervariables").  This was also in the MA27
+ *	    code - although we modified the method of detecting them (the
+ *	    previous hash was the true degree, which we no longer keep track
+ *	    of).  A supervariable is a set of rows with identical nonzero
+ *	    pattern.  All variables in a supervariable are eliminated together.
+ *	    Each supervariable has as its numerical name that of one of its
+ *	    variables (its principal variable).
+ *
+ *	quotient graph representation:
+ *	    Yes.  We use the term "element" for the cliques formed during
+ *	    elimination.  This was also in the MA27 code.  The algorithm can
+ *	    operate in place, but it will work more efficiently if given some
+ *	    "elbow room."
+ *
+ *	element absorption:
+ *	    Yes.  This was also in the MA27 code.
+ *
+ *	external degree:
+ *	    Yes.  The MA27 code was based on the true degree.
+ *
+ *	incomplete degree update and multiple elimination:
+ *	    No.  This was not in MA27, either.  Our method of degree update
+ *	    within MC47B is element-based, not variable-based.  It is thus
+ *	    not well-suited for use with incomplete degree update or multiple
+ *	    elimination.
+ *
+ * Authors, and Copyright (C) 2004 by:
+ * Timothy A. Davis, Patrick Amestoy, Iain S. Duff, John K. Reid.
+ *
+ * Acknowledgements: This work (and the UMFPACK package) was supported by the
+ * National Science Foundation (ASC-9111263, DMS-9223088, and CCR-0203270).
+ * The UMFPACK/MA38 approximate degree update algorithm, the unsymmetric analog
+ * which forms the basis of AMD, was developed while Tim Davis was supported by
+ * CERFACS (Toulouse, France) in a post-doctoral position.  This C version, and
+ * the etree postorder, were written while Tim Davis was on sabbatical at
+ * Stanford University and Lawrence Berkeley National Laboratory.
+
+ * ----------------------------------------------------------------------------
+ * INPUT ARGUMENTS (unaltered):
+ * ----------------------------------------------------------------------------
+
+ * n:  The matrix order.  Restriction:  n >= 1.
+ *
+ * iwlen:  The size of the Iw array.  On input, the matrix is stored in
+ *	Iw [0..pfree-1].  However, Iw [0..iwlen-1] should be slightly larger
+ *	than what is required to hold the matrix, at least iwlen >= pfree + n.
+ *	Otherwise, excessive compressions will take place.  The recommended
+ *	value of iwlen is 1.2 * pfree + n, which is the value used in the
+ *	user-callable interface to this routine (amd_order.c).  The algorithm
+ *	will not run at all if iwlen < pfree.  Restriction: iwlen >= pfree + n.
+ *	Note that this is slightly more restrictive than the actual minimum
+ *	(iwlen >= pfree), but AMD_2 will be very slow with no elbow room.
+ *	Thus, this routine enforces a bare minimum elbow room of size n.
+ *
+ * pfree: On input the tail end of the array, Iw [pfree..iwlen-1], is empty,
+ *	and the matrix is stored in Iw [0..pfree-1].  During execution,
+ *	additional data is placed in Iw, and pfree is modified so that
+ *	Iw [pfree..iwlen-1] is always the unused part of Iw.
+ *
+ * Control:  A c_float array of size AMD_CONTROL containing input parameters
+ *	that affect how the ordering is computed.  If NULL, then default
+ *	settings are used.
+ *
+ *	Control [AMD_DENSE] is used to determine whether or not a given input
+ *	row is "dense".  A row is "dense" if the number of entries in the row
+ *	exceeds Control [AMD_DENSE] times sqrt (n), except that rows with 16 or
+ *	fewer entries are never considered "dense".  To turn off the detection
+ *	of dense rows, set Control [AMD_DENSE] to a negative number, or to a
+ *	number larger than sqrt (n).  The default value of Control [AMD_DENSE]
+ *	is AMD_DEFAULT_DENSE, which is defined in amd.h as 10.
+ *
+ *	Control [AMD_AGGRESSIVE] is used to determine whether or not aggressive
+ *	absorption is to be performed.  If nonzero, then aggressive absorption
+ *	is performed (this is the default).
+
+ * ----------------------------------------------------------------------------
+ * INPUT/OUPUT ARGUMENTS:
+ * ----------------------------------------------------------------------------
+ *
+ * Pe:  An integer array of size n.  On input, Pe [i] is the index in Iw of
+ *	the start of row i.  Pe [i] is ignored if row i has no off-diagonal
+ *	entries.  Thus Pe [i] must be in the range 0 to pfree-1 for non-empty
+ *	rows.
+ *
+ *	During execution, it is used for both supervariables and elements:
+ *
+ *	Principal supervariable i:  index into Iw of the description of
+ *	    supervariable i.  A supervariable represents one or more rows of
+ *	    the matrix with identical nonzero pattern.  In this case,
+ *	    Pe [i] >= 0.
+ *
+ *	Non-principal supervariable i:  if i has been absorbed into another
+ *	    supervariable j, then Pe [i] = FLIP (j), where FLIP (j) is defined
+ *	    as (-(j)-2).  Row j has the same pattern as row i.  Note that j
+ *	    might later be absorbed into another supervariable j2, in which
+ *	    case Pe [i] is still FLIP (j), and Pe [j] = FLIP (j2) which is
+ *	    < EMPTY, where EMPTY is defined as (-1) in amd_internal.h.
+ *
+ *	Unabsorbed element e:  the index into Iw of the description of element
+ *	    e, if e has not yet been absorbed by a subsequent element.  Element
+ *	    e is created when the supervariable of the same name is selected as
+ *	    the pivot.  In this case, Pe [i] >= 0.
+ *
+ *	Absorbed element e:  if element e is absorbed into element e2, then
+ *	    Pe [e] = FLIP (e2).  This occurs when the pattern of e (which we
+ *	    refer to as Le) is found to be a subset of the pattern of e2 (that
+ *	    is, Le2).  In this case, Pe [i] < EMPTY.  If element e is "null"
+ *	    (it has no nonzeros outside its pivot block), then Pe [e] = EMPTY,
+ *	    and e is the root of an assembly subtree (or the whole tree if
+ *	    there is just one such root).
+ *
+ *	Dense variable i:  if i is "dense", then Pe [i] = EMPTY.
+ *
+ *	On output, Pe holds the assembly tree/forest, which implicitly
+ *	represents a pivot order with identical fill-in as the actual order
+ *	(via a depth-first search of the tree), as follows.  If Nv [i] > 0,
+ *	then i represents a node in the assembly tree, and the parent of i is
+ *	Pe [i], or EMPTY if i is a root.  If Nv [i] = 0, then (i, Pe [i])
+ *	represents an edge in a subtree, the root of which is a node in the
+ *	assembly tree.  Note that i refers to a row/column in the original
+ *	matrix, not the permuted matrix.
+ *
+ * Info:  A c_float array of size AMD_INFO.  If present, (that is, not NULL),
+ *	then statistics about the ordering are returned in the Info array.
+ *	See amd.h for a description.
+
+ * ----------------------------------------------------------------------------
+ * INPUT/MODIFIED (undefined on output):
+ * ----------------------------------------------------------------------------
+ *
+ * Len:  An integer array of size n.  On input, Len [i] holds the number of
+ *	entries in row i of the matrix, excluding the diagonal.  The contents
+ *	of Len are undefined on output.
+ *
+ * Iw:  An integer array of size iwlen.  On input, Iw [0..pfree-1] holds the
+ *	description of each row i in the matrix.  The matrix must be symmetric,
+ *	and both upper and lower triangular parts must be present.  The
+ *	diagonal must not be present.  Row i is held as follows:
+ *
+ *	    Len [i]:  the length of the row i data structure in the Iw array.
+ *	    Iw [Pe [i] ... Pe [i] + Len [i] - 1]:
+ *		the list of column indices for nonzeros in row i (simple
+ *		supervariables), excluding the diagonal.  All supervariables
+ *		start with one row/column each (supervariable i is just row i).
+ *		If Len [i] is zero on input, then Pe [i] is ignored on input.
+ *
+ *	    Note that the rows need not be in any particular order, and there
+ *	    may be empty space between the rows.
+ *
+ *	During execution, the supervariable i experiences fill-in.  This is
+ *	represented by placing in i a list of the elements that cause fill-in
+ *	in supervariable i:
+ *
+ *	    Len [i]:  the length of supervariable i in the Iw array.
+ *	    Iw [Pe [i] ... Pe [i] + Elen [i] - 1]:
+ *		the list of elements that contain i.  This list is kept short
+ *		by removing absorbed elements.
+ *	    Iw [Pe [i] + Elen [i] ... Pe [i] + Len [i] - 1]:
+ *		the list of supervariables in i.  This list is kept short by
+ *		removing nonprincipal variables, and any entry j that is also
+ *		contained in at least one of the elements (j in Le) in the list
+ *		for i (e in row i).
+ *
+ *	When supervariable i is selected as pivot, we create an element e of
+ *	the same name (e=i):
+ *
+ *	    Len [e]:  the length of element e in the Iw array.
+ *	    Iw [Pe [e] ... Pe [e] + Len [e] - 1]:
+ *		the list of supervariables in element e.
+ *
+ *	An element represents the fill-in that occurs when supervariable i is
+ *	selected as pivot (which represents the selection of row i and all
+ *	non-principal variables whose principal variable is i).  We use the
+ *	term Le to denote the set of all supervariables in element e.  Absorbed
+ *	supervariables and elements are pruned from these lists when
+ *	computationally convenient.
+ *
+ *  CAUTION:  THE INPUT MATRIX IS OVERWRITTEN DURING COMPUTATION.
+ *  The contents of Iw are undefined on output.
+
+ * ----------------------------------------------------------------------------
+ * OUTPUT (need not be set on input):
+ * ----------------------------------------------------------------------------
+ *
+ * Nv:  An integer array of size n.  During execution, ABS (Nv [i]) is equal to
+ *	the number of rows that are represented by the principal supervariable
+ *	i.  If i is a nonprincipal or dense variable, then Nv [i] = 0.
+ *	Initially, Nv [i] = 1 for all i.  Nv [i] < 0 signifies that i is a
+ *	principal variable in the pattern Lme of the current pivot element me.
+ *	After element me is constructed, Nv [i] is set back to a positive
+ *	value.
+ *
+ *	On output, Nv [i] holds the number of pivots represented by super
+ *	row/column i of the original matrix, or Nv [i] = 0 for non-principal
+ *	rows/columns.  Note that i refers to a row/column in the original
+ *	matrix, not the permuted matrix.
+ *
+ * Elen:  An integer array of size n.  See the description of Iw above.  At the
+ *	start of execution, Elen [i] is set to zero for all rows i.  During
+ *	execution, Elen [i] is the number of elements in the list for
+ *	supervariable i.  When e becomes an element, Elen [e] = FLIP (esize) is
+ *	set, where esize is the size of the element (the number of pivots, plus
+ *	the number of nonpivotal entries).  Thus Elen [e] < EMPTY.
+ *	Elen (i) = EMPTY set when variable i becomes nonprincipal.
+ *
+ *	For variables, Elen (i) >= EMPTY holds until just before the
+ *	postordering and permutation vectors are computed.  For elements,
+ *	Elen [e] < EMPTY holds.
+ *
+ *	On output, Elen [i] is the degree of the row/column in the Cholesky
+ *	factorization of the permuted matrix, corresponding to the original row
+ *	i, if i is a super row/column.  It is equal to EMPTY if i is
+ *	non-principal.  Note that i refers to a row/column in the original
+ *	matrix, not the permuted matrix.
+ *
+ *	Note that the contents of Elen on output differ from the Fortran
+ *	version (Elen holds the inverse permutation in the Fortran version,
+ *	which is instead returned in the Next array in this C version,
+ *	described below).
+ *
+ * Last: In a degree list, Last [i] is the supervariable preceding i, or EMPTY
+ *	if i is the head of the list.  In a hash bucket, Last [i] is the hash
+ *	key for i.
+ *
+ *	Last [Head [hash]] is also used as the head of a hash bucket if
+ *	Head [hash] contains a degree list (see the description of Head,
+ *	below).
+ *
+ *	On output, Last [0..n-1] holds the permutation.  That is, if
+ *	i = Last [k], then row i is the kth pivot row (where k ranges from 0 to
+ *	n-1).  Row Last [k] of A is the kth row in the permuted matrix, PAP'.
+ *
+ * Next: Next [i] is the supervariable following i in a link list, or EMPTY if
+ *	i is the last in the list.  Used for two kinds of lists:  degree lists
+ *	and hash buckets (a supervariable can be in only one kind of list at a
+ *	time).
+ *
+ *	On output Next [0..n-1] holds the inverse permutation. 	That is, if
+ *	k = Next [i], then row i is the kth pivot row. Row i of A appears as
+ *	the (Next[i])-th row in the permuted matrix, PAP'.
+ *
+ *	Note that the contents of Next on output differ from the Fortran
+ *	version (Next is undefined on output in the Fortran version).
+
+ * ----------------------------------------------------------------------------
+ * LOCAL WORKSPACE (not input or output - used only during execution):
+ * ----------------------------------------------------------------------------
+ *
+ * Degree:  An integer array of size n.  If i is a supervariable, then
+ *	Degree [i] holds the current approximation of the external degree of
+ *	row i (an upper bound).  The external degree is the number of nonzeros
+ *	in row i, minus ABS (Nv [i]), the diagonal part.  The bound is equal to
+ *	the exact external degree if Elen [i] is less than or equal to two.
+ *
+ *	We also use the term "external degree" for elements e to refer to
+ *	|Le \ Lme|.  If e is an element, then Degree [e] is |Le|, which is the
+ *	degree of the off-diagonal part of the element e (not including the
+ *	diagonal part).
+ *
+ * Head:   An integer array of size n.  Head is used for degree lists.
+ *	Head [deg] is the first supervariable in a degree list.  All
+ *	supervariables i in a degree list Head [deg] have the same approximate
+ *	degree, namely, deg = Degree [i].  If the list Head [deg] is empty then
+ *	Head [deg] = EMPTY.
+ *
+ *	During supervariable detection Head [hash] also serves as a pointer to
+ *	a hash bucket.  If Head [hash] >= 0, there is a degree list of degree
+ *	hash.  The hash bucket head pointer is Last [Head [hash]].  If
+ *	Head [hash] = EMPTY, then the degree list and hash bucket are both
+ *	empty.  If Head [hash] < EMPTY, then the degree list is empty, and
+ *	FLIP (Head [hash]) is the head of the hash bucket.  After supervariable
+ *	detection is complete, all hash buckets are empty, and the
+ *	(Last [Head [hash]] = EMPTY) condition is restored for the non-empty
+ *	degree lists.
+ *
+ * W:  An integer array of size n.  The flag array W determines the status of
+ *	elements and variables, and the external degree of elements.
+ *
+ *	for elements:
+ *	    if W [e] = 0, then the element e is absorbed.
+ *	    if W [e] >= wflg, then W [e] - wflg is the size of the set
+ *		|Le \ Lme|, in terms of nonzeros (the sum of ABS (Nv [i]) for
+ *		each principal variable i that is both in the pattern of
+ *		element e and NOT in the pattern of the current pivot element,
+ *		me).
+ *	    if wflg > W [e] > 0, then e is not absorbed and has not yet been
+ *		seen in the scan of the element lists in the computation of
+ *		|Le\Lme| in Scan 1 below.
+ *
+ *	for variables:
+ *	    during supervariable detection, if W [j] != wflg then j is
+ *	    not in the pattern of variable i.
+ *
+ *	The W array is initialized by setting W [i] = 1 for all i, and by
+ *	setting wflg = 2.  It is reinitialized if wflg becomes too large (to
+ *	ensure that wflg+n does not cause integer overflow).
+
+ * ----------------------------------------------------------------------------
+ * LOCAL INTEGERS:
+ * ----------------------------------------------------------------------------
+ */
+
+    Int deg, degme, dext, lemax, e, elenme, eln, i, ilast, inext, j,
+	jlast, jnext, k, knt1, knt2, knt3, lenj, ln, me, mindeg, nel, nleft,
+	nvi, nvj, nvpiv, slenme, wbig, we, wflg, wnvi, ok, ndense, ncmpa,
+	dense, aggressive ;
+
+    unsigned Int hash ;	    /* unsigned, so that hash % n is well defined.*/
+
+/*
+ * deg:		the degree of a variable or element
+ * degme:	size, |Lme|, of the current element, me (= Degree [me])
+ * dext:	external degree, |Le \ Lme|, of some element e
+ * lemax:	largest |Le| seen so far (called dmax in Fortran version)
+ * e:		an element
+ * elenme:	the length, Elen [me], of element list of pivotal variable
+ * eln:		the length, Elen [...], of an element list
+ * hash:	the computed value of the hash function
+ * i:		a supervariable
+ * ilast:	the entry in a link list preceding i
+ * inext:	the entry in a link list following i
+ * j:		a supervariable
+ * jlast:	the entry in a link list preceding j
+ * jnext:	the entry in a link list, or path, following j
+ * k:		the pivot order of an element or variable
+ * knt1:	loop counter used during element construction
+ * knt2:	loop counter used during element construction
+ * knt3:	loop counter used during compression
+ * lenj:	Len [j]
+ * ln:		length of a supervariable list
+ * me:		current supervariable being eliminated, and the current
+ *		    element created by eliminating that supervariable
+ * mindeg:	current minimum degree
+ * nel:		number of pivots selected so far
+ * nleft:	n - nel, the number of nonpivotal rows/columns remaining
+ * nvi:		the number of variables in a supervariable i (= Nv [i])
+ * nvj:		the number of variables in a supervariable j (= Nv [j])
+ * nvpiv:	number of pivots in current element
+ * slenme:	number of variables in variable list of pivotal variable
+ * wbig:	= (INT_MAX - n) for the int version, (SuiteSparse_long_max - n)
+ *                  for the SuiteSparse_long version.  wflg is not allowed to
+ *                  be >= wbig.
+ * we:		W [e]
+ * wflg:	used for flagging the W array.  See description of Iw.
+ * wnvi:	wflg - Nv [i]
+ * x:		either a supervariable or an element
+ *
+ * ok:		true if supervariable j can be absorbed into i
+ * ndense:	number of "dense" rows/columns
+ * dense:	rows/columns with initial degree > dense are considered "dense"
+ * aggressive:	true if aggressive absorption is being performed
+ * ncmpa:	number of garbage collections
+
+ * ----------------------------------------------------------------------------
+ * LOCAL DOUBLES, used for statistical output only (except for alpha):
+ * ----------------------------------------------------------------------------
+ */
+
+    c_float f, r, ndiv, s, nms_lu, nms_ldl, dmax, alpha, lnz, lnzme ;
+
+/*
+ * f:		nvpiv
+ * r:		degme + nvpiv
+ * ndiv:	number of divisions for LU or LDL' factorizations
+ * s:		number of multiply-subtract pairs for LU factorization, for the
+ *		    current element me
+ * nms_lu	number of multiply-subtract pairs for LU factorization
+ * nms_ldl	number of multiply-subtract pairs for LDL' factorization
+ * dmax:	the largest number of entries in any column of L, including the
+ *		    diagonal
+ * alpha:	"dense" degree ratio
+ * lnz:		the number of nonzeros in L (excluding the diagonal)
+ * lnzme:	the number of nonzeros in L (excl. the diagonal) for the
+ *		    current element me
+
+ * ----------------------------------------------------------------------------
+ * LOCAL "POINTERS" (indices into the Iw array)
+ * ----------------------------------------------------------------------------
+*/
+
+    Int p, p1, p2, p3, p4, pdst, pend, pj, pme, pme1, pme2, pn, psrc ;
+
+/*
+ * Any parameter (Pe [...] or pfree) or local variable starting with "p" (for
+ * Pointer) is an index into Iw, and all indices into Iw use variables starting
+ * with "p."  The only exception to this rule is the iwlen input argument.
+ *
+ * p:           pointer into lots of things
+ * p1:          Pe [i] for some variable i (start of element list)
+ * p2:          Pe [i] + Elen [i] -  1 for some variable i
+ * p3:          index of first supervariable in clean list
+ * p4:
+ * pdst:        destination pointer, for compression
+ * pend:        end of memory to compress
+ * pj:          pointer into an element or variable
+ * pme:         pointer into the current element (pme1...pme2)
+ * pme1:        the current element, me, is stored in Iw [pme1...pme2]
+ * pme2:        the end of the current element
+ * pn:          pointer into a "clean" variable, also used to compress
+ * psrc:        source pointer, for compression
+*/
+
+/* ========================================================================= */
+/*  INITIALIZATIONS */
+/* ========================================================================= */
+
+    /* Note that this restriction on iwlen is slightly more restrictive than
+     * what is actually required in AMD_2.  AMD_2 can operate with no elbow
+     * room at all, but it will be slow.  For better performance, at least
+     * size-n elbow room is enforced. */
+    ASSERT (iwlen >= pfree + n) ;
+    ASSERT (n > 0) ;
+
+    /* initialize output statistics */
+    lnz = 0 ;
+    ndiv = 0 ;
+    nms_lu = 0 ;
+    nms_ldl = 0 ;
+    dmax = 1 ;
+    me = EMPTY ;
+
+    mindeg = 0 ;
+    ncmpa = 0 ;
+    nel = 0 ;
+    lemax = 0 ;
+
+    /* get control parameters */
+    if (Control != (c_float *) NULL)
+    {
+	alpha = Control [AMD_DENSE] ;
+	aggressive = (Control [AMD_AGGRESSIVE] != 0) ;
+    }
+    else
+    {
+	alpha = AMD_DEFAULT_DENSE ;
+	aggressive = AMD_DEFAULT_AGGRESSIVE ;
+    }
+    /* Note: if alpha is NaN, this is undefined: */
+    if (alpha < 0)
+    {
+	/* only remove completely dense rows/columns */
+	dense = n-2 ;
+    }
+    else
+    {
+	dense = (Int) (alpha * sqrt ((c_float) n)) ;
+    }
+    dense = MAX (16, dense) ;
+    dense = MIN (n,  dense) ;
+    AMD_DEBUG1 (("\n\nAMD (debug), alpha %g, aggr. "ID"\n",
+	alpha, aggressive)) ;
+
+    for (i = 0 ; i < n ; i++)
+    {
+	Last [i] = EMPTY ;
+	Head [i] = EMPTY ;
+	Next [i] = EMPTY ;
+	/* if separate Hhead array is used for hash buckets: *
+	Hhead [i] = EMPTY ;
+	*/
+	Nv [i] = 1 ;
+	W [i] = 1 ;
+	Elen [i] = 0 ;
+	Degree [i] = Len [i] ;
+    }
+
+#ifndef NDEBUG
+    AMD_DEBUG1 (("\n======Nel "ID" initial\n", nel)) ;
+    AMD_dump (n, Pe, Iw, Len, iwlen, pfree, Nv, Next, Last,
+		Head, Elen, Degree, W, -1) ;
+#endif
+
+    /* initialize wflg */
+    wbig = Int_MAX - n ;
+    wflg = clear_flag (0, wbig, W, n) ;
+
+    /* --------------------------------------------------------------------- */
+    /* initialize degree lists and eliminate dense and empty rows */
+    /* --------------------------------------------------------------------- */
+
+    ndense = 0 ;
+
+    for (i = 0 ; i < n ; i++)
+    {
+	deg = Degree [i] ;
+	ASSERT (deg >= 0 && deg < n) ;
+	if (deg == 0)
+	{
+
+	    /* -------------------------------------------------------------
+	     * we have a variable that can be eliminated at once because
+	     * there is no off-diagonal non-zero in its row.  Note that
+	     * Nv [i] = 1 for an empty variable i.  It is treated just
+	     * the same as an eliminated element i.
+	     * ------------------------------------------------------------- */
+
+	    Elen [i] = FLIP (1) ;
+	    nel++ ;
+	    Pe [i] = EMPTY ;
+	    W [i] = 0 ;
+
+	}
+	else if (deg > dense)
+	{
+
+	    /* -------------------------------------------------------------
+	     * Dense variables are not treated as elements, but as unordered,
+	     * non-principal variables that have no parent.  They do not take
+	     * part in the postorder, since Nv [i] = 0.  Note that the Fortran
+	     * version does not have this option.
+	     * ------------------------------------------------------------- */
+
+	    AMD_DEBUG1 (("Dense node "ID" degree "ID"\n", i, deg)) ;
+	    ndense++ ;
+	    Nv [i] = 0 ;		/* do not postorder this node */
+	    Elen [i] = EMPTY ;
+	    nel++ ;
+	    Pe [i] = EMPTY ;
+
+	}
+	else
+	{
+
+	    /* -------------------------------------------------------------
+	     * place i in the degree list corresponding to its degree
+	     * ------------------------------------------------------------- */
+
+	    inext = Head [deg] ;
+	    ASSERT (inext >= EMPTY && inext < n) ;
+	    if (inext != EMPTY) Last [inext] = i ;
+	    Next [i] = inext ;
+	    Head [deg] = i ;
+
+	}
+    }
+
+/* ========================================================================= */
+/* WHILE (selecting pivots) DO */
+/* ========================================================================= */
+
+    while (nel < n)
+    {
+
+#ifndef NDEBUG
+	AMD_DEBUG1 (("\n======Nel "ID"\n", nel)) ;
+	if (AMD_debug >= 2)
+	{
+	    AMD_dump (n, Pe, Iw, Len, iwlen, pfree, Nv, Next,
+		    Last, Head, Elen, Degree, W, nel) ;
+	}
+#endif
+
+/* ========================================================================= */
+/* GET PIVOT OF MINIMUM DEGREE */
+/* ========================================================================= */
+
+	/* ----------------------------------------------------------------- */
+	/* find next supervariable for elimination */
+	/* ----------------------------------------------------------------- */
+
+	ASSERT (mindeg >= 0 && mindeg < n) ;
+	for (deg = mindeg ; deg < n ; deg++)
+	{
+	    me = Head [deg] ;
+	    if (me != EMPTY) break ;
+	}
+	mindeg = deg ;
+	ASSERT (me >= 0 && me < n) ;
+	AMD_DEBUG1 (("=================me: "ID"\n", me)) ;
+
+	/* ----------------------------------------------------------------- */
+	/* remove chosen variable from link list */
+	/* ----------------------------------------------------------------- */
+
+	inext = Next [me] ;
+	ASSERT (inext >= EMPTY && inext < n) ;
+	if (inext != EMPTY) Last [inext] = EMPTY ;
+	Head [deg] = inext ;
+
+	/* ----------------------------------------------------------------- */
+	/* me represents the elimination of pivots nel to nel+Nv[me]-1. */
+	/* place me itself as the first in this set. */
+	/* ----------------------------------------------------------------- */
+
+	elenme = Elen [me] ;
+	nvpiv = Nv [me] ;
+	ASSERT (nvpiv > 0) ;
+	nel += nvpiv ;
+
+/* ========================================================================= */
+/* CONSTRUCT NEW ELEMENT */
+/* ========================================================================= */
+
+	/* -----------------------------------------------------------------
+	 * At this point, me is the pivotal supervariable.  It will be
+	 * converted into the current element.  Scan list of the pivotal
+	 * supervariable, me, setting tree pointers and constructing new list
+	 * of supervariables for the new element, me.  p is a pointer to the
+	 * current position in the old list.
+	 * ----------------------------------------------------------------- */
+
+	/* flag the variable "me" as being in Lme by negating Nv [me] */
+	Nv [me] = -nvpiv ;
+	degme = 0 ;
+	ASSERT (Pe [me] >= 0 && Pe [me] < iwlen) ;
+
+	if (elenme == 0)
+	{
+
+	    /* ------------------------------------------------------------- */
+	    /* construct the new element in place */
+	    /* ------------------------------------------------------------- */
+
+	    pme1 = Pe [me] ;
+	    pme2 = pme1 - 1 ;
+
+	    for (p = pme1 ; p <= pme1 + Len [me] - 1 ; p++)
+	    {
+		i = Iw [p] ;
+		ASSERT (i >= 0 && i < n && Nv [i] >= 0) ;
+		nvi = Nv [i] ;
+		if (nvi > 0)
+		{
+
+		    /* ----------------------------------------------------- */
+		    /* i is a principal variable not yet placed in Lme. */
+		    /* store i in new list */
+		    /* ----------------------------------------------------- */
+
+		    /* flag i as being in Lme by negating Nv [i] */
+		    degme += nvi ;
+		    Nv [i] = -nvi ;
+		    Iw [++pme2] = i ;
+
+		    /* ----------------------------------------------------- */
+		    /* remove variable i from degree list. */
+		    /* ----------------------------------------------------- */
+
+		    ilast = Last [i] ;
+		    inext = Next [i] ;
+		    ASSERT (ilast >= EMPTY && ilast < n) ;
+		    ASSERT (inext >= EMPTY && inext < n) ;
+		    if (inext != EMPTY) Last [inext] = ilast ;
+		    if (ilast != EMPTY)
+		    {
+			Next [ilast] = inext ;
+		    }
+		    else
+		    {
+			/* i is at the head of the degree list */
+			ASSERT (Degree [i] >= 0 && Degree [i] < n) ;
+			Head [Degree [i]] = inext ;
+		    }
+		}
+	    }
+	}
+	else
+	{
+
+	    /* ------------------------------------------------------------- */
+	    /* construct the new element in empty space, Iw [pfree ...] */
+	    /* ------------------------------------------------------------- */
+
+	    p = Pe [me] ;
+	    pme1 = pfree ;
+	    slenme = Len [me] - elenme ;
+
+	    for (knt1 = 1 ; knt1 <= elenme + 1 ; knt1++)
+	    {
+
+		if (knt1 > elenme)
+		{
+		    /* search the supervariables in me. */
+		    e = me ;
+		    pj = p ;
+		    ln = slenme ;
+		    AMD_DEBUG2 (("Search sv: "ID" "ID" "ID"\n", me,pj,ln)) ;
+		}
+		else
+		{
+		    /* search the elements in me. */
+		    e = Iw [p++] ;
+		    ASSERT (e >= 0 && e < n) ;
+		    pj = Pe [e] ;
+		    ln = Len [e] ;
+		    AMD_DEBUG2 (("Search element e "ID" in me "ID"\n", e,me)) ;
+		    ASSERT (Elen [e] < EMPTY && W [e] > 0 && pj >= 0) ;
+		}
+		ASSERT (ln >= 0 && (ln == 0 || (pj >= 0 && pj < iwlen))) ;
+
+		/* ---------------------------------------------------------
+		 * search for different supervariables and add them to the
+		 * new list, compressing when necessary. this loop is
+		 * executed once for each element in the list and once for
+		 * all the supervariables in the list.
+		 * --------------------------------------------------------- */
+
+		for (knt2 = 1 ; knt2 <= ln ; knt2++)
+		{
+		    i = Iw [pj++] ;
+		    ASSERT (i >= 0 && i < n && (i == me || Elen [i] >= EMPTY));
+		    nvi = Nv [i] ;
+		    AMD_DEBUG2 ((": "ID" "ID" "ID" "ID"\n",
+				i, Elen [i], Nv [i], wflg)) ;
+
+		    if (nvi > 0)
+		    {
+
+			/* ------------------------------------------------- */
+			/* compress Iw, if necessary */
+			/* ------------------------------------------------- */
+
+			if (pfree >= iwlen)
+			{
+
+			    AMD_DEBUG1 (("GARBAGE COLLECTION\n")) ;
+
+			    /* prepare for compressing Iw by adjusting pointers
+			     * and lengths so that the lists being searched in
+			     * the inner and outer loops contain only the
+			     * remaining entries. */
+
+			    Pe [me] = p ;
+			    Len [me] -= knt1 ;
+			    /* check if nothing left of supervariable me */
+			    if (Len [me] == 0) Pe [me] = EMPTY ;
+			    Pe [e] = pj ;
+			    Len [e] = ln - knt2 ;
+			    /* nothing left of element e */
+			    if (Len [e] == 0) Pe [e] = EMPTY ;
+
+			    ncmpa++ ;	/* one more garbage collection */
+
+			    /* store first entry of each object in Pe */
+			    /* FLIP the first entry in each object */
+			    for (j = 0 ; j < n ; j++)
+			    {
+				pn = Pe [j] ;
+				if (pn >= 0)
+				{
+				    ASSERT (pn >= 0 && pn < iwlen) ;
+				    Pe [j] = Iw [pn] ;
+				    Iw [pn] = FLIP (j) ;
+				}
+			    }
+
+			    /* psrc/pdst point to source/destination */
+			    psrc = 0 ;
+			    pdst = 0 ;
+			    pend = pme1 - 1 ;
+
+			    while (psrc <= pend)
+			    {
+				/* search for next FLIP'd entry */
+				j = FLIP (Iw [psrc++]) ;
+				if (j >= 0)
+				{
+				    AMD_DEBUG2 (("Got object j: "ID"\n", j)) ;
+				    Iw [pdst] = Pe [j] ;
+				    Pe [j] = pdst++ ;
+				    lenj = Len [j] ;
+				    /* copy from source to destination */
+				    for (knt3 = 0 ; knt3 <= lenj - 2 ; knt3++)
+				    {
+					Iw [pdst++] = Iw [psrc++] ;
+				    }
+				}
+			    }
+
+			    /* move the new partially-constructed element */
+			    p1 = pdst ;
+			    for (psrc = pme1 ; psrc <= pfree-1 ; psrc++)
+			    {
+				Iw [pdst++] = Iw [psrc] ;
+			    }
+			    pme1 = p1 ;
+			    pfree = pdst ;
+			    pj = Pe [e] ;
+			    p = Pe [me] ;
+
+			}
+
+			/* ------------------------------------------------- */
+			/* i is a principal variable not yet placed in Lme */
+			/* store i in new list */
+			/* ------------------------------------------------- */
+
+			/* flag i as being in Lme by negating Nv [i] */
+			degme += nvi ;
+			Nv [i] = -nvi ;
+			Iw [pfree++] = i ;
+			AMD_DEBUG2 (("     s: "ID"     nv "ID"\n", i, Nv [i]));
+
+			/* ------------------------------------------------- */
+			/* remove variable i from degree link list */
+			/* ------------------------------------------------- */
+
+			ilast = Last [i] ;
+			inext = Next [i] ;
+			ASSERT (ilast >= EMPTY && ilast < n) ;
+			ASSERT (inext >= EMPTY && inext < n) ;
+			if (inext != EMPTY) Last [inext] = ilast ;
+			if (ilast != EMPTY)
+			{
+			    Next [ilast] = inext ;
+			}
+			else
+			{
+			    /* i is at the head of the degree list */
+			    ASSERT (Degree [i] >= 0 && Degree [i] < n) ;
+			    Head [Degree [i]] = inext ;
+			}
+		    }
+		}
+
+		if (e != me)
+		{
+		    /* set tree pointer and flag to indicate element e is
+		     * absorbed into new element me (the parent of e is me) */
+		    AMD_DEBUG1 ((" Element "ID" => "ID"\n", e, me)) ;
+		    Pe [e] = FLIP (me) ;
+		    W [e] = 0 ;
+		}
+	    }
+
+	    pme2 = pfree - 1 ;
+	}
+
+	/* ----------------------------------------------------------------- */
+	/* me has now been converted into an element in Iw [pme1..pme2] */
+	/* ----------------------------------------------------------------- */
+
+	/* degme holds the external degree of new element */
+	Degree [me] = degme ;
+	Pe [me] = pme1 ;
+	Len [me] = pme2 - pme1 + 1 ;
+	ASSERT (Pe [me] >= 0 && Pe [me] < iwlen) ;
+
+	Elen [me] = FLIP (nvpiv + degme) ;
+	/* FLIP (Elen (me)) is now the degree of pivot (including
+	 * diagonal part). */
+
+#ifndef NDEBUG
+	AMD_DEBUG2 (("New element structure: length= "ID"\n", pme2-pme1+1)) ;
+	for (pme = pme1 ; pme <= pme2 ; pme++) AMD_DEBUG3 ((" "ID"", Iw[pme]));
+	AMD_DEBUG3 (("\n")) ;
+#endif
+
+	/* ----------------------------------------------------------------- */
+	/* make sure that wflg is not too large. */
+	/* ----------------------------------------------------------------- */
+
+	/* With the current value of wflg, wflg+n must not cause integer
+	 * overflow */
+
+	wflg = clear_flag (wflg, wbig, W, n) ;
+
+/* ========================================================================= */
+/* COMPUTE (W [e] - wflg) = |Le\Lme| FOR ALL ELEMENTS */
+/* ========================================================================= */
+
+	/* -----------------------------------------------------------------
+	 * Scan 1:  compute the external degrees of previous elements with
+	 * respect to the current element.  That is:
+	 *       (W [e] - wflg) = |Le \ Lme|
+	 * for each element e that appears in any supervariable in Lme.  The
+	 * notation Le refers to the pattern (list of supervariables) of a
+	 * previous element e, where e is not yet absorbed, stored in
+	 * Iw [Pe [e] + 1 ... Pe [e] + Len [e]].  The notation Lme
+	 * refers to the pattern of the current element (stored in
+	 * Iw [pme1..pme2]).   If aggressive absorption is enabled, and
+	 * (W [e] - wflg) becomes zero, then the element e will be absorbed
+	 * in Scan 2.
+	 * ----------------------------------------------------------------- */
+
+	AMD_DEBUG2 (("me: ")) ;
+	for (pme = pme1 ; pme <= pme2 ; pme++)
+	{
+	    i = Iw [pme] ;
+	    ASSERT (i >= 0 && i < n) ;
+	    eln = Elen [i] ;
+	    AMD_DEBUG3 ((""ID" Elen "ID": \n", i, eln)) ;
+	    if (eln > 0)
+	    {
+		/* note that Nv [i] has been negated to denote i in Lme: */
+		nvi = -Nv [i] ;
+		ASSERT (nvi > 0 && Pe [i] >= 0 && Pe [i] < iwlen) ;
+		wnvi = wflg - nvi ;
+		for (p = Pe [i] ; p <= Pe [i] + eln - 1 ; p++)
+		{
+		    e = Iw [p] ;
+		    ASSERT (e >= 0 && e < n) ;
+		    we = W [e] ;
+		    AMD_DEBUG4 (("    e "ID" we "ID" ", e, we)) ;
+		    if (we >= wflg)
+		    {
+			/* unabsorbed element e has been seen in this loop */
+			AMD_DEBUG4 (("    unabsorbed, first time seen")) ;
+			we -= nvi ;
+		    }
+		    else if (we != 0)
+		    {
+			/* e is an unabsorbed element */
+			/* this is the first we have seen e in all of Scan 1 */
+			AMD_DEBUG4 (("    unabsorbed")) ;
+			we = Degree [e] + wnvi ;
+		    }
+		    AMD_DEBUG4 (("\n")) ;
+		    W [e] = we ;
+		}
+	    }
+	}
+	AMD_DEBUG2 (("\n")) ;
+
+/* ========================================================================= */
+/* DEGREE UPDATE AND ELEMENT ABSORPTION */
+/* ========================================================================= */
+
+	/* -----------------------------------------------------------------
+	 * Scan 2:  for each i in Lme, sum up the degree of Lme (which is
+	 * degme), plus the sum of the external degrees of each Le for the
+	 * elements e appearing within i, plus the supervariables in i.
+	 * Place i in hash list.
+	 * ----------------------------------------------------------------- */
+
+	for (pme = pme1 ; pme <= pme2 ; pme++)
+	{
+	    i = Iw [pme] ;
+	    ASSERT (i >= 0 && i < n && Nv [i] < 0 && Elen [i] >= 0) ;
+	    AMD_DEBUG2 (("Updating: i "ID" "ID" "ID"\n", i, Elen[i], Len [i]));
+	    p1 = Pe [i] ;
+	    p2 = p1 + Elen [i] - 1 ;
+	    pn = p1 ;
+	    hash = 0 ;
+	    deg = 0 ;
+	    ASSERT (p1 >= 0 && p1 < iwlen && p2 >= -1 && p2 < iwlen) ;
+
+	    /* ------------------------------------------------------------- */
+	    /* scan the element list associated with supervariable i */
+	    /* ------------------------------------------------------------- */
+
+	    /* UMFPACK/MA38-style approximate degree: */
+	    if (aggressive)
+	    {
+		for (p = p1 ; p <= p2 ; p++)
+		{
+		    e = Iw [p] ;
+		    ASSERT (e >= 0 && e < n) ;
+		    we = W [e] ;
+		    if (we != 0)
+		    {
+			/* e is an unabsorbed element */
+			/* dext = | Le \ Lme | */
+			dext = we - wflg ;
+			if (dext > 0)
+			{
+			    deg += dext ;
+			    Iw [pn++] = e ;
+			    hash += e ;
+			    AMD_DEBUG4 ((" e: "ID" hash = "ID"\n",e,hash)) ;
+			}
+			else
+			{
+			    /* external degree of e is zero, absorb e into me*/
+			    AMD_DEBUG1 ((" Element "ID" =>"ID" (aggressive)\n",
+				e, me)) ;
+			    ASSERT (dext == 0) ;
+			    Pe [e] = FLIP (me) ;
+			    W [e] = 0 ;
+			}
+		    }
+		}
+	    }
+	    else
+	    {
+		for (p = p1 ; p <= p2 ; p++)
+		{
+		    e = Iw [p] ;
+		    ASSERT (e >= 0 && e < n) ;
+		    we = W [e] ;
+		    if (we != 0)
+		    {
+			/* e is an unabsorbed element */
+			dext = we - wflg ;
+			ASSERT (dext >= 0) ;
+			deg += dext ;
+			Iw [pn++] = e ;
+			hash += e ;
+			AMD_DEBUG4 (("	e: "ID" hash = "ID"\n",e,hash)) ;
+		    }
+		}
+	    }
+
+	    /* count the number of elements in i (including me): */
+	    Elen [i] = pn - p1 + 1 ;
+
+	    /* ------------------------------------------------------------- */
+	    /* scan the supervariables in the list associated with i */
+	    /* ------------------------------------------------------------- */
+
+	    /* The bulk of the AMD run time is typically spent in this loop,
+	     * particularly if the matrix has many dense rows that are not
+	     * removed prior to ordering. */
+	    p3 = pn ;
+	    p4 = p1 + Len [i] ;
+	    for (p = p2 + 1 ; p < p4 ; p++)
+	    {
+		j = Iw [p] ;
+		ASSERT (j >= 0 && j < n) ;
+		nvj = Nv [j] ;
+		if (nvj > 0)
+		{
+		    /* j is unabsorbed, and not in Lme. */
+		    /* add to degree and add to new list */
+		    deg += nvj ;
+		    Iw [pn++] = j ;
+		    hash += j ;
+		    AMD_DEBUG4 (("  s: "ID" hash "ID" Nv[j]= "ID"\n",
+				j, hash, nvj)) ;
+		}
+	    }
+
+	    /* ------------------------------------------------------------- */
+	    /* update the degree and check for mass elimination */
+	    /* ------------------------------------------------------------- */
+
+	    /* with aggressive absorption, deg==0 is identical to the
+	     * Elen [i] == 1 && p3 == pn test, below. */
+	    ASSERT (IMPLIES (aggressive, (deg==0) == (Elen[i]==1 && p3==pn))) ;
+
+	    if (Elen [i] == 1 && p3 == pn)
+	    {
+
+		/* --------------------------------------------------------- */
+		/* mass elimination */
+		/* --------------------------------------------------------- */
+
+		/* There is nothing left of this node except for an edge to
+		 * the current pivot element.  Elen [i] is 1, and there are
+		 * no variables adjacent to node i.  Absorb i into the
+		 * current pivot element, me.  Note that if there are two or
+		 * more mass eliminations, fillin due to mass elimination is
+		 * possible within the nvpiv-by-nvpiv pivot block.  It is this
+		 * step that causes AMD's analysis to be an upper bound.
+		 *
+		 * The reason is that the selected pivot has a lower
+		 * approximate degree than the true degree of the two mass
+		 * eliminated nodes.  There is no edge between the two mass
+		 * eliminated nodes.  They are merged with the current pivot
+		 * anyway.
+		 *
+		 * No fillin occurs in the Schur complement, in any case,
+		 * and this effect does not decrease the quality of the
+		 * ordering itself, just the quality of the nonzero and
+		 * flop count analysis.  It also means that the post-ordering
+		 * is not an exact elimination tree post-ordering. */
+
+		AMD_DEBUG1 (("  MASS i "ID" => parent e "ID"\n", i, me)) ;
+		Pe [i] = FLIP (me) ;
+		nvi = -Nv [i] ;
+		degme -= nvi ;
+		nvpiv += nvi ;
+		nel += nvi ;
+		Nv [i] = 0 ;
+		Elen [i] = EMPTY ;
+
+	    }
+	    else
+	    {
+
+		/* --------------------------------------------------------- */
+		/* update the upper-bound degree of i */
+		/* --------------------------------------------------------- */
+
+		/* the following degree does not yet include the size
+		 * of the current element, which is added later: */
+
+		Degree [i] = MIN (Degree [i], deg) ;
+
+		/* --------------------------------------------------------- */
+		/* add me to the list for i */
+		/* --------------------------------------------------------- */
+
+		/* move first supervariable to end of list */
+		Iw [pn] = Iw [p3] ;
+		/* move first element to end of element part of list */
+		Iw [p3] = Iw [p1] ;
+		/* add new element, me, to front of list. */
+		Iw [p1] = me ;
+		/* store the new length of the list in Len [i] */
+		Len [i] = pn - p1 + 1 ;
+
+		/* --------------------------------------------------------- */
+		/* place in hash bucket.  Save hash key of i in Last [i]. */
+		/* --------------------------------------------------------- */
+
+		/* NOTE: this can fail if hash is negative, because the ANSI C
+		 * standard does not define a % b when a and/or b are negative.
+		 * That's why hash is defined as an unsigned Int, to avoid this
+		 * problem. */
+		hash = hash % n ;
+		ASSERT (((Int) hash) >= 0 && ((Int) hash) < n) ;
+
+		/* if the Hhead array is not used: */
+		j = Head [hash] ;
+		if (j <= EMPTY)
+		{
+		    /* degree list is empty, hash head is FLIP (j) */
+		    Next [i] = FLIP (j) ;
+		    Head [hash] = FLIP (i) ;
+		}
+		else
+		{
+		    /* degree list is not empty, use Last [Head [hash]] as
+		     * hash head. */
+		    Next [i] = Last [j] ;
+		    Last [j] = i ;
+		}
+
+		/* if a separate Hhead array is used: *
+		Next [i] = Hhead [hash] ;
+		Hhead [hash] = i ;
+		*/
+
+		Last [i] = hash ;
+	    }
+	}
+
+	Degree [me] = degme ;
+
+	/* ----------------------------------------------------------------- */
+	/* Clear the counter array, W [...], by incrementing wflg. */
+	/* ----------------------------------------------------------------- */
+
+	/* make sure that wflg+n does not cause integer overflow */
+	lemax =  MAX (lemax, degme) ;
+	wflg += lemax ;
+	wflg = clear_flag (wflg, wbig, W, n) ;
+	/*  at this point, W [0..n-1] < wflg holds */
+
+/* ========================================================================= */
+/* SUPERVARIABLE DETECTION */
+/* ========================================================================= */
+
+	AMD_DEBUG1 (("Detecting supervariables:\n")) ;
+	for (pme = pme1 ; pme <= pme2 ; pme++)
+	{
+	    i = Iw [pme] ;
+	    ASSERT (i >= 0 && i < n) ;
+	    AMD_DEBUG2 (("Consider i "ID" nv "ID"\n", i, Nv [i])) ;
+	    if (Nv [i] < 0)
+	    {
+		/* i is a principal variable in Lme */
+
+		/* ---------------------------------------------------------
+		 * examine all hash buckets with 2 or more variables.  We do
+		 * this by examing all unique hash keys for supervariables in
+		 * the pattern Lme of the current element, me
+		 * --------------------------------------------------------- */
+
+		/* let i = head of hash bucket, and empty the hash bucket */
+		ASSERT (Last [i] >= 0 && Last [i] < n) ;
+		hash = Last [i] ;
+
+		/* if Hhead array is not used: */
+		j = Head [hash] ;
+		if (j == EMPTY)
+		{
+		    /* hash bucket and degree list are both empty */
+		    i = EMPTY ;
+		}
+		else if (j < EMPTY)
+		{
+		    /* degree list is empty */
+		    i = FLIP (j) ;
+		    Head [hash] = EMPTY ;
+		}
+		else
+		{
+		    /* degree list is not empty, restore Last [j] of head j */
+		    i = Last [j] ;
+		    Last [j] = EMPTY ;
+		}
+
+		/* if separate Hhead array is used: *
+		i = Hhead [hash] ;
+		Hhead [hash] = EMPTY ;
+		*/
+
+		ASSERT (i >= EMPTY && i < n) ;
+		AMD_DEBUG2 (("----i "ID" hash "ID"\n", i, hash)) ;
+
+		while (i != EMPTY && Next [i] != EMPTY)
+		{
+
+		    /* -----------------------------------------------------
+		     * this bucket has one or more variables following i.
+		     * scan all of them to see if i can absorb any entries
+		     * that follow i in hash bucket.  Scatter i into w.
+		     * ----------------------------------------------------- */
+
+		    ln = Len [i] ;
+		    eln = Elen [i] ;
+		    ASSERT (ln >= 0 && eln >= 0) ;
+		    ASSERT (Pe [i] >= 0 && Pe [i] < iwlen) ;
+		    /* do not flag the first element in the list (me) */
+		    for (p = Pe [i] + 1 ; p <= Pe [i] + ln - 1 ; p++)
+		    {
+			ASSERT (Iw [p] >= 0 && Iw [p] < n) ;
+			W [Iw [p]] = wflg ;
+		    }
+
+		    /* ----------------------------------------------------- */
+		    /* scan every other entry j following i in bucket */
+		    /* ----------------------------------------------------- */
+
+		    jlast = i ;
+		    j = Next [i] ;
+		    ASSERT (j >= EMPTY && j < n) ;
+
+		    while (j != EMPTY)
+		    {
+			/* ------------------------------------------------- */
+			/* check if j and i have identical nonzero pattern */
+			/* ------------------------------------------------- */
+
+			AMD_DEBUG3 (("compare i "ID" and j "ID"\n", i,j)) ;
+
+			/* check if i and j have the same Len and Elen */
+			ASSERT (Len [j] >= 0 && Elen [j] >= 0) ;
+			ASSERT (Pe [j] >= 0 && Pe [j] < iwlen) ;
+			ok = (Len [j] == ln) && (Elen [j] == eln) ;
+			/* skip the first element in the list (me) */
+			for (p = Pe [j] + 1 ; ok && p <= Pe [j] + ln - 1 ; p++)
+			{
+			    ASSERT (Iw [p] >= 0 && Iw [p] < n) ;
+			    if (W [Iw [p]] != wflg) ok = 0 ;
+			}
+			if (ok)
+			{
+			    /* --------------------------------------------- */
+			    /* found it!  j can be absorbed into i */
+			    /* --------------------------------------------- */
+
+			    AMD_DEBUG1 (("found it! j "ID" => i "ID"\n", j,i));
+			    Pe [j] = FLIP (i) ;
+			    /* both Nv [i] and Nv [j] are negated since they */
+			    /* are in Lme, and the absolute values of each */
+			    /* are the number of variables in i and j: */
+			    Nv [i] += Nv [j] ;
+			    Nv [j] = 0 ;
+			    Elen [j] = EMPTY ;
+			    /* delete j from hash bucket */
+			    ASSERT (j != Next [j]) ;
+			    j = Next [j] ;
+			    Next [jlast] = j ;
+
+			}
+			else
+			{
+			    /* j cannot be absorbed into i */
+			    jlast = j ;
+			    ASSERT (j != Next [j]) ;
+			    j = Next [j] ;
+			}
+			ASSERT (j >= EMPTY && j < n) ;
+		    }
+
+		    /* -----------------------------------------------------
+		     * no more variables can be absorbed into i
+		     * go to next i in bucket and clear flag array
+		     * ----------------------------------------------------- */
+
+		    wflg++ ;
+		    i = Next [i] ;
+		    ASSERT (i >= EMPTY && i < n) ;
+
+		}
+	    }
+	}
+	AMD_DEBUG2 (("detect done\n")) ;
+
+/* ========================================================================= */
+/* RESTORE DEGREE LISTS AND REMOVE NONPRINCIPAL SUPERVARIABLES FROM ELEMENT */
+/* ========================================================================= */
+
+	p = pme1 ;
+	nleft = n - nel ;
+	for (pme = pme1 ; pme <= pme2 ; pme++)
+	{
+	    i = Iw [pme] ;
+	    ASSERT (i >= 0 && i < n) ;
+	    nvi = -Nv [i] ;
+	    AMD_DEBUG3 (("Restore i "ID" "ID"\n", i, nvi)) ;
+	    if (nvi > 0)
+	    {
+		/* i is a principal variable in Lme */
+		/* restore Nv [i] to signify that i is principal */
+		Nv [i] = nvi ;
+
+		/* --------------------------------------------------------- */
+		/* compute the external degree (add size of current element) */
+		/* --------------------------------------------------------- */
+
+		deg = Degree [i] + degme - nvi ;
+		deg = MIN (deg, nleft - nvi) ;
+		ASSERT (IMPLIES (aggressive, deg > 0) && deg >= 0 && deg < n) ;
+
+		/* --------------------------------------------------------- */
+		/* place the supervariable at the head of the degree list */
+		/* --------------------------------------------------------- */
+
+		inext = Head [deg] ;
+		ASSERT (inext >= EMPTY && inext < n) ;
+		if (inext != EMPTY) Last [inext] = i ;
+		Next [i] = inext ;
+		Last [i] = EMPTY ;
+		Head [deg] = i ;
+
+		/* --------------------------------------------------------- */
+		/* save the new degree, and find the minimum degree */
+		/* --------------------------------------------------------- */
+
+		mindeg = MIN (mindeg, deg) ;
+		Degree [i] = deg ;
+
+		/* --------------------------------------------------------- */
+		/* place the supervariable in the element pattern */
+		/* --------------------------------------------------------- */
+
+		Iw [p++] = i ;
+
+	    }
+	}
+	AMD_DEBUG2 (("restore done\n")) ;
+
+/* ========================================================================= */
+/* FINALIZE THE NEW ELEMENT */
+/* ========================================================================= */
+
+	AMD_DEBUG2 (("ME = "ID" DONE\n", me)) ;
+	Nv [me] = nvpiv ;
+	/* save the length of the list for the new element me */
+	Len [me] = p - pme1 ;
+	if (Len [me] == 0)
+	{
+	    /* there is nothing left of the current pivot element */
+	    /* it is a root of the assembly tree */
+	    Pe [me] = EMPTY ;
+	    W [me] = 0 ;
+	}
+	if (elenme != 0)
+	{
+	    /* element was not constructed in place: deallocate part of */
+	    /* it since newly nonprincipal variables may have been removed */
+	    pfree = p ;
+	}
+
+	/* The new element has nvpiv pivots and the size of the contribution
+	 * block for a multifrontal method is degme-by-degme, not including
+	 * the "dense" rows/columns.  If the "dense" rows/columns are included,
+	 * the frontal matrix is no larger than
+	 * (degme+ndense)-by-(degme+ndense).
+	 */
+
+	if (Info != (c_float *) NULL)
+	{
+	    f = nvpiv ;
+	    r = degme + ndense ;
+	    dmax = MAX (dmax, f + r) ;
+
+	    /* number of nonzeros in L (excluding the diagonal) */
+	    lnzme = f*r + (f-1)*f/2 ;
+	    lnz += lnzme ;
+
+	    /* number of divide operations for LDL' and for LU */
+	    ndiv += lnzme ;
+
+	    /* number of multiply-subtract pairs for LU */
+	    s = f*r*r + r*(f-1)*f + (f-1)*f*(2*f-1)/6 ;
+	    nms_lu += s ;
+
+	    /* number of multiply-subtract pairs for LDL' */
+	    nms_ldl += (s + lnzme)/2 ;
+	}
+
+#ifndef NDEBUG
+	AMD_DEBUG2 (("finalize done nel "ID" n "ID"\n   ::::\n", nel, n)) ;
+	for (pme = Pe [me] ; pme <= Pe [me] + Len [me] - 1 ; pme++)
+	{
+	      AMD_DEBUG3 ((" "ID"", Iw [pme])) ;
+	}
+	AMD_DEBUG3 (("\n")) ;
+#endif
+
+    }
+
+/* ========================================================================= */
+/* DONE SELECTING PIVOTS */
+/* ========================================================================= */
+
+    if (Info != (c_float *) NULL)
+    {
+
+	/* count the work to factorize the ndense-by-ndense submatrix */
+	f = ndense ;
+	dmax = MAX (dmax, (c_float) ndense) ;
+
+	/* number of nonzeros in L (excluding the diagonal) */
+	lnzme = (f-1)*f/2 ;
+	lnz += lnzme ;
+
+	/* number of divide operations for LDL' and for LU */
+	ndiv += lnzme ;
+
+	/* number of multiply-subtract pairs for LU */
+	s = (f-1)*f*(2*f-1)/6 ;
+	nms_lu += s ;
+
+	/* number of multiply-subtract pairs for LDL' */
+	nms_ldl += (s + lnzme)/2 ;
+
+	/* number of nz's in L (excl. diagonal) */
+	Info [AMD_LNZ] = lnz ;
+
+	/* number of divide ops for LU and LDL' */
+	Info [AMD_NDIV] = ndiv ;
+
+	/* number of multiply-subtract pairs for LDL' */
+	Info [AMD_NMULTSUBS_LDL] = nms_ldl ;
+
+	/* number of multiply-subtract pairs for LU */
+	Info [AMD_NMULTSUBS_LU] = nms_lu ;
+
+	/* number of "dense" rows/columns */
+	Info [AMD_NDENSE] = ndense ;
+
+	/* largest front is dmax-by-dmax */
+	Info [AMD_DMAX] = dmax ;
+
+	/* number of garbage collections in AMD */
+	Info [AMD_NCMPA] = ncmpa ;
+
+	/* successful ordering */
+	Info [AMD_STATUS] = AMD_OK ;
+    }
+
+/* ========================================================================= */
+/* POST-ORDERING */
+/* ========================================================================= */
+
+/* -------------------------------------------------------------------------
+ * Variables at this point:
+ *
+ * Pe: holds the elimination tree.  The parent of j is FLIP (Pe [j]),
+ *	or EMPTY if j is a root.  The tree holds both elements and
+ *	non-principal (unordered) variables absorbed into them.
+ *	Dense variables are non-principal and unordered.
+ *
+ * Elen: holds the size of each element, including the diagonal part.
+ *	FLIP (Elen [e]) > 0 if e is an element.  For unordered
+ *	variables i, Elen [i] is EMPTY.
+ *
+ * Nv: Nv [e] > 0 is the number of pivots represented by the element e.
+ *	For unordered variables i, Nv [i] is zero.
+ *
+ * Contents no longer needed:
+ *	W, Iw, Len, Degree, Head, Next, Last.
+ *
+ * The matrix itself has been destroyed.
+ *
+ * n: the size of the matrix.
+ * No other scalars needed (pfree, iwlen, etc.)
+ * ------------------------------------------------------------------------- */
+
+    /* restore Pe */
+    for (i = 0 ; i < n ; i++)
+    {
+	Pe [i] = FLIP (Pe [i]) ;
+    }
+
+    /* restore Elen, for output information, and for postordering */
+    for (i = 0 ; i < n ; i++)
+    {
+	Elen [i] = FLIP (Elen [i]) ;
+    }
+
+/* Now the parent of j is Pe [j], or EMPTY if j is a root.  Elen [e] > 0
+ * is the size of element e.  Elen [i] is EMPTY for unordered variable i. */
+
+#ifndef NDEBUG
+    AMD_DEBUG2 (("\nTree:\n")) ;
+    for (i = 0 ; i < n ; i++)
+    {
+	AMD_DEBUG2 ((" "ID" parent: "ID"   ", i, Pe [i])) ;
+	ASSERT (Pe [i] >= EMPTY && Pe [i] < n) ;
+	if (Nv [i] > 0)
+	{
+	    /* this is an element */
+	    e = i ;
+	    AMD_DEBUG2 ((" element, size is "ID"\n", Elen [i])) ;
+	    ASSERT (Elen [e] > 0) ;
+	}
+	AMD_DEBUG2 (("\n")) ;
+    }
+    AMD_DEBUG2 (("\nelements:\n")) ;
+    for (e = 0 ; e < n ; e++)
+    {
+	if (Nv [e] > 0)
+	{
+	    AMD_DEBUG3 (("Element e= "ID" size "ID" nv "ID" \n", e,
+		Elen [e], Nv [e])) ;
+	}
+    }
+    AMD_DEBUG2 (("\nvariables:\n")) ;
+    for (i = 0 ; i < n ; i++)
+    {
+	Int cnt ;
+	if (Nv [i] == 0)
+	{
+	    AMD_DEBUG3 (("i unordered: "ID"\n", i)) ;
+	    j = Pe [i] ;
+	    cnt = 0 ;
+	    AMD_DEBUG3 (("  j: "ID"\n", j)) ;
+	    if (j == EMPTY)
+	    {
+		AMD_DEBUG3 (("	i is a dense variable\n")) ;
+	    }
+	    else
+	    {
+		ASSERT (j >= 0 && j < n) ;
+		while (Nv [j] == 0)
+		{
+		    AMD_DEBUG3 (("	j : "ID"\n", j)) ;
+		    j = Pe [j] ;
+		    AMD_DEBUG3 (("	j:: "ID"\n", j)) ;
+		    cnt++ ;
+		    if (cnt > n) break ;
+		}
+		e = j ;
+		AMD_DEBUG3 (("	got to e: "ID"\n", e)) ;
+	    }
+	}
+    }
+#endif
+
+/* ========================================================================= */
+/* compress the paths of the variables */
+/* ========================================================================= */
+
+    for (i = 0 ; i < n ; i++)
+    {
+	if (Nv [i] == 0)
+	{
+
+	    /* -------------------------------------------------------------
+	     * i is an un-ordered row.  Traverse the tree from i until
+	     * reaching an element, e.  The element, e, was the principal
+	     * supervariable of i and all nodes in the path from i to when e
+	     * was selected as pivot.
+	     * ------------------------------------------------------------- */
+
+	    AMD_DEBUG1 (("Path compression, i unordered: "ID"\n", i)) ;
+	    j = Pe [i] ;
+	    ASSERT (j >= EMPTY && j < n) ;
+	    AMD_DEBUG3 (("	j: "ID"\n", j)) ;
+	    if (j == EMPTY)
+	    {
+		/* Skip a dense variable.  It has no parent. */
+		AMD_DEBUG3 (("      i is a dense variable\n")) ;
+		continue ;
+	    }
+
+	    /* while (j is a variable) */
+	    while (Nv [j] == 0)
+	    {
+		AMD_DEBUG3 (("		j : "ID"\n", j)) ;
+		j = Pe [j] ;
+		AMD_DEBUG3 (("		j:: "ID"\n", j)) ;
+		ASSERT (j >= 0 && j < n) ;
+	    }
+	    /* got to an element e */
+	    e = j ;
+	    AMD_DEBUG3 (("got to e: "ID"\n", e)) ;
+
+	    /* -------------------------------------------------------------
+	     * traverse the path again from i to e, and compress the path
+	     * (all nodes point to e).  Path compression allows this code to
+	     * compute in O(n) time.
+	     * ------------------------------------------------------------- */
+
+	    j = i ;
+	    /* while (j is a variable) */
+	    while (Nv [j] == 0)
+	    {
+		jnext = Pe [j] ;
+		AMD_DEBUG3 (("j "ID" jnext "ID"\n", j, jnext)) ;
+		Pe [j] = e ;
+		j = jnext ;
+		ASSERT (j >= 0 && j < n) ;
+	    }
+	}
+    }
+
+/* ========================================================================= */
+/* postorder the assembly tree */
+/* ========================================================================= */
+
+    AMD_postorder (n, Pe, Nv, Elen,
+	W,			/* output order */
+	Head, Next, Last) ;	/* workspace */
+
+/* ========================================================================= */
+/* compute output permutation and inverse permutation */
+/* ========================================================================= */
+
+    /* W [e] = k means that element e is the kth element in the new
+     * order.  e is in the range 0 to n-1, and k is in the range 0 to
+     * the number of elements.  Use Head for inverse order. */
+
+    for (k = 0 ; k < n ; k++)
+    {
+	Head [k] = EMPTY ;
+	Next [k] = EMPTY ;
+    }
+    for (e = 0 ; e < n ; e++)
+    {
+	k = W [e] ;
+	ASSERT ((k == EMPTY) == (Nv [e] == 0)) ;
+	if (k != EMPTY)
+	{
+	    ASSERT (k >= 0 && k < n) ;
+	    Head [k] = e ;
+	}
+    }
+
+    /* construct output inverse permutation in Next,
+     * and permutation in Last */
+    nel = 0 ;
+    for (k = 0 ; k < n ; k++)
+    {
+	e = Head [k] ;
+	if (e == EMPTY) break ;
+	ASSERT (e >= 0 && e < n && Nv [e] > 0) ;
+	Next [e] = nel ;
+	nel += Nv [e] ;
+    }
+    ASSERT (nel == n - ndense) ;
+
+    /* order non-principal variables (dense, & those merged into supervar's) */
+    for (i = 0 ; i < n ; i++)
+    {
+	if (Nv [i] == 0)
+	{
+	    e = Pe [i] ;
+	    ASSERT (e >= EMPTY && e < n) ;
+	    if (e != EMPTY)
+	    {
+		/* This is an unordered variable that was merged
+		 * into element e via supernode detection or mass
+		 * elimination of i when e became the pivot element.
+		 * Place i in order just before e. */
+		ASSERT (Next [i] == EMPTY && Nv [e] > 0) ;
+		Next [i] = Next [e] ;
+		Next [e]++ ;
+	    }
+	    else
+	    {
+		/* This is a dense unordered variable, with no parent.
+		 * Place it last in the output order. */
+		Next [i] = nel++ ;
+	    }
+	}
+    }
+    ASSERT (nel == n) ;
+
+    AMD_DEBUG2 (("\n\nPerm:\n")) ;
+    for (i = 0 ; i < n ; i++)
+    {
+	k = Next [i] ;
+	ASSERT (k >= 0 && k < n) ;
+	Last [k] = i ;
+	AMD_DEBUG2 (("   perm ["ID"] = "ID"\n", k, i)) ;
+    }
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_aat.c b/lin_sys/direct/qdldl/amd/src/amd_aat.c
new file mode 100644
index 0000000..cccb617
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_aat.c
@@ -0,0 +1,184 @@
+/* ========================================================================= */
+/* === AMD_aat ============================================================= */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* AMD_aat:  compute the symmetry of the pattern of A, and count the number of
+ * nonzeros each column of A+A' (excluding the diagonal).  Assumes the input
+ * matrix has no errors, with sorted columns and no duplicates
+ * (AMD_valid (n, n, Ap, Ai) must be AMD_OK, but this condition is not
+ * checked).
+ */
+
+#include "amd_internal.h"
+
+GLOBAL size_t AMD_aat	/* returns nz in A+A' */
+(
+    Int n,
+    const Int Ap [ ],
+    const Int Ai [ ],
+    Int Len [ ],	/* Len [j]: length of column j of A+A', excl diagonal*/
+    Int Tp [ ],		/* workspace of size n */
+    c_float Info [ ]
+)
+{
+    Int p1, p2, p, i, j, pj, pj2, k, nzdiag, nzboth, nz ;
+    c_float sym ;
+    size_t nzaat ;
+
+#ifndef NDEBUG
+    AMD_debug_init ("AMD AAT") ;
+    for (k = 0 ; k < n ; k++) Tp [k] = EMPTY ;
+    ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ;
+#endif
+
+    if (Info != (c_float *) NULL)
+    {
+	/* clear the Info array, if it exists */
+	for (i = 0 ; i < AMD_INFO ; i++)
+	{
+	    Info [i] = EMPTY ;
+	}
+	Info [AMD_STATUS] = AMD_OK ;
+    }
+
+    for (k = 0 ; k < n ; k++)
+    {
+	Len [k] = 0 ;
+    }
+
+    nzdiag = 0 ;
+    nzboth = 0 ;
+    nz = Ap [n] ;
+
+    for (k = 0 ; k < n ; k++)
+    {
+	p1 = Ap [k] ;
+	p2 = Ap [k+1] ;
+	AMD_DEBUG2 (("\nAAT Column: "ID" p1: "ID" p2: "ID"\n", k, p1, p2)) ;
+
+	/* construct A+A' */
+	for (p = p1 ; p < p2 ; )
+	{
+	    /* scan the upper triangular part of A */
+	    j = Ai [p] ;
+	    if (j < k)
+	    {
+		/* entry A (j,k) is in the strictly upper triangular part,
+		 * add both A (j,k) and A (k,j) to the matrix A+A' */
+		Len [j]++ ;
+		Len [k]++ ;
+		AMD_DEBUG3 (("    upper ("ID","ID") ("ID","ID")\n", j,k, k,j));
+		p++ ;
+	    }
+	    else if (j == k)
+	    {
+		/* skip the diagonal */
+		p++ ;
+		nzdiag++ ;
+		break ;
+	    }
+	    else /* j > k */
+	    {
+		/* first entry below the diagonal */
+		break ;
+	    }
+	    /* scan lower triangular part of A, in column j until reaching
+	     * row k.  Start where last scan left off. */
+	    ASSERT (Tp [j] != EMPTY) ;
+	    ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ;
+	    pj2 = Ap [j+1] ;
+	    for (pj = Tp [j] ; pj < pj2 ; )
+	    {
+		i = Ai [pj] ;
+		if (i < k)
+		{
+		    /* A (i,j) is only in the lower part, not in upper.
+		     * add both A (i,j) and A (j,i) to the matrix A+A' */
+		    Len [i]++ ;
+		    Len [j]++ ;
+		    AMD_DEBUG3 (("    lower ("ID","ID") ("ID","ID")\n",
+			i,j, j,i)) ;
+		    pj++ ;
+		}
+		else if (i == k)
+		{
+		    /* entry A (k,j) in lower part and A (j,k) in upper */
+		    pj++ ;
+		    nzboth++ ;
+		    break ;
+		}
+		else /* i > k */
+		{
+		    /* consider this entry later, when k advances to i */
+		    break ;
+		}
+	    }
+	    Tp [j] = pj ;
+	}
+	/* Tp [k] points to the entry just below the diagonal in column k */
+	Tp [k] = p ;
+    }
+
+    /* clean up, for remaining mismatched entries */
+    for (j = 0 ; j < n ; j++)
+    {
+	for (pj = Tp [j] ; pj < Ap [j+1] ; pj++)
+	{
+	    i = Ai [pj] ;
+	    /* A (i,j) is only in the lower part, not in upper.
+	     * add both A (i,j) and A (j,i) to the matrix A+A' */
+	    Len [i]++ ;
+	    Len [j]++ ;
+	    AMD_DEBUG3 (("    lower cleanup ("ID","ID") ("ID","ID")\n",
+		i,j, j,i)) ;
+	}
+    }
+
+    /* --------------------------------------------------------------------- */
+    /* compute the symmetry of the nonzero pattern of A */
+    /* --------------------------------------------------------------------- */
+
+    /* Given a matrix A, the symmetry of A is:
+     *	B = tril (spones (A), -1) + triu (spones (A), 1) ;
+     *  sym = nnz (B & B') / nnz (B) ;
+     *  or 1 if nnz (B) is zero.
+     */
+
+    if (nz == nzdiag)
+    {
+	sym = 1 ;
+    }
+    else
+    {
+	sym = (2 * (c_float) nzboth) / ((c_float) (nz - nzdiag)) ;
+    }
+
+    nzaat = 0 ;
+    for (k = 0 ; k < n ; k++)
+    {
+	nzaat += Len [k] ;
+    }
+
+    AMD_DEBUG1 (("AMD nz in A+A', excluding diagonal (nzaat) = %g\n",
+	(c_float) nzaat)) ;
+    AMD_DEBUG1 (("   nzboth: "ID" nz: "ID" nzdiag: "ID" symmetry: %g\n",
+		nzboth, nz, nzdiag, sym)) ;
+
+    if (Info != (c_float *) NULL)
+    {
+	Info [AMD_STATUS] = AMD_OK ;
+	Info [AMD_N] = n ;
+	Info [AMD_NZ] = nz ;
+	Info [AMD_SYMMETRY] = sym ;	    /* symmetry of pattern of A */
+	Info [AMD_NZDIAG] = nzdiag ;	    /* nonzeros on diagonal of A */
+	Info [AMD_NZ_A_PLUS_AT] = nzaat ;   /* nonzeros in A+A' */
+    }
+
+    return (nzaat) ;
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_control.c b/lin_sys/direct/qdldl/amd/src/amd_control.c
new file mode 100644
index 0000000..0cfd298
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_control.c
@@ -0,0 +1,64 @@
+/* ========================================================================= */
+/* === AMD_control ========================================================= */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* User-callable.  Prints the control parameters for AMD.  See amd.h
+ * for details.  If the Control array is not present, the defaults are
+ * printed instead.
+ */
+
+#include "amd_internal.h"
+
+GLOBAL void AMD_control
+(
+    c_float Control [ ]
+)
+{
+    c_float alpha ;
+    Int aggressive ;
+
+    if (Control != (c_float *) NULL)
+    {
+	alpha = Control [AMD_DENSE] ;
+	aggressive = Control [AMD_AGGRESSIVE] != 0 ;
+    }
+    else
+    {
+	alpha = AMD_DEFAULT_DENSE ;
+	aggressive = AMD_DEFAULT_AGGRESSIVE ;
+    }
+
+    SUITESPARSE_PRINTF ((
+        "\nAMD version %d.%d.%d, %s: approximate minimum degree ordering\n"
+	"    dense row parameter: %g\n", AMD_MAIN_VERSION, AMD_SUB_VERSION,
+	AMD_SUBSUB_VERSION, AMD_DATE, alpha)) ;
+
+    if (alpha < 0)
+    {
+	SUITESPARSE_PRINTF (("    no rows treated as dense\n")) ;
+    }
+    else
+    {
+	SUITESPARSE_PRINTF ((
+	"    (rows with more than max (%g * sqrt (n), 16) entries are\n"
+	"    considered \"dense\", and placed last in output permutation)\n",
+	alpha)) ;
+    }
+
+    if (aggressive)
+    {
+	SUITESPARSE_PRINTF (("    aggressive absorption:  yes\n")) ;
+    }
+    else
+    {
+	SUITESPARSE_PRINTF (("    aggressive absorption:  no\n")) ;
+    }
+
+    SUITESPARSE_PRINTF (("    size of AMD integer: %d\n\n", sizeof (Int))) ;
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_defaults.c b/lin_sys/direct/qdldl/amd/src/amd_defaults.c
new file mode 100644
index 0000000..50112f1
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_defaults.c
@@ -0,0 +1,37 @@
+/* ========================================================================= */
+/* === AMD_defaults ======================================================== */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* User-callable.  Sets default control parameters for AMD.  See amd.h
+ * for details.
+ */
+
+#include "amd_internal.h"
+
+/* ========================================================================= */
+/* === AMD defaults ======================================================== */
+/* ========================================================================= */
+
+GLOBAL void AMD_defaults
+(
+    c_float Control [ ]
+)
+{
+    Int i ;
+
+    if (Control != (c_float *) NULL)
+    {
+	for (i = 0 ; i < AMD_CONTROL ; i++)
+	{
+	    Control [i] = 0 ;
+	}
+	Control [AMD_DENSE] = AMD_DEFAULT_DENSE ;
+	Control [AMD_AGGRESSIVE] = AMD_DEFAULT_AGGRESSIVE ;
+    }
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_info.c b/lin_sys/direct/qdldl/amd/src/amd_info.c
new file mode 100644
index 0000000..bf39378
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_info.c
@@ -0,0 +1,119 @@
+/* ========================================================================= */
+/* === AMD_info ============================================================ */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* User-callable.  Prints the output statistics for AMD.  See amd.h
+ * for details.  If the Info array is not present, nothing is printed.
+ */
+
+#include "amd_internal.h"
+
+#define PRI(format,x) { if (x >= 0) { SUITESPARSE_PRINTF ((format, x)) ; }}
+
+GLOBAL void AMD_info
+(
+    c_float Info [ ]
+)
+{
+    c_float n, ndiv, nmultsubs_ldl, nmultsubs_lu, lnz, lnzd ;
+
+    SUITESPARSE_PRINTF (("\nAMD version %d.%d.%d, %s, results:\n",
+	AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_SUBSUB_VERSION, AMD_DATE)) ;
+
+    if (!Info)
+    {
+	return ;
+    }
+
+    n = Info [AMD_N] ;
+    ndiv = Info [AMD_NDIV] ;
+    nmultsubs_ldl = Info [AMD_NMULTSUBS_LDL] ;
+    nmultsubs_lu = Info [AMD_NMULTSUBS_LU] ;
+    lnz = Info [AMD_LNZ] ;
+    lnzd = (n >= 0 && lnz >= 0) ? (n + lnz) : (-1) ;
+
+    /* AMD return status */
+    SUITESPARSE_PRINTF (("    status: ")) ;
+    if (Info [AMD_STATUS] == AMD_OK)
+    {
+	SUITESPARSE_PRINTF (("OK\n")) ;
+    }
+    else if (Info [AMD_STATUS] == AMD_OUT_OF_MEMORY)
+    {
+	SUITESPARSE_PRINTF (("out of memory\n")) ;
+    }
+    else if (Info [AMD_STATUS] == AMD_INVALID)
+    {
+	SUITESPARSE_PRINTF (("invalid matrix\n")) ;
+    }
+    else if (Info [AMD_STATUS] == AMD_OK_BUT_JUMBLED)
+    {
+	SUITESPARSE_PRINTF (("OK, but jumbled\n")) ;
+    }
+    else
+    {
+	SUITESPARSE_PRINTF (("unknown\n")) ;
+    }
+
+    /* statistics about the input matrix */
+    PRI ("    n, dimension of A:                                  %.20g\n", n);
+    PRI ("    nz, number of nonzeros in A:                        %.20g\n",
+	Info [AMD_NZ]) ;
+    PRI ("    symmetry of A:                                      %.4f\n",
+	Info [AMD_SYMMETRY]) ;
+    PRI ("    number of nonzeros on diagonal:                     %.20g\n",
+	Info [AMD_NZDIAG]) ;
+    PRI ("    nonzeros in pattern of A+A' (excl. diagonal):       %.20g\n",
+	Info [AMD_NZ_A_PLUS_AT]) ;
+    PRI ("    # dense rows/columns of A+A':                       %.20g\n",
+	Info [AMD_NDENSE]) ;
+
+    /* statistics about AMD's behavior  */
+    PRI ("    memory used, in bytes:                              %.20g\n",
+	Info [AMD_MEMORY]) ;
+    PRI ("    # of memory compactions:                            %.20g\n",
+	Info [AMD_NCMPA]) ;
+
+    /* statistics about the ordering quality */
+    SUITESPARSE_PRINTF (("\n"
+	"    The following approximate statistics are for a subsequent\n"
+	"    factorization of A(P,P) + A(P,P)'.  They are slight upper\n"
+	"    bounds if there are no dense rows/columns in A+A', and become\n"
+	"    looser if dense rows/columns exist.\n\n")) ;
+
+    PRI ("    nonzeros in L (excluding diagonal):                 %.20g\n",
+	lnz) ;
+    PRI ("    nonzeros in L (including diagonal):                 %.20g\n",
+	lnzd) ;
+    PRI ("    # divide operations for LDL' or LU:                 %.20g\n",
+	ndiv) ;
+    PRI ("    # multiply-subtract operations for LDL':            %.20g\n",
+	nmultsubs_ldl) ;
+    PRI ("    # multiply-subtract operations for LU:              %.20g\n",
+	nmultsubs_lu) ;
+    PRI ("    max nz. in any column of L (incl. diagonal):        %.20g\n",
+	Info [AMD_DMAX]) ;
+
+    /* total flop counts for various factorizations */
+
+    if (n >= 0 && ndiv >= 0 && nmultsubs_ldl >= 0 && nmultsubs_lu >= 0)
+    {
+	SUITESPARSE_PRINTF (("\n"
+	"    chol flop count for real A, sqrt counted as 1 flop: %.20g\n"
+	"    LDL' flop count for real A:                         %.20g\n"
+	"    LDL' flop count for complex A:                      %.20g\n"
+	"    LU flop count for real A (with no pivoting):        %.20g\n"
+	"    LU flop count for complex A (with no pivoting):     %.20g\n\n",
+	n + ndiv + 2*nmultsubs_ldl,
+	    ndiv + 2*nmultsubs_ldl,
+	  9*ndiv + 8*nmultsubs_ldl,
+	    ndiv + 2*nmultsubs_lu,
+	  9*ndiv + 8*nmultsubs_lu)) ;
+    }
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_order.c b/lin_sys/direct/qdldl/amd/src/amd_order.c
new file mode 100644
index 0000000..8b635a9
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_order.c
@@ -0,0 +1,199 @@
+/* ========================================================================= */
+/* === AMD_order =========================================================== */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* User-callable AMD minimum degree ordering routine.  See amd.h for
+ * documentation.
+ */
+
+#include "amd_internal.h"
+
+/* ========================================================================= */
+/* === AMD_order =========================================================== */
+/* ========================================================================= */
+
+GLOBAL Int AMD_order
+(
+    Int n,
+    const Int Ap [ ],
+    const Int Ai [ ],
+    Int P [ ],
+    c_float Control [ ],
+    c_float Info [ ]
+)
+{
+    Int *Len, *S, nz, i, *Pinv, info, status, *Rp, *Ri, *Cp, *Ci, ok ;
+    size_t nzaat, slen ;
+    c_float mem = 0 ;
+
+#ifndef NDEBUG
+    AMD_debug_init ("amd") ;
+#endif
+
+    /* clear the Info array, if it exists */
+    info = Info != (c_float *) NULL ;
+    if (info)
+    {
+	for (i = 0 ; i < AMD_INFO ; i++)
+	{
+	    Info [i] = EMPTY ;
+	}
+	Info [AMD_N] = n ;
+	Info [AMD_STATUS] = AMD_OK ;
+    }
+
+    /* make sure inputs exist and n is >= 0 */
+    if (Ai == (Int *) NULL || Ap == (Int *) NULL || P == (Int *) NULL || n < 0)
+    {
+	if (info) Info [AMD_STATUS] = AMD_INVALID ;
+	return (AMD_INVALID) ;	    /* arguments are invalid */
+    }
+
+    if (n == 0)
+    {
+	return (AMD_OK) ;	    /* n is 0 so there's nothing to do */
+    }
+
+    nz = Ap [n] ;
+    if (info)
+    {
+	Info [AMD_NZ] = nz ;
+    }
+    if (nz < 0)
+    {
+	if (info) Info [AMD_STATUS] = AMD_INVALID ;
+	return (AMD_INVALID) ;
+    }
+
+    /* check if n or nz will cause size_t overflow */
+    if (((size_t) n) >= SIZE_T_MAX / sizeof (Int)
+     || ((size_t) nz) >= SIZE_T_MAX / sizeof (Int))
+    {
+	if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ;
+	return (AMD_OUT_OF_MEMORY) ;	    /* problem too large */
+    }
+
+    /* check the input matrix:	AMD_OK, AMD_INVALID, or AMD_OK_BUT_JUMBLED */
+    status = AMD_valid (n, n, Ap, Ai) ;
+
+    if (status == AMD_INVALID)
+    {
+	if (info) Info [AMD_STATUS] = AMD_INVALID ;
+	return (AMD_INVALID) ;	    /* matrix is invalid */
+    }
+
+    /* allocate two size-n integer workspaces */
+    Len  = SuiteSparse_malloc (n, sizeof (Int)) ;
+    Pinv = SuiteSparse_malloc (n, sizeof (Int)) ;
+    mem += n ;
+    mem += n ;
+    if (!Len || !Pinv)
+    {
+	/* :: out of memory :: */
+	SuiteSparse_free (Len) ;
+	SuiteSparse_free (Pinv) ;
+	if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ;
+	return (AMD_OUT_OF_MEMORY) ;
+    }
+
+    if (status == AMD_OK_BUT_JUMBLED)
+    {
+	/* sort the input matrix and remove duplicate entries */
+	AMD_DEBUG1 (("Matrix is jumbled\n")) ;
+	Rp = SuiteSparse_malloc (n+1, sizeof (Int)) ;
+	Ri = SuiteSparse_malloc (nz,  sizeof (Int)) ;
+	mem += (n+1) ;
+	mem += MAX (nz,1) ;
+	if (!Rp || !Ri)
+	{
+	    /* :: out of memory :: */
+	    SuiteSparse_free (Rp) ;
+	    SuiteSparse_free (Ri) ;
+	    SuiteSparse_free (Len) ;
+	    SuiteSparse_free (Pinv) ;
+	    if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ;
+	    return (AMD_OUT_OF_MEMORY) ;
+	}
+	/* use Len and Pinv as workspace to create R = A' */
+	AMD_preprocess (n, Ap, Ai, Rp, Ri, Len, Pinv) ;
+	Cp = Rp ;
+	Ci = Ri ;
+    }
+    else
+    {
+	/* order the input matrix as-is.  No need to compute R = A' first */
+	Rp = NULL ;
+	Ri = NULL ;
+	Cp = (Int *) Ap ;
+	Ci = (Int *) Ai ;
+    }
+
+    /* --------------------------------------------------------------------- */
+    /* determine the symmetry and count off-diagonal nonzeros in A+A' */
+    /* --------------------------------------------------------------------- */
+
+    nzaat = AMD_aat (n, Cp, Ci, Len, P, Info) ;
+    AMD_DEBUG1 (("nzaat: %g\n", (c_float) nzaat)) ;
+    ASSERT ((MAX (nz-n, 0) <= nzaat) && (nzaat <= 2 * (size_t) nz)) ;
+
+    /* --------------------------------------------------------------------- */
+    /* allocate workspace for matrix, elbow room, and 6 size-n vectors */
+    /* --------------------------------------------------------------------- */
+
+    S = NULL ;
+    slen = nzaat ;			/* space for matrix */
+    ok = ((slen + nzaat/5) >= slen) ;	/* check for size_t overflow */
+    slen += nzaat/5 ;			/* add elbow room */
+    for (i = 0 ; ok && i < 7 ; i++)
+    {
+	ok = ((slen + n) > slen) ;	/* check for size_t overflow */
+	slen += n ;			/* size-n elbow room, 6 size-n work */
+    }
+    mem += slen ;
+    ok = ok && (slen < SIZE_T_MAX / sizeof (Int)) ; /* check for overflow */
+    ok = ok && (slen < Int_MAX) ;	/* S[i] for Int i must be OK */
+    if (ok)
+    {
+	S = SuiteSparse_malloc (slen, sizeof (Int)) ;
+    }
+    AMD_DEBUG1 (("slen %g\n", (c_float) slen)) ;
+    if (!S)
+    {
+	/* :: out of memory :: (or problem too large) */
+	SuiteSparse_free (Rp) ;
+	SuiteSparse_free (Ri) ;
+	SuiteSparse_free (Len) ;
+	SuiteSparse_free (Pinv) ;
+	if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ;
+	return (AMD_OUT_OF_MEMORY) ;
+    }
+    if (info)
+    {
+	/* memory usage, in bytes. */
+	Info [AMD_MEMORY] = mem * sizeof (Int) ;
+    }
+
+    /* --------------------------------------------------------------------- */
+    /* order the matrix */
+    /* --------------------------------------------------------------------- */
+
+    AMD_1 (n, Cp, Ci, P, Pinv, Len, slen, S, Control, Info) ;
+
+    /* --------------------------------------------------------------------- */
+    /* free the workspace */
+    /* --------------------------------------------------------------------- */
+
+    SuiteSparse_free (Rp) ;
+    SuiteSparse_free (Ri) ;
+    SuiteSparse_free (Len) ;
+    SuiteSparse_free (Pinv) ;
+    SuiteSparse_free (S) ;
+    if (info) Info [AMD_STATUS] = status ;
+    return (status) ;	    /* successful ordering */
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_post_tree.c b/lin_sys/direct/qdldl/amd/src/amd_post_tree.c
new file mode 100644
index 0000000..516c95c
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_post_tree.c
@@ -0,0 +1,120 @@
+/* ========================================================================= */
+/* === AMD_post_tree ======================================================= */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* Post-ordering of a supernodal elimination tree.  */
+
+#include "amd_internal.h"
+
+GLOBAL Int AMD_post_tree
+(
+    Int root,			/* root of the tree */
+    Int k,			/* start numbering at k */
+    Int Child [ ],		/* input argument of size nn, undefined on
+				 * output.  Child [i] is the head of a link
+				 * list of all nodes that are children of node
+				 * i in the tree. */
+    const Int Sibling [ ],	/* input argument of size nn, not modified.
+				 * If f is a node in the link list of the
+				 * children of node i, then Sibling [f] is the
+				 * next child of node i.
+				 */
+    Int Order [ ],		/* output order, of size nn.  Order [i] = k
+				 * if node i is the kth node of the reordered
+				 * tree. */
+    Int Stack [ ]		/* workspace of size nn */
+#ifndef NDEBUG
+    , Int nn			/* nodes are in the range 0..nn-1. */
+#endif
+)
+{
+    Int f, head, h, i ;
+
+#if 0
+    /* --------------------------------------------------------------------- */
+    /* recursive version (Stack [ ] is not used): */
+    /* --------------------------------------------------------------------- */
+
+    /* this is simple, but can caouse stack overflow if nn is large */
+    i = root ;
+    for (f = Child [i] ; f != EMPTY ; f = Sibling [f])
+    {
+	k = AMD_post_tree (f, k, Child, Sibling, Order, Stack, nn) ;
+    }
+    Order [i] = k++ ;
+    return (k) ;
+#endif
+
+    /* --------------------------------------------------------------------- */
+    /* non-recursive version, using an explicit stack */
+    /* --------------------------------------------------------------------- */
+
+    /* push root on the stack */
+    head = 0 ;
+    Stack [0] = root ;
+
+    while (head >= 0)
+    {
+	/* get head of stack */
+	ASSERT (head < nn) ;
+	i = Stack [head] ;
+	AMD_DEBUG1 (("head of stack "ID" \n", i)) ;
+	ASSERT (i >= 0 && i < nn) ;
+
+	if (Child [i] != EMPTY)
+	{
+	    /* the children of i are not yet ordered */
+	    /* push each child onto the stack in reverse order */
+	    /* so that small ones at the head of the list get popped first */
+	    /* and the biggest one at the end of the list gets popped last */
+	    for (f = Child [i] ; f != EMPTY ; f = Sibling [f])
+	    {
+		head++ ;
+		ASSERT (head < nn) ;
+		ASSERT (f >= 0 && f < nn) ;
+	    }
+	    h = head ;
+	    ASSERT (head < nn) ;
+	    for (f = Child [i] ; f != EMPTY ; f = Sibling [f])
+	    {
+		ASSERT (h > 0) ;
+		Stack [h--] = f ;
+		AMD_DEBUG1 (("push "ID" on stack\n", f)) ;
+		ASSERT (f >= 0 && f < nn) ;
+	    }
+	    ASSERT (Stack [h] == i) ;
+
+	    /* delete child list so that i gets ordered next time we see it */
+	    Child [i] = EMPTY ;
+	}
+	else
+	{
+	    /* the children of i (if there were any) are already ordered */
+	    /* remove i from the stack and order it.  Front i is kth front */
+	    head-- ;
+	    AMD_DEBUG1 (("pop "ID" order "ID"\n", i, k)) ;
+	    Order [i] = k++ ;
+	    ASSERT (k <= nn) ;
+	}
+
+#ifndef NDEBUG
+	AMD_DEBUG1 (("\nStack:")) ;
+	for (h = head ; h >= 0 ; h--)
+	{
+	    Int j = Stack [h] ;
+	    AMD_DEBUG1 ((" "ID, j)) ;
+	    ASSERT (j >= 0 && j < nn) ;
+	}
+	AMD_DEBUG1 (("\n\n")) ;
+	ASSERT (head < nn) ;
+#endif
+
+    }
+    return (k) ;
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_postorder.c b/lin_sys/direct/qdldl/amd/src/amd_postorder.c
new file mode 100644
index 0000000..e5aea7b
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_postorder.c
@@ -0,0 +1,206 @@
+/* ========================================================================= */
+/* === AMD_postorder ======================================================= */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* Perform a postordering (via depth-first search) of an assembly tree. */
+
+#include "amd_internal.h"
+
+GLOBAL void AMD_postorder
+(
+    /* inputs, not modified on output: */
+    Int nn,		/* nodes are in the range 0..nn-1 */
+    Int Parent [ ],	/* Parent [j] is the parent of j, or EMPTY if root */
+    Int Nv [ ],		/* Nv [j] > 0 number of pivots represented by node j,
+			 * or zero if j is not a node. */
+    Int Fsize [ ],	/* Fsize [j]: size of node j */
+
+    /* output, not defined on input: */
+    Int Order [ ],	/* output post-order */
+
+    /* workspaces of size nn: */
+    Int Child [ ],
+    Int Sibling [ ],
+    Int Stack [ ]
+)
+{
+    Int i, j, k, parent, frsize, f, fprev, maxfrsize, bigfprev, bigf, fnext ;
+
+    for (j = 0 ; j < nn ; j++)
+    {
+	Child [j] = EMPTY ;
+	Sibling [j] = EMPTY ;
+    }
+
+    /* --------------------------------------------------------------------- */
+    /* place the children in link lists - bigger elements tend to be last */
+    /* --------------------------------------------------------------------- */
+
+    for (j = nn-1 ; j >= 0 ; j--)
+    {
+	if (Nv [j] > 0)
+	{
+	    /* this is an element */
+	    parent = Parent [j] ;
+	    if (parent != EMPTY)
+	    {
+		/* place the element in link list of the children its parent */
+		/* bigger elements will tend to be at the end of the list */
+		Sibling [j] = Child [parent] ;
+		Child [parent] = j ;
+	    }
+	}
+    }
+
+#ifndef NDEBUG
+    {
+	Int nels, ff, nchild ;
+	AMD_DEBUG1 (("\n\n================================ AMD_postorder:\n"));
+	nels = 0 ;
+	for (j = 0 ; j < nn ; j++)
+	{
+	    if (Nv [j] > 0)
+	    {
+		AMD_DEBUG1 (( ""ID" :  nels "ID" npiv "ID" size "ID
+		    " parent "ID" maxfr "ID"\n", j, nels,
+		    Nv [j], Fsize [j], Parent [j], Fsize [j])) ;
+		/* this is an element */
+		/* dump the link list of children */
+		nchild = 0 ;
+		AMD_DEBUG1 (("    Children: ")) ;
+		for (ff = Child [j] ; ff != EMPTY ; ff = Sibling [ff])
+		{
+		    AMD_DEBUG1 ((ID" ", ff)) ;
+		    ASSERT (Parent [ff] == j) ;
+		    nchild++ ;
+		    ASSERT (nchild < nn) ;
+		}
+		AMD_DEBUG1 (("\n")) ;
+		parent = Parent [j] ;
+		if (parent != EMPTY)
+		{
+		    ASSERT (Nv [parent] > 0) ;
+		}
+		nels++ ;
+	    }
+	}
+    }
+    AMD_DEBUG1 (("\n\nGo through the children of each node, and put\n"
+		 "the biggest child last in each list:\n")) ;
+#endif
+
+    /* --------------------------------------------------------------------- */
+    /* place the largest child last in the list of children for each node */
+    /* --------------------------------------------------------------------- */
+
+    for (i = 0 ; i < nn ; i++)
+    {
+	if (Nv [i] > 0 && Child [i] != EMPTY)
+	{
+
+#ifndef NDEBUG
+	    Int nchild ;
+	    AMD_DEBUG1 (("Before partial sort, element "ID"\n", i)) ;
+	    nchild = 0 ;
+	    for (f = Child [i] ; f != EMPTY ; f = Sibling [f])
+	    {
+		ASSERT (f >= 0 && f < nn) ;
+		AMD_DEBUG1 (("      f: "ID"  size: "ID"\n", f, Fsize [f])) ;
+		nchild++ ;
+		ASSERT (nchild <= nn) ;
+	    }
+#endif
+
+	    /* find the biggest element in the child list */
+	    fprev = EMPTY ;
+	    maxfrsize = EMPTY ;
+	    bigfprev = EMPTY ;
+	    bigf = EMPTY ;
+	    for (f = Child [i] ; f != EMPTY ; f = Sibling [f])
+	    {
+		ASSERT (f >= 0 && f < nn) ;
+		frsize = Fsize [f] ;
+		if (frsize >= maxfrsize)
+		{
+		    /* this is the biggest seen so far */
+		    maxfrsize = frsize ;
+		    bigfprev = fprev ;
+		    bigf = f ;
+		}
+		fprev = f ;
+	    }
+	    ASSERT (bigf != EMPTY) ;
+
+	    fnext = Sibling [bigf] ;
+
+	    AMD_DEBUG1 (("bigf "ID" maxfrsize "ID" bigfprev "ID" fnext "ID
+		" fprev " ID"\n", bigf, maxfrsize, bigfprev, fnext, fprev)) ;
+
+	    if (fnext != EMPTY)
+	    {
+		/* if fnext is EMPTY then bigf is already at the end of list */
+
+		if (bigfprev == EMPTY)
+		{
+		    /* delete bigf from the element of the list */
+		    Child [i] = fnext ;
+		}
+		else
+		{
+		    /* delete bigf from the middle of the list */
+		    Sibling [bigfprev] = fnext ;
+		}
+
+		/* put bigf at the end of the list */
+		Sibling [bigf] = EMPTY ;
+		ASSERT (Child [i] != EMPTY) ;
+		ASSERT (fprev != bigf) ;
+		ASSERT (fprev != EMPTY) ;
+		Sibling [fprev] = bigf ;
+	    }
+
+#ifndef NDEBUG
+	    AMD_DEBUG1 (("After partial sort, element "ID"\n", i)) ;
+	    for (f = Child [i] ; f != EMPTY ; f = Sibling [f])
+	    {
+		ASSERT (f >= 0 && f < nn) ;
+		AMD_DEBUG1 (("        "ID"  "ID"\n", f, Fsize [f])) ;
+		ASSERT (Nv [f] > 0) ;
+		nchild-- ;
+	    }
+	    ASSERT (nchild == 0) ;
+#endif
+
+	}
+    }
+
+    /* --------------------------------------------------------------------- */
+    /* postorder the assembly tree */
+    /* --------------------------------------------------------------------- */
+
+    for (i = 0 ; i < nn ; i++)
+    {
+	Order [i] = EMPTY ;
+    }
+
+    k = 0 ;
+
+    for (i = 0 ; i < nn ; i++)
+    {
+	if (Parent [i] == EMPTY && Nv [i] > 0)
+	{
+	    AMD_DEBUG1 (("Root of assembly tree "ID"\n", i)) ;
+	    k = AMD_post_tree (i, k, Child, Sibling, Order, Stack
+#ifndef NDEBUG
+		, nn
+#endif
+		) ;
+	}
+    }
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_preprocess.c b/lin_sys/direct/qdldl/amd/src/amd_preprocess.c
new file mode 100644
index 0000000..a8139c3
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_preprocess.c
@@ -0,0 +1,118 @@
+/* ========================================================================= */
+/* === AMD_preprocess ====================================================== */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* Sorts, removes duplicate entries, and transposes from the nonzero pattern of
+ * a column-form matrix A, to obtain the matrix R.  The input matrix can have
+ * duplicate entries and/or unsorted columns (AMD_valid (n,Ap,Ai) must not be
+ * AMD_INVALID).
+ *
+ * This input condition is NOT checked.  This routine is not user-callable.
+ */
+
+#include "amd_internal.h"
+
+/* ========================================================================= */
+/* === AMD_preprocess ====================================================== */
+/* ========================================================================= */
+
+/* AMD_preprocess does not check its input for errors or allocate workspace.
+ * On input, the condition (AMD_valid (n,n,Ap,Ai) != AMD_INVALID) must hold.
+ */
+
+GLOBAL void AMD_preprocess
+(
+    Int n,		/* input matrix: A is n-by-n */
+    const Int Ap [ ],	/* size n+1 */
+    const Int Ai [ ],	/* size nz = Ap [n] */
+
+    /* output matrix R: */
+    Int Rp [ ],		/* size n+1 */
+    Int Ri [ ],		/* size nz (or less, if duplicates present) */
+
+    Int W [ ],		/* workspace of size n */
+    Int Flag [ ]	/* workspace of size n */
+)
+{
+
+    /* --------------------------------------------------------------------- */
+    /* local variables */
+    /* --------------------------------------------------------------------- */
+
+    Int i, j, p, p2 ;
+
+    ASSERT (AMD_valid (n, n, Ap, Ai) != AMD_INVALID) ;
+
+    /* --------------------------------------------------------------------- */
+    /* count the entries in each row of A (excluding duplicates) */
+    /* --------------------------------------------------------------------- */
+
+    for (i = 0 ; i < n ; i++)
+    {
+	W [i] = 0 ;		/* # of nonzeros in row i (excl duplicates) */
+	Flag [i] = EMPTY ;	/* Flag [i] = j if i appears in column j */
+    }
+    for (j = 0 ; j < n ; j++)
+    {
+	p2 = Ap [j+1] ;
+	for (p = Ap [j] ; p < p2 ; p++)
+	{
+	    i = Ai [p] ;
+	    if (Flag [i] != j)
+	    {
+		/* row index i has not yet appeared in column j */
+		W [i]++ ;	    /* one more entry in row i */
+		Flag [i] = j ;	    /* flag row index i as appearing in col j*/
+	    }
+	}
+    }
+
+    /* --------------------------------------------------------------------- */
+    /* compute the row pointers for R */
+    /* --------------------------------------------------------------------- */
+
+    Rp [0] = 0 ;
+    for (i = 0 ; i < n ; i++)
+    {
+	Rp [i+1] = Rp [i] + W [i] ;
+    }
+    for (i = 0 ; i < n ; i++)
+    {
+	W [i] = Rp [i] ;
+	Flag [i] = EMPTY ;
+    }
+
+    /* --------------------------------------------------------------------- */
+    /* construct the row form matrix R */
+    /* --------------------------------------------------------------------- */
+
+    /* R = row form of pattern of A */
+    for (j = 0 ; j < n ; j++)
+    {
+	p2 = Ap [j+1] ;
+	for (p = Ap [j] ; p < p2 ; p++)
+	{
+	    i = Ai [p] ;
+	    if (Flag [i] != j)
+	    {
+		/* row index i has not yet appeared in column j */
+		Ri [W [i]++] = j ;  /* put col j in row i */
+		Flag [i] = j ;	    /* flag row index i as appearing in col j*/
+	    }
+	}
+    }
+
+#ifndef NDEBUG
+    ASSERT (AMD_valid (n, n, Rp, Ri) == AMD_OK) ;
+    for (j = 0 ; j < n ; j++)
+    {
+	ASSERT (W [j] == Rp [j+1]) ;
+    }
+#endif
+}
diff --git a/lin_sys/direct/qdldl/amd/src/amd_valid.c b/lin_sys/direct/qdldl/amd/src/amd_valid.c
new file mode 100644
index 0000000..609abca
--- /dev/null
+++ b/lin_sys/direct/qdldl/amd/src/amd_valid.c
@@ -0,0 +1,92 @@
+/* ========================================================================= */
+/* === AMD_valid =========================================================== */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* AMD, Copyright (c) Timothy A. Davis,					     */
+/* Patrick R. Amestoy, and Iain S. Duff.  See ../README.txt for License.     */
+/* email: DrTimothyAldenDavis@gmail.com                                      */
+/* ------------------------------------------------------------------------- */
+
+/* Check if a column-form matrix is valid or not.  The matrix A is
+ * n_row-by-n_col.  The row indices of entries in column j are in
+ * Ai [Ap [j] ... Ap [j+1]-1].  Required conditions are:
+ *
+ *	n_row >= 0
+ *	n_col >= 0
+ *	nz = Ap [n_col] >= 0	    number of entries in the matrix
+ *	Ap [0] == 0
+ *	Ap [j] <= Ap [j+1] for all j in the range 0 to n_col.
+ *      Ai [0 ... nz-1] must be in the range 0 to n_row-1.
+ *
+ * If any of the above conditions hold, AMD_INVALID is returned.  If the
+ * following condition holds, AMD_OK_BUT_JUMBLED is returned (a warning,
+ * not an error):
+ *
+ *	row indices in Ai [Ap [j] ... Ap [j+1]-1] are not sorted in ascending
+ *	    order, and/or duplicate entries exist.
+ *
+ * Otherwise, AMD_OK is returned.
+ *
+ * In v1.2 and earlier, this function returned TRUE if the matrix was valid
+ * (now returns AMD_OK), or FALSE otherwise (now returns AMD_INVALID or
+ * AMD_OK_BUT_JUMBLED).
+ */
+
+#include "amd_internal.h"
+
+GLOBAL Int AMD_valid
+(
+    /* inputs, not modified on output: */
+    Int n_row,		/* A is n_row-by-n_col */
+    Int n_col,
+    const Int Ap [ ],	/* column pointers of A, of size n_col+1 */
+    const Int Ai [ ]	/* row indices of A, of size nz = Ap [n_col] */
+)
+{
+    Int nz, j, p1, p2, ilast, i, p, result = AMD_OK ;
+
+    if (n_row < 0 || n_col < 0 || Ap == NULL || Ai == NULL)
+    {
+	return (AMD_INVALID) ;
+    }
+    nz = Ap [n_col] ;
+    if (Ap [0] != 0 || nz < 0)
+    {
+	/* column pointers must start at Ap [0] = 0, and Ap [n] must be >= 0 */
+	AMD_DEBUG0 (("column 0 pointer bad or nz < 0\n")) ;
+	return (AMD_INVALID) ;
+    }
+    for (j = 0 ; j < n_col ; j++)
+    {
+	p1 = Ap [j] ;
+	p2 = Ap [j+1] ;
+	AMD_DEBUG2 (("\nColumn: "ID" p1: "ID" p2: "ID"\n", j, p1, p2)) ;
+	if (p1 > p2)
+	{
+	    /* column pointers must be ascending */
+	    AMD_DEBUG0 (("column "ID" pointer bad\n", j)) ;
+	    return (AMD_INVALID) ;
+	}
+	ilast = EMPTY ;
+	for (p = p1 ; p < p2 ; p++)
+	{
+	    i = Ai [p] ;
+	    AMD_DEBUG3 (("row: "ID"\n", i)) ;
+	    if (i < 0 || i >= n_row)
+	    {
+		/* row index out of range */
+		AMD_DEBUG0 (("index out of range, col "ID" row "ID"\n", j, i));
+		return (AMD_INVALID) ;
+	    }
+	    if (i <= ilast)
+	    {
+		/* row index unsorted, or duplicate entry present */
+		AMD_DEBUG1 (("index unsorted/dupl col "ID" row "ID"\n", j, i));
+		result = AMD_OK_BUT_JUMBLED ;
+	    }
+	    ilast = i ;
+	}
+    }
+    return (result) ;
+}
diff --git a/lin_sys/direct/qdldl/qdldl_interface.c b/lin_sys/direct/qdldl/qdldl_interface.c
new file mode 100644
index 0000000..43f30ef
--- /dev/null
+++ b/lin_sys/direct/qdldl/qdldl_interface.c
@@ -0,0 +1,413 @@
+#include "glob_opts.h"
+
+#include "qdldl.h"
+#include "qdldl_interface.h"
+
+#ifndef EMBEDDED
+#include "amd.h"
+#endif
+
+#if EMBEDDED != 1
+#include "kkt.h"
+#endif
+
+#ifndef EMBEDDED
+
+// Free LDL Factorization structure
+void free_linsys_solver_qdldl(qdldl_solver *s) {
+    if (s) {
+        if (s->L)           csc_spfree(s->L);
+        if (s->P)           c_free(s->P);
+        if (s->Dinv)        c_free(s->Dinv);
+        if (s->bp)          c_free(s->bp);
+        if (s->sol)         c_free(s->sol);
+        if (s->rho_inv_vec) c_free(s->rho_inv_vec);
+
+        // These are required for matrix updates
+        if (s->Pdiag_idx) c_free(s->Pdiag_idx);
+        if (s->KKT)       csc_spfree(s->KKT);
+        if (s->PtoKKT)    c_free(s->PtoKKT);
+        if (s->AtoKKT)    c_free(s->AtoKKT);
+        if (s->rhotoKKT)  c_free(s->rhotoKKT);
+
+        // QDLDL workspace
+        if (s->D)         c_free(s->D);
+        if (s->etree)     c_free(s->etree);
+        if (s->Lnz)       c_free(s->Lnz);
+        if (s->iwork)     c_free(s->iwork);
+        if (s->bwork)     c_free(s->bwork);
+        if (s->fwork)     c_free(s->fwork);
+        c_free(s);
+
+    }
+}
+
+
+/**
+ * Compute LDL factorization of matrix A
+ * @param  A    Matrix to be factorized
+ * @param  p    Private workspace
+ * @param  nvar Number of QP variables
+ * @return      exitstatus (0 is good)
+ */
+static c_int LDL_factor(csc *A,  qdldl_solver * p, c_int nvar){
+
+    c_int sum_Lnz;
+    c_int factor_status;
+
+    // Compute elimination tree
+    sum_Lnz = QDLDL_etree(A->n, A->p, A->i, p->iwork, p->Lnz, p->etree);
+
+    if (sum_Lnz < 0){
+      // Error
+#ifdef PRINTING
+      c_eprint("Error in KKT matrix LDL factorization when computing the elimination tree.");
+      if(sum_Lnz == -1){
+        c_eprint("Matrix is not perfectly upper triangular.");
+      }
+      else if(sum_Lnz == -2){
+        c_eprint("Integer overflow in L nonzero count.");
+      }
+#endif
+      return sum_Lnz;
+    }
+
+    // Allocate memory for Li and Lx
+    p->L->i = (c_int *)c_malloc(sizeof(c_int)*sum_Lnz);
+    p->L->x = (c_float *)c_malloc(sizeof(c_float)*sum_Lnz);
+    p->L->nzmax = sum_Lnz;
+
+    // Factor matrix
+    factor_status = QDLDL_factor(A->n, A->p, A->i, A->x,
+                                 p->L->p, p->L->i, p->L->x,
+                                 p->D, p->Dinv, p->Lnz,
+                                 p->etree, p->bwork, p->iwork, p->fwork);
+
+
+    if (factor_status < 0){
+      // Error
+#ifdef PRINTING
+      c_eprint("Error in KKT matrix LDL factorization when computing the nonzero elements. There are zeros in the diagonal matrix");
+#endif
+      return factor_status;
+    } else if (factor_status < nvar) {
+      // Error: Number of positive elements of D should be equal to nvar
+#ifdef PRINTING
+      c_eprint("Error in KKT matrix LDL factorization when computing the nonzero elements. The problem seems to be non-convex");
+#endif
+      return -2;
+    }
+
+    return 0;
+
+}
+
+
+static c_int permute_KKT(csc ** KKT, qdldl_solver * p, c_int Pnz, c_int Anz, c_int m, c_int * PtoKKT, c_int * AtoKKT, c_int * rhotoKKT){
+    c_float *info;
+    c_int amd_status;
+    c_int * Pinv;
+    csc *KKT_temp;
+    c_int * KtoPKPt;
+    c_int i; // Indexing
+
+    info = (c_float *)c_malloc(AMD_INFO * sizeof(c_float));
+
+    // Compute permutation matrix P using AMD
+#ifdef DLONG
+    amd_status = amd_l_order((*KKT)->n, (*KKT)->p, (*KKT)->i, p->P, (c_float *)OSQP_NULL, info);
+#else
+    amd_status = amd_order((*KKT)->n, (*KKT)->p, (*KKT)->i, p->P, (c_float *)OSQP_NULL, info);
+#endif
+    if (amd_status < 0) {
+        // Free Amd info and return an error
+        c_free(info);
+        return amd_status;
+    }
+
+
+    // Inverse of the permutation vector
+    Pinv = csc_pinv(p->P, (*KKT)->n);
+
+    // Permute KKT matrix
+    if (!PtoKKT && !AtoKKT && !rhotoKKT){  // No vectors to be stored
+        // Assign values of mapping
+        KKT_temp = csc_symperm((*KKT), Pinv, OSQP_NULL, 1);
+    }
+    else {
+        // Allocate vector of mappings from unpermuted to permuted
+        KtoPKPt = c_malloc((*KKT)->p[(*KKT)->n] * sizeof(c_int));
+        KKT_temp = csc_symperm((*KKT), Pinv, KtoPKPt, 1);
+
+        // Update vectors PtoKKT, AtoKKT and rhotoKKT
+        if (PtoKKT){
+            for (i = 0; i < Pnz; i++){
+                PtoKKT[i] = KtoPKPt[PtoKKT[i]];
+            }
+        }
+        if (AtoKKT){
+            for (i = 0; i < Anz; i++){
+                AtoKKT[i] = KtoPKPt[AtoKKT[i]];
+            }
+        }
+        if (rhotoKKT){
+            for (i = 0; i < m; i++){
+                rhotoKKT[i] = KtoPKPt[rhotoKKT[i]];
+            }
+        }
+
+        // Cleanup vector of mapping
+        c_free(KtoPKPt);
+    }
+
+    // Cleanup
+    // Free previous KKT matrix and assign pointer to new one
+    csc_spfree((*KKT));
+    (*KKT) = KKT_temp;
+    // Free Pinv
+    c_free(Pinv);
+    // Free Amd info
+    c_free(info);
+
+    return 0;
+}
+
+
+// Initialize LDL Factorization structure
+c_int init_linsys_solver_qdldl(qdldl_solver ** sp, const csc * P, const csc * A, c_float sigma, const c_float * rho_vec, c_int polish){
+
+    // Define Variables
+    csc * KKT_temp;     // Temporary KKT pointer
+    c_int i;            // Loop counter
+    c_int n_plus_m;     // Define n_plus_m dimension
+
+    // Allocate private structure to store KKT factorization
+    qdldl_solver *s;
+    s = c_calloc(1, sizeof(qdldl_solver));
+    *sp = s;
+
+    // Size of KKT
+    s->n = P->n;
+    s->m = A->m;
+    n_plus_m = s->n + s->m;
+
+    // Sigma parameter
+    s->sigma = sigma;
+
+    // Polishing flag
+    s->polish = polish;
+
+    // Link Functions
+    s->solve = &solve_linsys_qdldl;
+
+#ifndef EMBEDDED
+    s->free = &free_linsys_solver_qdldl;
+#endif
+
+#if EMBEDDED != 1
+    s->update_matrices = &update_linsys_solver_matrices_qdldl;
+    s->update_rho_vec = &update_linsys_solver_rho_vec_qdldl;
+#endif
+
+    // Assign type
+    s->type = QDLDL_SOLVER;
+
+    // Set number of threads to 1 (single threaded)
+    s->nthreads = 1;
+
+    // Sparse matrix L (lower triangular)
+    // NB: We don not allocate L completely (CSC elements)
+    //      L will be allocated during the factorization depending on the
+    //      resulting number of elements.
+    s->L = c_malloc(sizeof(csc));
+    s->L->m = n_plus_m;
+    s->L->n = n_plus_m;
+    s->L->nz = -1;
+
+    // Diagonal matrix stored as a vector D
+    s->Dinv = (QDLDL_float *)c_malloc(sizeof(QDLDL_float) * n_plus_m);
+    s->D    = (QDLDL_float *)c_malloc(sizeof(QDLDL_float) * n_plus_m);
+
+    // Permutation vector P
+    s->P    = (QDLDL_int *)c_malloc(sizeof(QDLDL_int) * n_plus_m);
+
+    // Working vector
+    s->bp   = (QDLDL_float *)c_malloc(sizeof(QDLDL_float) * n_plus_m);
+
+    // Solution vector
+    s->sol  = (QDLDL_float *)c_malloc(sizeof(QDLDL_float) * n_plus_m);
+
+    // Parameter vector
+    s->rho_inv_vec = (c_float *)c_malloc(sizeof(c_float) * s->m);
+
+    // Elimination tree workspace
+    s->etree = (QDLDL_int *)c_malloc(n_plus_m * sizeof(QDLDL_int));
+    s->Lnz   = (QDLDL_int *)c_malloc(n_plus_m * sizeof(QDLDL_int));
+
+    // Preallocate L matrix (Lx and Li are sparsity dependent)
+    s->L->p = (c_int *)c_malloc((n_plus_m+1) * sizeof(QDLDL_int));
+
+    // Lx and Li are sparsity dependent, so set them to
+    // null initially so we don't try to free them prematurely
+    s->L->i = OSQP_NULL;
+    s->L->x = OSQP_NULL;
+
+    // Preallocate workspace
+    s->iwork = (QDLDL_int *)c_malloc(sizeof(QDLDL_int)*(3*n_plus_m));
+    s->bwork = (QDLDL_bool *)c_malloc(sizeof(QDLDL_bool)*n_plus_m);
+    s->fwork = (QDLDL_float *)c_malloc(sizeof(QDLDL_float)*n_plus_m);
+
+    // Form and permute KKT matrix
+    if (polish){ // Called from polish()
+        // Use s->rho_inv_vec for storing param2 = vec(delta)
+        for (i = 0; i < A->m; i++){
+            s->rho_inv_vec[i] = sigma;
+        }
+
+        KKT_temp = form_KKT(P, A, 0, sigma, s->rho_inv_vec, OSQP_NULL, OSQP_NULL, OSQP_NULL, OSQP_NULL, OSQP_NULL);
+
+        // Permute matrix
+        if (KKT_temp)
+            permute_KKT(&KKT_temp, s, OSQP_NULL, OSQP_NULL, OSQP_NULL, OSQP_NULL, OSQP_NULL, OSQP_NULL);
+    }
+    else { // Called from ADMM algorithm
+
+        // Allocate vectors of indices
+        s->PtoKKT = c_malloc((P->p[P->n]) * sizeof(c_int));
+        s->AtoKKT = c_malloc((A->p[A->n]) * sizeof(c_int));
+        s->rhotoKKT = c_malloc((A->m) * sizeof(c_int));
+
+        // Use p->rho_inv_vec for storing param2 = rho_inv_vec
+        for (i = 0; i < A->m; i++){
+            s->rho_inv_vec[i] = 1. / rho_vec[i];
+        }
+
+        KKT_temp = form_KKT(P, A, 0, sigma, s->rho_inv_vec,
+                            s->PtoKKT, s->AtoKKT,
+                            &(s->Pdiag_idx), &(s->Pdiag_n), s->rhotoKKT);
+
+        // Permute matrix
+        if (KKT_temp)
+            permute_KKT(&KKT_temp, s, P->p[P->n], A->p[A->n], A->m, s->PtoKKT, s->AtoKKT, s->rhotoKKT);
+    }
+
+    // Check if matrix has been created
+    if (!KKT_temp){
+#ifdef PRINTING
+        c_eprint("Error forming and permuting KKT matrix");
+#endif
+        free_linsys_solver_qdldl(s);
+        *sp = OSQP_NULL;
+        return OSQP_LINSYS_SOLVER_INIT_ERROR;
+    }
+
+    // Factorize the KKT matrix
+    if (LDL_factor(KKT_temp, s, P->n) < 0) {
+        csc_spfree(KKT_temp);
+        free_linsys_solver_qdldl(s);
+        *sp = OSQP_NULL;
+        return OSQP_NONCVX_ERROR;
+    }
+
+    if (polish){ // If KKT passed, assign it to KKT_temp
+        // Polish, no need for KKT_temp
+        csc_spfree(KKT_temp);
+    }
+    else { // If not embedded option 1 copy pointer to KKT_temp. Do not free it.
+        s->KKT = KKT_temp;
+    }
+
+
+    // No error
+    return 0;
+}
+
+#endif  // EMBEDDED
+
+
+// Permute x = P*b using P
+void permute_x(c_int n, c_float * x, const c_float * b, const c_int * P) {
+    c_int j;
+    for (j = 0 ; j < n ; j++) x[j] = b[P[j]];
+}
+
+// Permute x = P'*b using P
+void permutet_x(c_int n, c_float * x, const c_float * b, const c_int * P) {
+    c_int j;
+    for (j = 0 ; j < n ; j++) x[P[j]] = b[j];
+}
+
+
+static void LDLSolve(c_float *x, c_float *b, const csc *L, const c_float *Dinv, const c_int *P, c_float *bp) {
+    /* solves P'LDL'P x = b for x */
+    permute_x(L->n, bp, b, P);
+    QDLDL_solve(L->n, L->p, L->i, L->x, Dinv, bp);
+    permutet_x(L->n, x, bp, P);
+
+}
+
+
+c_int solve_linsys_qdldl(qdldl_solver * s, c_float * b) {
+    c_int j;
+
+#ifndef EMBEDDED
+    if (s->polish) {
+        /* stores solution to the KKT system in b */
+        LDLSolve(b, b, s->L, s->Dinv, s->P, s->bp);
+    } else {
+#endif
+        /* stores solution to the KKT system in s->sol */
+        LDLSolve(s->sol, b, s->L, s->Dinv, s->P, s->bp);
+
+        /* copy x_tilde from s->sol */
+        for (j = 0 ; j < s->n ; j++) {
+            b[j] = s->sol[j];
+        }
+
+        /* compute z_tilde from b and s->sol */
+        for (j = 0 ; j < s->m ; j++) {
+            b[j + s->n] += s->rho_inv_vec[j] * s->sol[j + s->n];
+        }
+#ifndef EMBEDDED
+    }
+#endif
+
+    return 0;
+}
+
+
+#if EMBEDDED != 1
+// Update private structure with new P and A
+c_int update_linsys_solver_matrices_qdldl(qdldl_solver * s, const csc *P, const csc *A) {
+
+    // Update KKT matrix with new P
+    update_KKT_P(s->KKT, P, s->PtoKKT, s->sigma, s->Pdiag_idx, s->Pdiag_n);
+
+    // Update KKT matrix with new A
+    update_KKT_A(s->KKT, A, s->AtoKKT);
+
+    return (QDLDL_factor(s->KKT->n, s->KKT->p, s->KKT->i, s->KKT->x,
+        s->L->p, s->L->i, s->L->x, s->D, s->Dinv, s->Lnz,
+        s->etree, s->bwork, s->iwork, s->fwork) < 0);
+
+}
+
+
+c_int update_linsys_solver_rho_vec_qdldl(qdldl_solver * s, const c_float * rho_vec){
+    c_int i;
+
+    // Update internal rho_inv_vec
+    for (i = 0; i < s->m; i++){
+        s->rho_inv_vec[i] = 1. / rho_vec[i];
+    }
+
+    // Update KKT matrix with new rho_vec
+    update_KKT_param2(s->KKT, s->rho_inv_vec, s->rhotoKKT, s->m);
+
+    return (QDLDL_factor(s->KKT->n, s->KKT->p, s->KKT->i, s->KKT->x,
+        s->L->p, s->L->i, s->L->x, s->D, s->Dinv, s->Lnz,
+        s->etree, s->bwork, s->iwork, s->fwork) < 0);
+}
+
+
+#endif
diff --git a/lin_sys/direct/qdldl/qdldl_interface.h b/lin_sys/direct/qdldl/qdldl_interface.h
new file mode 100644
index 0000000..7995ef3
--- /dev/null
+++ b/lin_sys/direct/qdldl/qdldl_interface.h
@@ -0,0 +1,135 @@
+#ifndef QDLDL_INTERFACE_H
+#define QDLDL_INTERFACE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "types.h"
+#include "qdldl_types.h"
+
+/**
+ * QDLDL solver structure
+ */
+typedef struct qdldl qdldl_solver;
+
+struct qdldl {
+    enum linsys_solver_type type;
+
+    /**
+     * @name Functions
+     * @{
+     */
+    c_int (*solve)(struct qdldl * self, c_float * b);
+
+#ifndef EMBEDDED
+    void (*free)(struct qdldl * self); ///< Free workspace (only if desktop)
+#endif
+
+    // This used only in non embedded or embedded 2 version
+#if EMBEDDED != 1
+    c_int (*update_matrices)(struct qdldl * self, const csc *P, const csc *A);  ///< Update solver matrices
+    c_int (*update_rho_vec)(struct qdldl * self, const c_float * rho_vec);      ///< Update rho_vec parameter
+#endif
+
+#ifndef EMBEDDED
+    c_int nthreads;
+#endif
+    /** @} */
+
+    /**
+     * @name Attributes
+     * @{
+     */
+    csc *L;                 ///< lower triangular matrix in LDL factorization
+    c_float *Dinv;          ///< inverse of diag matrix in LDL (as a vector)
+    c_int   *P;             ///< permutation of KKT matrix for factorization
+    c_float *bp;            ///< workspace memory for solves
+    c_float *sol;           ///< solution to the KKT system
+    c_float *rho_inv_vec;   ///< parameter vector
+    c_float sigma;          ///< scalar parameter
+#ifndef EMBEDDED
+    c_int polish;           ///< polishing flag
+#endif
+    c_int n;                ///< number of QP variables
+    c_int m;                ///< number of QP constraints
+
+
+#if EMBEDDED != 1
+    // These are required for matrix updates
+    c_int * Pdiag_idx, Pdiag_n;  ///< index and number of diagonal elements in P
+    csc   * KKT;                 ///< Permuted KKT matrix in sparse form (used to update P and A matrices)
+    c_int * PtoKKT, * AtoKKT;    ///< Index of elements from P and A to KKT matrix
+    c_int * rhotoKKT;            ///< Index of rho places in KKT matrix
+    // QDLDL Numeric workspace
+    QDLDL_float *D;
+    QDLDL_int   *etree;
+    QDLDL_int   *Lnz;
+    QDLDL_int   *iwork;
+    QDLDL_bool  *bwork;
+    QDLDL_float *fwork;
+#endif
+
+    /** @} */
+};
+
+
+
+/**
+ * Initialize QDLDL Solver
+ *
+ * @param  s         Pointer to a private structure
+ * @param  P         Cost function matrix (upper triangular form)
+ * @param  A         Constraints matrix
+ * @param  sigma     Algorithm parameter. If polish, then sigma = delta.
+ * @param  rho_vec   Algorithm parameter. If polish, then rho_vec = OSQP_NULL.
+ * @param  polish    Flag whether we are initializing for polish or not
+ * @return           Exitflag for error (0 if no errors)
+ */
+c_int init_linsys_solver_qdldl(qdldl_solver ** sp, const csc * P, const csc * A, c_float sigma, const c_float * rho_vec, c_int polish);
+
+/**
+ * Solve linear system and store result in b
+ * @param  s        Linear system solver structure
+ * @param  b        Right-hand side
+ * @return          Exitflag
+ */
+c_int solve_linsys_qdldl(qdldl_solver * s, c_float * b);
+
+
+#if EMBEDDED != 1
+/**
+ * Update linear system solver matrices
+ * @param  s        Linear system solver structure
+ * @param  P        Matrix P
+ * @param  A        Matrix A
+ * @return          Exitflag
+ */
+c_int update_linsys_solver_matrices_qdldl(qdldl_solver * s, const csc *P, const csc *A);
+
+
+
+
+/**
+ * Update rho_vec parameter in linear system solver structure
+ * @param  s        Linear system solver structure
+ * @param  rho_vec  new rho_vec value
+ * @return          exitflag
+ */
+c_int update_linsys_solver_rho_vec_qdldl(qdldl_solver * s, const c_float * rho_vec);
+
+#endif
+
+#ifndef EMBEDDED
+/**
+ * Free linear system solver
+ * @param s linear system solver object
+ */
+void free_linsys_solver_qdldl(qdldl_solver * s);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lin_sys/direct/qdldl/qdldl_sources b/lin_sys/direct/qdldl/qdldl_sources
new file mode 160000
index 0000000..7d16b70
--- /dev/null
+++ b/lin_sys/direct/qdldl/qdldl_sources
@@ -0,0 +1 @@
+Subproject commit 7d16b70a10a152682204d745d814b6eb63dc5cd2
diff --git a/lin_sys/lib_handler.c b/lin_sys/lib_handler.c
new file mode 100644
index 0000000..f3c8cca
--- /dev/null
+++ b/lin_sys/lib_handler.c
@@ -0,0 +1,155 @@
+#include "lib_handler.h"
+#include <ctype.h> // Needed for tolower functions
+
+#include "constants.h"
+#include "util.h"
+
+soHandle_t lh_load_lib(const char *libName) {
+    soHandle_t h = OSQP_NULL;
+
+    if (!libName) {
+        #ifdef PRINTING
+        c_eprint("no library name given");
+        #endif
+        return OSQP_NULL;
+    }
+
+#ifdef IS_WINDOWS
+    h = LoadLibrary (libName);
+    if (!h) {
+        #ifdef PRINTING
+        c_eprint("Windows error while loading dynamic library %s, error = %d",
+                libName, (int)GetLastError());
+        #endif
+    }
+#else
+    h = dlopen (libName, RTLD_LAZY);
+    if (!h) {
+        #ifdef PRINTING
+        c_eprint("Error while loading dynamic library %s: %s", libName, dlerror());
+        #endif
+    }
+#endif
+
+    return h;
+} /* lh_load_lib */
+
+
+c_int lh_unload_lib (soHandle_t h) {
+    c_int rc = 1;
+
+#ifdef IS_WINDOWS
+    rc = FreeLibrary (h);
+    rc = ! rc;
+#else
+    rc = dlclose (h);
+#endif
+
+    return rc;
+} /* LSL_unLoadLib */
+
+
+#ifdef IS_WINDOWS
+typedef FARPROC symtype;
+#else
+typedef void* symtype;
+#endif
+/** Loads a symbol from a dynamically linked library.
+ * This function is not defined in the header to allow a workaround for the problem that dlsym returns an object instead of a function pointer.
+ * However, Windows also needs special care.
+ *
+ * The method does six attempts to load the symbol. Next to its given name, it also tries variations of lower case and upper case form and with an extra underscore.
+ * @param h Handle of dynamically linked library.
+ * @param symName Name of the symbol to load.
+ * @return A pointer to the symbol, or OSQP_NULL if not found.
+ */
+symtype lh_load_sym (soHandle_t h, const char *symName) {
+    symtype s;
+    const char *from;
+    char *to;
+    const char *tripSym;
+    char* err;
+    char lcbuf[257];
+    char ucbuf[257];
+    char ocbuf[257];
+    size_t symLen;
+    int trip;
+
+    s = OSQP_NULL;
+    err = OSQP_NULL;
+
+    /* search in this order:
+     *  1. original
+     *  2. lower_
+     *  3. upper_
+     *  4. original_
+     *  5. lower
+     *  6. upper
+     */
+
+    symLen = 0;
+    for (trip = 1;  trip <= 6;  trip++) {
+        switch (trip) {
+        case 1:                             /* original */
+            tripSym = symName;
+            break;
+        case 2:                             /* lower_ */
+            for (from = symName, to = lcbuf;  *from;  from++, to++) {
+                *to = tolower(*from);
+            }
+            symLen = from - symName;
+            *to++ = '_';
+            *to = '\0';
+            tripSym = lcbuf;
+            break;
+        case 3:                             /* upper_ */
+            for (from = symName, to = ucbuf;  *from;  from++, to++) {
+                *to = toupper(*from);
+            }
+            *to++ = '_';
+            *to = '\0';
+            tripSym = ucbuf;
+            break;
+        case 4:                             /* original_ */
+            c_strcpy(ocbuf, symName);
+            ocbuf[symLen] = '_';
+            ocbuf[symLen+1] = '\0';
+            tripSym = ocbuf;
+            break;
+        case 5:                             /* lower */
+            lcbuf[symLen] = '\0';
+            tripSym = lcbuf;
+            break;
+        case 6:                             /* upper */
+            ucbuf[symLen] = '\0';
+            tripSym = ucbuf;
+            break;
+        default:
+            tripSym = symName;
+        } /* end switch */
+#ifdef IS_WINDOWS
+        s = GetProcAddress (h, tripSym);
+        if (s) {
+            return s;
+        } else {
+            #ifdef PRINTING
+            c_eprint("Cannot find symbol %s in dynamic library, error = %d",
+                    symName, (int)GetLastError());
+            #endif
+        }
+#else
+        s = dlsym (h, tripSym);
+        err = dlerror();  /* we have only one chance; a successive call to dlerror() returns OSQP_NULL */
+        if (err) {
+            #ifdef PRINTING
+            c_eprint("Cannot find symbol %s in dynamic library, error = %s",
+                    symName, err);
+            #endif
+        } else {
+            return s;
+        }
+#endif
+    } /* end loop over symbol name variations */
+
+    return OSQP_NULL;
+} /* lh_load_sym */
diff --git a/lin_sys/lib_handler.h b/lin_sys/lib_handler.h
new file mode 100644
index 0000000..0c3b5b6
--- /dev/null
+++ b/lin_sys/lib_handler.h
@@ -0,0 +1,44 @@
+#ifndef LIB_HANDLER_H
+#define LIB_HANDLER_H
+
+#include "glob_opts.h"
+
+#ifdef IS_WINDOWS
+#include <windows.h>
+typedef HINSTANCE soHandle_t;
+#else
+#include <unistd.h>
+#include <dlfcn.h>
+typedef void *soHandle_t;
+#endif
+
+#ifdef IS_WINDOWS
+#define SHAREDLIBEXT "dll"
+#elif defined IS_MAC
+#define SHAREDLIBEXT "dylib"
+#else
+#define SHAREDLIBEXT "so"
+#endif
+
+
+// NB: A shared library should be put into the shared library search path:
+//  Linux:    LD_LIBRARY_PATH
+//  Mac OSX:  DYLD_LIBRARY_PATH
+//  Windows:  PATH
+
+
+/** Loads a dynamically linked library.
+ * @param libname The name of the library to load.
+ * @return Shared library handle, or OSQP_NULL if failure.
+ */
+soHandle_t lh_load_lib(const char* libname);
+
+
+/** Unloads a shared library.
+ * @param libhandle Handle of shared library to unload.
+ * @return Zero on success, nonzero on failure.
+ */
+c_int lh_unload_lib(soHandle_t libhandle);
+
+
+#endif /*LIB_HANDLER_H*/
diff --git a/site/Gemfile b/site/Gemfile
new file mode 100644
index 0000000..c7ca611
--- /dev/null
+++ b/site/Gemfile
@@ -0,0 +1,34 @@
+source "https://rubygems.org"
+
+# Hello! This is where you manage which Jekyll version is used to run.
+# When you want to use a different version, change it below, save the
+# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
+#
+#     bundle exec jekyll serve
+#
+# This will help ensure the proper Jekyll version is running.
+# Happy Jekylling!
+# gem "jekyll", "~> 3.7.0"
+gem "github-pages", "~> 217", group: :jekyll_plugins
+
+gem "sprockets", "~> 3.7"
+
+
+# This is the default theme for new Jekyll sites. You may change this to anything you like.
+gem "minima", "~> 2.0"
+
+# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
+# uncomment the line below. To upgrade, run `bundle update github-pages`.
+# gem "github-pages", group: :jekyll_plugins
+
+# If you have any plugins, put them here!
+group :jekyll_plugins do
+  gem "jekyll-feed", "~> 0.6"
+  gem "jekyll-assets"
+end
+
+
+# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
+# gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
+gem "bootstrap", ">= 4.3.1"
+gem 'jquery-rails'
diff --git a/site/README.md b/site/README.md
new file mode 100644
index 0000000..0d0199c
--- /dev/null
+++ b/site/README.md
@@ -0,0 +1,6 @@
+# OSQP Solver Website
+
+Built with [Jekyll](https://jekyllrb.com/) and [Bootstrap](http://getbootstrap.com/).
+
+## TODO
+
diff --git a/site/_config.yml b/site/_config.yml
new file mode 100644
index 0000000..5bc06b3
--- /dev/null
+++ b/site/_config.yml
@@ -0,0 +1,86 @@
+# Welcome to Jekyll!
+#
+# This config file is meant for settings that affect your whole blog, values
+# which you are expected to set up once and rarely edit after that. If you find
+# yourself editing this file very often, consider using Jekyll's data files
+# feature for the data you need to update frequently.
+#
+# For technical reasons, this file is *NOT* reloaded automatically when you use
+# 'bundle exec jekyll serve'. If you change this file, please restart the server process.
+
+# Site settings
+# These are used to personalize your new site. If you look in the HTML files,
+# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.
+# You can create any custom variable you would like, and they will be accessible
+# in the templates via {{ site.myvariable }}.
+title: OSQP
+email: bartolomeo.stellato@gmail.com
+description: >- # this means to ignore newlines until "baseurl:"
+  OSQP is an efficient general purpose solver for quadratic programs.
+  It is open-source and suite all range of applications from embedded
+  to large-scale.
+baseurl: "/" # the subpath of your site, e.g. /blog
+url: "https://osqp.org" # the base hostname & protocol for your site, e.g. http://example.com
+# twitter_username: jekyllrb
+# github_username:  jekyll
+# facebook_link: https://www.facebook.com/groups/331532750262318/
+github_link: https://github.com/osqp/osqp
+google_analytics: UA-56714542-3
+discourse_link: https://osqp.discourse.group/
+
+# Which folders to include?
+# gh pages does not currently support a source directive,
+#   and assumes that all source files are at the root folder
+# source: src
+# destination: _site
+
+include:
+    - _pages
+    - _static
+
+
+# Dynamic Text on the Header
+dynamic-typing: True
+shuffle: False # Shuffle the lines.
+loop: True
+loop-count: False # Set False for infinite loop, or set any number for finite loop
+type-speed: 30
+back-speed: 10
+start-delay: 100
+delete-delay: 2000
+quotes:
+  - text: "A QP solver for everyone."
+  - text: "Efficient, scalable and embeddable."
+  - text: "Open-source, with many interfaces."
+  - text: "Did I say it runs on your toaster too?"
+
+
+# Conversion
+markdown:    kramdown
+highlighter: rouge
+
+# Markdown Processors
+kramdown:
+  input: GFM
+  auto_ids: true
+  syntax_highlighter: rouge
+
+theme: minima
+
+# Sass/SCSS
+sass:
+  sass_dir: _sass 
+  style: compressed # http://sass-lang.com/documentation/file.SASS_REFERENCE.html#output_style
+
+
+# Exclude from processing.
+# The following items will not be processed, by default. Create a custom list
+# to override the default setting.
+# exclude:
+#   - Gemfile
+#   - Gemfile.lock
+#   - node_modules
+#   - vendor/bundle/
+#   - vendor/cache/
+#   - vendor/gems/
+#   - vendor/ruby/
diff --git a/site/_data/credits.yml b/site/_data/credits.yml
new file mode 100644
index 0000000..142eed1
--- /dev/null
+++ b/site/_data/credits.yml
@@ -0,0 +1,38 @@
+- name: Bartolomeo Stellato
+  role: Main developer
+  affiliation: Princeton University
+  website: https://stellato.io
+
+- name: Goran Banjac
+  role: Main developer
+  affiliation: ETH Zurich
+  website: https://people.ee.ethz.ch/~gbanjac/
+
+- name: Paul Goulart
+  role: Methods and Maths
+  affiliation: University of Oxford
+  website: http://users.ox.ac.uk/~engs1373/
+
+- name: Stephen Boyd
+  role: Methods and Maths
+  affiliation: Stanford University
+  website: https://web.stanford.edu/~boyd/
+
+- name: Alberto Bemporad
+  role: Methods and Maths
+  affiliation: IMT Lucca
+  website: http://cse.lab.imtlucca.it/~bemporad/
+
+- name: Nicholas Moehle
+  role: Methods, Maths and Code Generation
+  affiliation: Stanford University
+  website: https://www.nicholasmoehle.com/
+
+- name: Michel Schubiger
+  role: GPU implementation
+  affiliation: Schindler R&D
+
+- name: John Lygeros
+  role: Methods and Maths
+  affiliation: ETH Zurich
+  website: https://control.ee.ethz.ch/people/profile.john-lygeros.html
diff --git a/site/_data/features.yml b/site/_data/features.yml
new file mode 100644
index 0000000..7927bbc
--- /dev/null
+++ b/site/_data/features.yml
@@ -0,0 +1,32 @@
+- title: Efficient
+  icon: fas fa-tachometer-alt
+  description: >
+    OSQP uses a specialized ADMM-based first-order method with custom sparse linear
+    algebra routines that exploit structure in problem data.
+
+- title: Robust
+  icon: fas fa-cogs
+  description: >
+    The algorithm is absolutely division free after the setup and it requires no
+    assumptions on problem data (the problem only needs to be convex). It just works!
+
+- title: Free
+  icon: fas fa-heart
+  description: >
+    OSQP is free and will always be free for everyone. Its license is Apache 2.0.
+
+- title: Embeddable
+  icon: fas fa-microchip
+  description: >
+    OSQP has an easy interface to generate customized embeddable C code with no memory manager required.
+
+
+- title: Many interfaces
+  icon: far fa-hand-pointer
+  description: >
+    OSQP supports many interfaces including C/C++, Fortran, Matlab, Python, R, Julia, Rust.
+
+- title: Library-free
+  icon: fas fa-dove
+  description: >
+    OSQP is self-contained and requires no external library to run.
diff --git a/site/_data/users.yml b/site/_data/users.yml
new file mode 100644
index 0000000..3e9d1d0
--- /dev/null
+++ b/site/_data/users.yml
@@ -0,0 +1,60 @@
+- name: MIT
+  logo: MIT.jpg
+
+- name: Lyft
+  logo: lyft.png
+
+- name: Stanford University
+  logo: stanford.png
+
+- name: Aptiv
+  logo: aptiv.png
+
+- name: UC Berkeley
+  logo: berkeley.jpg
+
+- name: Google
+  logo: google.jpg
+
+- name: Toyota
+  logo: toyota.jpg
+
+- name: ETH
+  logo: eth.jpg
+
+- name: KU Leuven
+  logo: kuleuven.png
+
+- name: BlackRock
+  logo: blackrock.jpg
+
+- name: UC Davis
+  logo: ucdavis.png
+
+- name: Los Alamos
+  logo: los_alamos.png
+
+- name: University of Stuttgart
+  logo: stuttgart.png
+
+- name: NYU
+  logo: nyu.jpg
+
+- name: IIT
+  logo: iit.png
+
+- name: Lund University
+  logo: lund.jpg
+
+- name: Linkedin
+  logo: linkedin.jpg
+
+- name: Macquarie
+  logo: macquarie.jpg
+
+- name: Seervision
+  logo: seervision.png
+
+- name: Siemens
+  logo: siemens.png
+
diff --git a/site/_includes/footer.html b/site/_includes/footer.html
new file mode 100644
index 0000000..5cc063a
--- /dev/null
+++ b/site/_includes/footer.html
@@ -0,0 +1,10 @@
+    <footer class="footer">
+      <div class="container">
+	              <hr class="mb-5">
+	<a href="mailto:{{site.email}}"><i class="far fa-envelope fa-2x mr-4"></i></a>
+	<a href="{{site.github_link}}"><i class="fab fa-github fa-2x"></i></a>
+	 <p class="copyright">&copy; <script>document.write(new Date().getFullYear())</script> <a href="https://stellato.io">Bartolomeo Stellato<a></p>
+      </div>
+    </footer>
+
+
diff --git a/site/_includes/google-analytics.html b/site/_includes/google-analytics.html
new file mode 100644
index 0000000..a43c349
--- /dev/null
+++ b/site/_includes/google-analytics.html
@@ -0,0 +1,10 @@
+<!-- Global site tag (gtag.js) - Google Analytics -->
+<script async src="https://www.googletagmanager.com/gtag/js?id=UA-56714542-3"></script>
+<script>
+  window.dataLayer = window.dataLayer || [];
+  function gtag(){dataLayer.push(arguments);}
+  gtag('js', new Date());
+
+  gtag('config', '{{ site.google_analytics }}');
+</script>
+
diff --git a/site/_includes/head.html b/site/_includes/head.html
new file mode 100644
index 0000000..107e687
--- /dev/null
+++ b/site/_includes/head.html
@@ -0,0 +1,27 @@
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+
+    <link rel="stylesheet" href="{{ site.baseurl }}/assets/css/osqp.css">
+
+    <title>{{ site.title }} {% if page.title %} - {{ page.title | escape }} {% endif %} </title>
+
+    {%- include google-analytics.html -%}
+
+<!-- Fonts -->
+<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
+
+<!-- Icons -->
+<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">
+
+<!-- Users slide -->
+<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.9.0/slick.css"/>
+<!-- Add the slick-theme.css if you want default styling -->
+<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.9.0/slick-theme.css"/>
+
+
+<link rel="shortcut icon" type="image/png" href="{{ site.baseurl }}/assets/images/favicon.ico"/>
+
+</head>
+
diff --git a/site/_includes/js.html b/site/_includes/js.html
new file mode 100644
index 0000000..5667561
--- /dev/null
+++ b/site/_includes/js.html
@@ -0,0 +1,134 @@
+<!-- Bootstrap scripts -->
+<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
+<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
+    <!-- Typed.js -->
+    <script type="text/javascript" src="https://rawgit.com/mattboldt/typed.js/master/lib/typed.min.js"></script>
+
+
+    <!-- Particles.js -->
+    <script src="https://cdn.jsdelivr.net/particles.js/2.0.0/particles.min.js"></script>
+
+<!-- Dynamic typing -->
+  <script type="text/javascript">
+  var myQuotes =  new Array();
+  {% for line in site.quotes %}
+    myQuotes.push("{{ line.text }}");
+  {% endfor %}
+
+  function shuffle(array) {
+    var currentIndex = array.length, temporaryValue, randomIndex ;
+    // While there remain elements to shuffle...
+    while (0 !== currentIndex) {
+      // Pick a remaining element...
+      randomIndex = Math.floor(Math.random() * currentIndex);
+      currentIndex -= 1;
+      // And swap it with the current element.
+      temporaryValue = array[currentIndex];
+      array[currentIndex] = array[randomIndex];
+      array[randomIndex] = temporaryValue;
+    }
+    return array;
+  }
+
+  {% if site.shuffle %}
+    shuffle(myQuotes)
+  {% endif %}
+
+  var typed = new Typed("#typed", {
+      strings: myQuotes,
+      typeSpeed: {{ site.type-speed }},
+      backSpeed: {{ site.back-speed }},
+      backDelay: {{ site.delete-delay }},
+      startDelay: {{ site.start-delay }},
+      loop: {{ site.loop }},
+      loopCount: {{ site.loop-count }},
+      cursorChar: "|"
+    });
+  </script>
+
+
+<!-- Place this tag in your head or just before your close body tag. -->
+<script async defer src="https://buttons.github.io/buttons.js"></script>
+
+
+<!-- Slider -->
+<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.9.0/slick.min.js"></script>
+
+
+<script type="text/javascript">
+	$.fn.randomize = function (selector) {
+		var $elems = selector ? $(this).find(selector) : $(this).children(),
+			$parents = $elems.parent();
+
+		$parents.each(function () {
+			$(this).children(selector).sort(function (childA, childB) {
+				// * Prevent last slide from being reordered
+				if($(childB).index() !== $(this).children(selector).length - 1) {
+					return Math.round(Math.random()) - 0.5;
+				}
+			}.bind(this)).detach().appendTo(this);
+		});
+
+		return this;
+	};
+
+
+
+	$('.slider').randomize().slick({
+		slidesToShow: 4,
+		slidesToScroll: 1,
+		autoplay: true,
+		autoplaySpeed: 2000,
+		  responsive: [
+			  {
+				  breakpoint: 992,
+				  settings: {
+					  slidesToShow: 3,
+				  }
+			  },
+			  {
+				  breakpoint: 768,
+				  settings: {
+					  slidesToShow: 2,
+				  }
+			  }
+		  ]
+	});
+</script>
+
+<script>particlesJS("particles-js", {
+    "particles": {
+        "number": {
+            "value": 60
+        },
+        "shape": {
+            "type": "circle"
+        },
+        "size": {
+            "value": 5,
+            "random": true
+        },
+        "line_linked": {
+            "enable": true
+        },
+        "move": {
+            "enable": true,
+            "speed": 3,
+            "direction": "none",
+            "straight": false
+        }
+    },
+    "interactivity": {
+        "detect_on": "canvas",
+        "events": {
+            "onhover": {
+                "enable": false
+            },
+            "onclick": {
+                "enable": false,
+            }
+        },
+        "retina_detect": true
+    }
+});</script>
diff --git a/site/_includes/navbar.html b/site/_includes/navbar.html
new file mode 100644
index 0000000..c58c665
--- /dev/null
+++ b/site/_includes/navbar.html
@@ -0,0 +1,28 @@
+<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
+	<div class="container d-flex justify-content-between">
+		<!-- <a class="navbar-brand navbar-dark" href="{{ site.url }}"> {{ site.title}}</a> -->
+		<a class="navbar-brand" href="{{site.url}}" title="OSQP"><img class="img-fluid" src="{{site.baseurl}}/assets/images/logo.png" alt="OSQP" /></a>
+		<!-- <a class="navbar-brand visible-sm" href="{{site.url}}" title="MITaly"><img src="{{site.baseurl}}/assets/images/mitaly_logo.png" height="50" alt="MITaly" /></a> -->
+
+		<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
+			<span class="navbar-toggler-icon"></span>
+		</button>
+
+		<div class="collapse navbar-collapse" id="navbarSupportedContent">
+			<ul class="navbar-nav mr-auto">
+				<li class="nav-item"><a class="nav-link" href="{{ site.baseurl }}/docs/">Docs</a></li>
+				{% assign pages_list = site.pages | sort:"order" %}
+				{% assign group = 'navigation' %}
+				{% include pages_list %}
+				<li class="nav-item"><a class="nav-link" href="{{ site.discourse_link }}">Forum</a></li>
+				<li class="nav-item"><a class="nav-link" href="{{ site.github_link }}">GitHub</a></li>
+			</ul>
+			<ul class="navbar-nav">
+				<li><a class="nav-item" href="http://www.ox.ac.uk" title="University of Oxford"><img src="{{site.baseurl}}/assets/images/oxford_rectangle.png" class="img-fluid" alt="University of Oxford" /></a></li>
+			</ul>
+		</div>
+	</div>
+
+
+
+</nav>
diff --git a/site/_includes/pages_list b/site/_includes/pages_list
new file mode 100644
index 0000000..7990521
--- /dev/null
+++ b/site/_includes/pages_list
@@ -0,0 +1,11 @@
+{% for node in pages_list %}
+  {% if group == null or group == node.group %}
+    {% if page.url == node.url %}
+      <li class="nav-item active"><a class="nav-link" href="{{ site.baseurl }}{{node.url}}" >{% if node.shorttitle %}{{node.shorttitle}} {% else %}{{node.title}}{% endif %}</a></li>
+    {% else %}
+      <li class="nav-item"><a class="nav-link" href="{{ site.baseurl }}{{node.url}}">{% if node.shorttitle %}{{node.shorttitle}} {% else %}{{node.title}}{% endif %}</a></li>
+    {% endif %}
+  {% endif %}
+{% endfor %}
+{% assign pages_list = nil %}
+{% assign group = nil %}
diff --git a/site/_layouts/default.html b/site/_layouts/default.html
new file mode 100644
index 0000000..81316a8
--- /dev/null
+++ b/site/_layouts/default.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="{{ page.lang | default: site.lang | default: "en" }}">
+
+  {%- include head.html -%}
+
+  <body class="d-flex flex-column">
+
+    {%- include navbar.html -%}
+
+    <main class="flex-grow">
+        {{ content }}
+    </main>
+
+    {%- include footer.html -%}
+    {%- include js.html -%}
+
+  </body>
+
+</html>
diff --git a/site/_layouts/home.html b/site/_layouts/home.html
new file mode 100644
index 0000000..26a7c83
--- /dev/null
+++ b/site/_layouts/home.html
@@ -0,0 +1,96 @@
+---
+layout: default
+---
+<div id="particles-js"></div>
+    <div class="head-intro position-relative overflow-hidden p-3 p-md-5 text-center">
+	    <div class="row h-100">
+      <div class="col-md-8 p-lg-8 mx-auto my-auto">
+	      <h1 class="display-4 font-weight-light title-text">{{ site.title }}</h1>
+	      <span class="lead font-weight-normal" id="typed"></span>
+	<!-- [> <a class="btn btn-outline-secondary" href="#">Coming soon</a> <] -->
+		  <!-- <a href="{{ site.baseurl }}/docs/" class="btn btn-lg btn-secondary">Get started</a> -->
+<div class="col-md-8 col-lg-8 mx-auto my-5">
+		  <a href="{{ site.baseurl }}/docs/get_started/" class="btn btn-light bg-white">Get started</a>
+		  </div>
+      </div>
+      </div>
+    </div>
+
+
+    <div class="home-section bg-white">
+	    <div class="container">
+
+		    <div class="row">
+			    <div class="col">
+				    <h2 class="mb-4">Features</h2>
+			    </div>
+		    </div>
+		    <div class="row">
+			    {% for feature in site.data.features %}
+			    <div class="col-lg-4 col-md-6">
+				    <i class="{{feature.icon}} fa-2x my-3"></i>
+				    <h5 class="mb-1">{{feature.title}}</h5>
+				    <p class="mb-2">{{feature.description}}</p>
+			    </div><!-- /.col-lg-4 -->
+			    {% endfor %}
+		    </div>
+
+		    <hr class="featurette-divider">
+
+		    <div class="row featurette">
+			    <div class="col-md-4">
+				    <h2 class="featurette-heading">OSQP beats most QP solvers.</h2>
+				    <p class="lead">We benchmarked OSQP against problems from many different classes, applications and scalings. OSQP beats most available commercial and academic solvers.</p>
+			    </div>
+			    <div class="col-md-8">
+				    <a href="https://github.com/osqp/osqp_benchmarks"> <img class="img-fluid mx-auto" src="{{ site.baseurl }}/assets/images/benchmarks/performance_profiles.jpg" alt="Performance Profiles"> </a>
+			    </div>
+		    </div>
+
+		    <hr class="featurette-divider mb-3">
+
+		    <!-- <div class="row"> -->
+		    <!--         <div class="col"> -->
+		    <!--                 <h2 class="mb-5">Users</h2> -->
+		    <!--         </div> -->
+		    <!--         </div> -->
+		    <div class="row slider">
+
+			    {% for user in site.data.users %}
+			    <div class="slider_block">
+				    <img src="{{ site.baseurl }}/assets/images/users/{{ user.logo }}" alt="{{ user.name }}">
+			    </div>
+			    {% endfor %}
+		    </div>
+
+		    <hr class="featurette-divider mt-3">
+
+		    <div class="row">
+			    <div class="col">
+				    <h2 class="mb-5">Credits</h2>
+			    </div>
+		    </div>
+		    <div class="row">
+			    {% for contributor in site.data.credits %}
+			    <div class="col-lg-4 col-md-6">
+				    {% if contributor.website %} <a href="{{contributor.website}}">{% endif %}
+					    <img class="rounded-circle mb-2" src="{{ site.baseurl }}/assets/images/credits/{{ contributor.name | downcase | replace: ' ', '_'}}.jpg" alt="{{ contributor.name }}" width="140" height="140">
+					    {% if contributor.website %} </a> {% endif %}
+				    <h5 class="mb-1">{{contributor.name}}</h5>
+				    <p class="my-1">{{contributor.role}}</p>
+				    <p class="mb-5"><b>{{contributor.affiliation}}</b></p>
+			    </div><!-- /.col-lg-4 -->
+			    {% endfor %}
+		    </div>
+		    <div class="row">
+			    <div class="col my-5">
+				    And many others...
+			    </div>
+		    </div>
+
+
+
+
+
+	    </div>
+    </div>
diff --git a/site/_layouts/page.html b/site/_layouts/page.html
new file mode 100644
index 0000000..776cada
--- /dev/null
+++ b/site/_layouts/page.html
@@ -0,0 +1,18 @@
+---
+layout: default
+---
+
+<div class="content py-5 bg-white">
+	<div class="container">
+		<div class="row justify-content-center">
+			<div class="col-md-10">
+
+				{%- if page.title -%}
+				<h2>{{ page.title }}</h2>
+				{%- endif -%}
+
+				{{ content }}
+			</div>
+		</div>
+	</div>
+</div>
diff --git a/site/_pages/404.md b/site/_pages/404.md
new file mode 100644
index 0000000..5e0616e
--- /dev/null
+++ b/site/_pages/404.md
@@ -0,0 +1,25 @@
+---
+layout: default
+permalink: /404.html
+---
+
+<style type="text/css" media="screen">
+  .container {
+    margin: 10px auto;
+    max-width: 600px;
+    text-align: center;
+  }
+  h1 {
+    margin: 30px 0;
+    font-size: 4em;
+    line-height: 1;
+    letter-spacing: -1px;
+  }
+</style>
+
+<div class="container">
+  <h1>404</h1>
+
+  <p><strong>Page not found :(</strong></p>
+  <p>The requested page could not be found.</p>
+</div>
diff --git a/site/_pages/citing.md b/site/_pages/citing.md
new file mode 100644
index 0000000..699a81d
--- /dev/null
+++ b/site/_pages/citing.md
@@ -0,0 +1,106 @@
+---
+layout: page
+title: Citing OSQP
+shorttitle: Citing
+permalink: /citing/
+group: "navigation"
+order: 1
+---
+
+If you use OSQP for published work, we encourage you to
+
+* Cite the accompanying papers
+* Put a star on <a class="github-button" href="https://github.com/osqp/osqp" data-size="large" data-show-count="true" aria-label="Star osqp/osqp on GitHub">GitHub</a>
+
+We are looking forward to hearing your success stories with OSQP! Please [share them with us](mailto:bartolomeo.stellato@gmail.com).
+
+
+### Papers
+
+#### Main paper
+Main algorithm description, derivation and benchmark available in this [paper](https://web.stanford.edu/~boyd/papers/pdf/osqp.pdf).
+
+{% raw %}
+```latex
+@article{osqp,
+  author  = {Stellato, B. and Banjac, G. and Goulart, P. and Bemporad, A. and Boyd, S.},
+  title   = {{OSQP}: an operator splitting solver for quadratic programs},
+  journal = {Mathematical Programming Computation},
+  year    = {2020},
+  volume  = {12},
+  number  = {4},
+  pages   = {637--672},
+  doi     = {10.1007/s12532-020-00179-2},
+  url     = {https://doi.org/10.1007/s12532-020-00179-2},
+}
+```
+{% endraw %}
+
+#### Infeasibility detection
+Infeasibility detection proofs using ADMM (also for general conic programs) in this [paper](https://stanford.edu/~boyd/papers/pdf/admm_infeas.pdf).
+
+{% raw %}
+```latex
+@article{osqp-infeasibility,
+  author  = {Banjac, G. and Goulart, P. and Stellato, B. and Boyd, S.},
+  title   = {Infeasibility detection in the alternating direction method of multipliers for convex optimization},
+  journal = {Journal of Optimization Theory and Applications},
+  year    = {2019},
+  volume  = {183},
+  number  = {2},
+  pages   = {490--519},
+  doi     = {10.1007/s10957-019-01575-y},
+  url     = {https://doi.org/10.1007/s10957-019-01575-y},
+}
+```
+{% endraw %}
+
+#### GPU implementation
+GPU implementation and PCG method for solving linear systems in this [paper](https://doi.org/10.1016/j.jpdc.2020.05.021).
+
+{% raw %}
+```latex
+@article{osqp-gpu,
+  author  = {Schubiger, M. and Banjac, G. and Lygeros, J.},
+  title   = {{GPU} acceleration of {ADMM} for large-scale quadratic programming},
+  journal = {Journal of Parallel and Distributed Computing},
+  year    = {2020},
+  volume  = {144},
+  pages   = {55--67},
+  doi     = {10.1016/j.jpdc.2020.05.021},
+  url     = {https://doi.org/10.1016/j.jpdc.2020.05.021},
+}
+```
+{% endraw %}
+
+#### Code generation
+Code generation functionality and example in this [paper](https://stanford.edu/~boyd/papers/pdf/osqp_embedded.pdf).
+
+{% raw %}
+```latex
+@inproceedings{osqp-codegen,
+  author    = {Banjac, G. and Stellato, B. and Moehle, N. and Goulart, P. and Bemporad, A. and Boyd, S.},
+  title     = {Embedded code generation using the {OSQP} solver},
+  booktitle = {IEEE Conference on Decision and Control (CDC)},
+  year      = {2017},
+  doi       = {10.1109/CDC.2017.8263928},
+  url       = {https://doi.org/10.1109/CDC.2017.8263928},
+}
+```
+{% endraw %}
+
+#### Mixed-integer optimization
+A branch-and-bound solver for mixed-integer quadratic optimization in this [paper](https://stellato.io/downloads/publications/2018/miosqp_ecc.pdf).
+
+{% raw %}
+```latex
+@inproceedings{miosqp,
+  author    = {Stellato, B. and Naik, V. V. and Bemporad, A. and Goulart, P. and Boyd, S.},
+  title     = {Embedded mixed-integer quadratic optimization using the {OSQP} solver},
+  booktitle = {European Control Conference (ECC)},
+  year      = {2018},
+  doi       = {10.23919/ECC.2018.8550136},
+  url       = {https://doi.org/10.23919/ECC.2018.8550136},
+}
+```
+{% endraw %}
diff --git a/site/_pages/index.md b/site/_pages/index.md
new file mode 100644
index 0000000..925508b
--- /dev/null
+++ b/site/_pages/index.md
@@ -0,0 +1,7 @@
+---
+# You don't need to edit this file, it's empty on purpose.
+# Edit theme's home layout instead if you wanna make some changes
+# See: https://jekyllrb.com/docs/themes/#overriding-theme-defaults
+layout: home
+permalink: /
+---
diff --git a/site/_posts/2018-04-15-welcome-to-jekyll.markdown b/site/_posts/2018-04-15-welcome-to-jekyll.markdown
new file mode 100644
index 0000000..0025e65
--- /dev/null
+++ b/site/_posts/2018-04-15-welcome-to-jekyll.markdown
@@ -0,0 +1,25 @@
+---
+layout: post
+title:  "Welcome to Jekyll!"
+date:   2018-04-15 15:56:48 -0400
+categories: jekyll update
+---
+You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated.
+
+To add new posts, simply add a file in the `_posts` directory that follows the convention `YYYY-MM-DD-name-of-post.ext` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works.
+
+Jekyll also offers powerful support for code snippets:
+
+{% highlight ruby %}
+def print_hi(name)
+  puts "Hi, #{name}"
+end
+print_hi('Tom')
+#=> prints 'Hi, Tom' to STDOUT.
+{% endhighlight %}
+
+Check out the [Jekyll docs][jekyll-docs] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll Talk][jekyll-talk].
+
+[jekyll-docs]: https://jekyllrb.com/docs/home
+[jekyll-gh]:   https://github.com/jekyll/jekyll
+[jekyll-talk]: https://talk.jekyllrb.com/
diff --git a/site/_sass/_bootstrap-grid.scss b/site/_sass/_bootstrap-grid.scss
new file mode 100644
index 0000000..bb5ee3a
--- /dev/null
+++ b/site/_sass/_bootstrap-grid.scss
@@ -0,0 +1,32 @@
+/*!
+ * Bootstrap Grid v4.1.3 (https://getbootstrap.com/)
+ * Copyright 2011-2018 The Bootstrap Authors
+ * Copyright 2011-2018 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+@at-root {
+  @-ms-viewport { width: device-width; } // stylelint-disable-line at-rule-no-vendor-prefix
+}
+
+html {
+  box-sizing: border-box;
+  -ms-overflow-style: scrollbar;
+}
+
+*,
+*::before,
+*::after {
+  box-sizing: inherit;
+}
+
+@import "bootstrap/functions";
+@import "bootstrap/variables";
+
+@import "bootstrap/mixins/breakpoints";
+@import "bootstrap/mixins/grid-framework";
+@import "bootstrap/mixins/grid";
+
+@import "bootstrap/grid";
+@import "bootstrap/utilities/display";
+@import "bootstrap/utilities/flex";
diff --git a/site/_sass/_bootstrap-reboot.scss b/site/_sass/_bootstrap-reboot.scss
new file mode 100644
index 0000000..3edc253
--- /dev/null
+++ b/site/_sass/_bootstrap-reboot.scss
@@ -0,0 +1,12 @@
+/*!
+ * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
+ * Copyright 2011-2018 The Bootstrap Authors
+ * Copyright 2011-2018 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
+ */
+
+@import "bootstrap/functions";
+@import "bootstrap/variables";
+@import "bootstrap/mixins";
+@import "bootstrap/reboot";
diff --git a/site/_sass/_bootstrap.scss b/site/_sass/_bootstrap.scss
new file mode 100644
index 0000000..d7d7be9
--- /dev/null
+++ b/site/_sass/_bootstrap.scss
@@ -0,0 +1,42 @@
+/*!
+ * Bootstrap v4.1.3 (https://getbootstrap.com/)
+ * Copyright 2011-2018 The Bootstrap Authors
+ * Copyright 2011-2018 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+@import "bootstrap/functions";
+@import "bootstrap/variables";
+@import "bootstrap/mixins";
+@import "bootstrap/root";
+@import "bootstrap/reboot";
+@import "bootstrap/type";
+@import "bootstrap/images";
+@import "bootstrap/code";
+@import "bootstrap/grid";
+@import "bootstrap/tables";
+@import "bootstrap/forms";
+@import "bootstrap/buttons";
+@import "bootstrap/transitions";
+@import "bootstrap/dropdown";
+@import "bootstrap/button-group";
+@import "bootstrap/input-group";
+@import "bootstrap/custom-forms";
+@import "bootstrap/nav";
+@import "bootstrap/navbar";
+@import "bootstrap/card";
+@import "bootstrap/breadcrumb";
+@import "bootstrap/pagination";
+@import "bootstrap/badge";
+@import "bootstrap/jumbotron";
+@import "bootstrap/alert";
+@import "bootstrap/progress";
+@import "bootstrap/media";
+@import "bootstrap/list-group";
+@import "bootstrap/close";
+@import "bootstrap/modal";
+@import "bootstrap/tooltip";
+@import "bootstrap/popover";
+@import "bootstrap/carousel";
+@import "bootstrap/utilities";
+@import "bootstrap/print";
diff --git a/site/_sass/bootstrap/_alert.scss b/site/_sass/bootstrap/_alert.scss
new file mode 100644
index 0000000..dd43e23
--- /dev/null
+++ b/site/_sass/bootstrap/_alert.scss
@@ -0,0 +1,51 @@
+//
+// Base styles
+//
+
+.alert {
+  position: relative;
+  padding: $alert-padding-y $alert-padding-x;
+  margin-bottom: $alert-margin-bottom;
+  border: $alert-border-width solid transparent;
+  @include border-radius($alert-border-radius);
+}
+
+// Headings for larger alerts
+.alert-heading {
+  // Specified to prevent conflicts of changing $headings-color
+  color: inherit;
+}
+
+// Provide class for links that match alerts
+.alert-link {
+  font-weight: $alert-link-font-weight;
+}
+
+
+// Dismissible alerts
+//
+// Expand the right padding and account for the close button's positioning.
+
+.alert-dismissible {
+  padding-right: ($close-font-size + $alert-padding-x * 2);
+
+  // Adjust close link position
+  .close {
+    position: absolute;
+    top: 0;
+    right: 0;
+    padding: $alert-padding-y $alert-padding-x;
+    color: inherit;
+  }
+}
+
+
+// Alternate styles
+//
+// Generate contextual modifier classes for colorizing the alert.
+
+@each $color, $value in $theme-colors {
+  .alert-#{$color} {
+    @include alert-variant(theme-color-level($color, $alert-bg-level), theme-color-level($color, $alert-border-level), theme-color-level($color, $alert-color-level));
+  }
+}
diff --git a/site/_sass/bootstrap/_badge.scss b/site/_sass/bootstrap/_badge.scss
new file mode 100644
index 0000000..b87a1b0
--- /dev/null
+++ b/site/_sass/bootstrap/_badge.scss
@@ -0,0 +1,47 @@
+// Base class
+//
+// Requires one of the contextual, color modifier classes for `color` and
+// `background-color`.
+
+.badge {
+  display: inline-block;
+  padding: $badge-padding-y $badge-padding-x;
+  font-size: $badge-font-size;
+  font-weight: $badge-font-weight;
+  line-height: 1;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  @include border-radius($badge-border-radius);
+
+  // Empty badges collapse automatically
+  &:empty {
+    display: none;
+  }
+}
+
+// Quick fix for badges in buttons
+.btn .badge {
+  position: relative;
+  top: -1px;
+}
+
+// Pill badges
+//
+// Make them extra rounded with a modifier to replace v3's badges.
+
+.badge-pill {
+  padding-right: $badge-pill-padding-x;
+  padding-left: $badge-pill-padding-x;
+  @include border-radius($badge-pill-border-radius);
+}
+
+// Colors
+//
+// Contextual variations (linked badges get darker on :hover).
+
+@each $color, $value in $theme-colors {
+  .badge-#{$color} {
+    @include badge-variant($value);
+  }
+}
diff --git a/site/_sass/bootstrap/_breadcrumb.scss b/site/_sass/bootstrap/_breadcrumb.scss
new file mode 100644
index 0000000..be30950
--- /dev/null
+++ b/site/_sass/bootstrap/_breadcrumb.scss
@@ -0,0 +1,41 @@
+.breadcrumb {
+  display: flex;
+  flex-wrap: wrap;
+  padding: $breadcrumb-padding-y $breadcrumb-padding-x;
+  margin-bottom: $breadcrumb-margin-bottom;
+  list-style: none;
+  background-color: $breadcrumb-bg;
+  @include border-radius($breadcrumb-border-radius);
+}
+
+.breadcrumb-item {
+  // The separator between breadcrumbs (by default, a forward-slash: "/")
+  + .breadcrumb-item {
+    padding-left: $breadcrumb-item-padding;
+
+    &::before {
+      display: inline-block; // Suppress underlining of the separator in modern browsers
+      padding-right: $breadcrumb-item-padding;
+      color: $breadcrumb-divider-color;
+      content: $breadcrumb-divider;
+    }
+  }
+
+  // IE9-11 hack to properly handle hyperlink underlines for breadcrumbs built
+  // without `<ul>`s. The `::before` pseudo-element generates an element
+  // *within* the .breadcrumb-item and thereby inherits the `text-decoration`.
+  //
+  // To trick IE into suppressing the underline, we give the pseudo-element an
+  // underline and then immediately remove it.
+  + .breadcrumb-item:hover::before {
+    text-decoration: underline;
+  }
+  // stylelint-disable-next-line no-duplicate-selectors
+  + .breadcrumb-item:hover::before {
+    text-decoration: none;
+  }
+
+  &.active {
+    color: $breadcrumb-active-color;
+  }
+}
diff --git a/site/_sass/bootstrap/_button-group.scss b/site/_sass/bootstrap/_button-group.scss
new file mode 100644
index 0000000..5495170
--- /dev/null
+++ b/site/_sass/bootstrap/_button-group.scss
@@ -0,0 +1,172 @@
+// stylelint-disable selector-no-qualifying-type
+
+// Make the div behave like a button
+.btn-group,
+.btn-group-vertical {
+  position: relative;
+  display: inline-flex;
+  vertical-align: middle; // match .btn alignment given font-size hack above
+
+  > .btn {
+    position: relative;
+    flex: 0 1 auto;
+
+    // Bring the hover, focused, and "active" buttons to the front to overlay
+    // the borders properly
+    @include hover {
+      z-index: 1;
+    }
+    &:focus,
+    &:active,
+    &.active {
+      z-index: 1;
+    }
+  }
+
+  // Prevent double borders when buttons are next to each other
+  .btn + .btn,
+  .btn + .btn-group,
+  .btn-group + .btn,
+  .btn-group + .btn-group {
+    margin-left: -$btn-border-width;
+  }
+}
+
+// Optional: Group multiple button groups together for a toolbar
+.btn-toolbar {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: flex-start;
+
+  .input-group {
+    width: auto;
+  }
+}
+
+.btn-group {
+  > .btn:first-child {
+    margin-left: 0;
+  }
+
+  // Reset rounded corners
+  > .btn:not(:last-child):not(.dropdown-toggle),
+  > .btn-group:not(:last-child) > .btn {
+    @include border-right-radius(0);
+  }
+
+  > .btn:not(:first-child),
+  > .btn-group:not(:first-child) > .btn {
+    @include border-left-radius(0);
+  }
+}
+
+// Sizing
+//
+// Remix the default button sizing classes into new ones for easier manipulation.
+
+.btn-group-sm > .btn { @extend .btn-sm; }
+.btn-group-lg > .btn { @extend .btn-lg; }
+
+
+//
+// Split button dropdowns
+//
+
+.dropdown-toggle-split {
+  padding-right: $btn-padding-x * .75;
+  padding-left: $btn-padding-x * .75;
+
+  &::after,
+  .dropup &::after,
+  .dropright &::after {
+    margin-left: 0;
+  }
+
+  .dropleft &::before {
+    margin-right: 0;
+  }
+}
+
+.btn-sm + .dropdown-toggle-split {
+  padding-right: $btn-padding-x-sm * .75;
+  padding-left: $btn-padding-x-sm * .75;
+}
+
+.btn-lg + .dropdown-toggle-split {
+  padding-right: $btn-padding-x-lg * .75;
+  padding-left: $btn-padding-x-lg * .75;
+}
+
+
+// The clickable button for toggling the menu
+// Set the same inset shadow as the :active state
+.btn-group.show .dropdown-toggle {
+  @include box-shadow($btn-active-box-shadow);
+
+  // Show no shadow for `.btn-link` since it has no other button styles.
+  &.btn-link {
+    @include box-shadow(none);
+  }
+}
+
+
+//
+// Vertical button groups
+//
+
+.btn-group-vertical {
+  flex-direction: column;
+  align-items: flex-start;
+  justify-content: center;
+
+  .btn,
+  .btn-group {
+    width: 100%;
+  }
+
+  > .btn + .btn,
+  > .btn + .btn-group,
+  > .btn-group + .btn,
+  > .btn-group + .btn-group {
+    margin-top: -$btn-border-width;
+    margin-left: 0;
+  }
+
+  // Reset rounded corners
+  > .btn:not(:last-child):not(.dropdown-toggle),
+  > .btn-group:not(:last-child) > .btn {
+    @include border-bottom-radius(0);
+  }
+
+  > .btn:not(:first-child),
+  > .btn-group:not(:first-child) > .btn {
+    @include border-top-radius(0);
+  }
+}
+
+
+// Checkbox and radio options
+//
+// In order to support the browser's form validation feedback, powered by the
+// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use
+// `display: none;` or `visibility: hidden;` as that also hides the popover.
+// Simply visually hiding the inputs via `opacity` would leave them clickable in
+// certain cases which is prevented by using `clip` and `pointer-events`.
+// This way, we ensure a DOM element is visible to position the popover from.
+//
+// See https://github.com/twbs/bootstrap/pull/12794 and
+// https://github.com/twbs/bootstrap/pull/14559 for more information.
+
+.btn-group-toggle {
+  > .btn,
+  > .btn-group > .btn {
+    margin-bottom: 0; // Override default `<label>` value
+
+    input[type="radio"],
+    input[type="checkbox"] {
+      position: absolute;
+      clip: rect(0, 0, 0, 0);
+      pointer-events: none;
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/_buttons.scss b/site/_sass/bootstrap/_buttons.scss
new file mode 100644
index 0000000..0a8eaa9
--- /dev/null
+++ b/site/_sass/bootstrap/_buttons.scss
@@ -0,0 +1,143 @@
+// stylelint-disable selector-no-qualifying-type
+
+//
+// Base styles
+//
+
+.btn {
+  display: inline-block;
+  font-weight: $btn-font-weight;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: middle;
+  user-select: none;
+  border: $btn-border-width solid transparent;
+  @include button-size($btn-padding-y, $btn-padding-x, $font-size-base, $btn-line-height, $btn-border-radius);
+  @include transition($btn-transition);
+
+  // Share hover and focus styles
+  @include hover-focus {
+    text-decoration: none;
+  }
+
+  &:focus,
+  &.focus {
+    outline: 0;
+    box-shadow: $btn-focus-box-shadow;
+  }
+
+  // Disabled comes first so active can properly restyle
+  &.disabled,
+  &:disabled {
+    opacity: $btn-disabled-opacity;
+    @include box-shadow(none);
+  }
+
+  // Opinionated: add "hand" cursor to non-disabled .btn elements
+  &:not(:disabled):not(.disabled) {
+    cursor: pointer;
+  }
+
+  &:not(:disabled):not(.disabled):active,
+  &:not(:disabled):not(.disabled).active {
+    @include box-shadow($btn-active-box-shadow);
+
+    &:focus {
+      @include box-shadow($btn-focus-box-shadow, $btn-active-box-shadow);
+    }
+  }
+}
+
+// Future-proof disabling of clicks on `<a>` elements
+a.btn.disabled,
+fieldset:disabled a.btn {
+  pointer-events: none;
+}
+
+
+//
+// Alternate buttons
+//
+
+@each $color, $value in $theme-colors {
+  .btn-#{$color} {
+    @include button-variant($value, $value);
+  }
+}
+
+@each $color, $value in $theme-colors {
+  .btn-outline-#{$color} {
+    @include button-outline-variant($value);
+  }
+}
+
+
+//
+// Link buttons
+//
+
+// Make a button look and behave like a link
+.btn-link {
+  font-weight: $font-weight-normal;
+  color: $link-color;
+  background-color: transparent;
+
+  @include hover {
+    color: $link-hover-color;
+    text-decoration: $link-hover-decoration;
+    background-color: transparent;
+    border-color: transparent;
+  }
+
+  &:focus,
+  &.focus {
+    text-decoration: $link-hover-decoration;
+    border-color: transparent;
+    box-shadow: none;
+  }
+
+  &:disabled,
+  &.disabled {
+    color: $btn-link-disabled-color;
+    pointer-events: none;
+  }
+
+  // No need for an active state here
+}
+
+
+//
+// Button Sizes
+//
+
+.btn-lg {
+  @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $font-size-lg, $btn-line-height-lg, $btn-border-radius-lg);
+}
+
+.btn-sm {
+  @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-line-height-sm, $btn-border-radius-sm);
+}
+
+
+//
+// Block button
+//
+
+.btn-block {
+  display: block;
+  width: 100%;
+
+  // Vertically space out multiple block buttons
+  + .btn-block {
+    margin-top: $btn-block-spacing-y;
+  }
+}
+
+// Specificity overrides
+input[type="submit"],
+input[type="reset"],
+input[type="button"] {
+  &.btn-block {
+    width: 100%;
+  }
+}
diff --git a/site/_sass/bootstrap/_card.scss b/site/_sass/bootstrap/_card.scss
new file mode 100644
index 0000000..28d7e62
--- /dev/null
+++ b/site/_sass/bootstrap/_card.scss
@@ -0,0 +1,301 @@
+//
+// Base styles
+//
+
+.card {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  min-width: 0;
+  word-wrap: break-word;
+  background-color: $card-bg;
+  background-clip: border-box;
+  border: $card-border-width solid $card-border-color;
+  @include border-radius($card-border-radius);
+
+  > hr {
+    margin-right: 0;
+    margin-left: 0;
+  }
+
+  > .list-group:first-child {
+    .list-group-item:first-child {
+      @include border-top-radius($card-border-radius);
+    }
+  }
+
+  > .list-group:last-child {
+    .list-group-item:last-child {
+      @include border-bottom-radius($card-border-radius);
+    }
+  }
+}
+
+.card-body {
+  // Enable `flex-grow: 1` for decks and groups so that card blocks take up
+  // as much space as possible, ensuring footers are aligned to the bottom.
+  flex: 1 1 auto;
+  padding: $card-spacer-x;
+}
+
+.card-title {
+  margin-bottom: $card-spacer-y;
+}
+
+.card-subtitle {
+  margin-top: -($card-spacer-y / 2);
+  margin-bottom: 0;
+}
+
+.card-text:last-child {
+  margin-bottom: 0;
+}
+
+.card-link {
+  @include hover {
+    text-decoration: none;
+  }
+
+  + .card-link {
+    margin-left: $card-spacer-x;
+  }
+}
+
+//
+// Optional textual caps
+//
+
+.card-header {
+  padding: $card-spacer-y $card-spacer-x;
+  margin-bottom: 0; // Removes the default margin-bottom of <hN>
+  background-color: $card-cap-bg;
+  border-bottom: $card-border-width solid $card-border-color;
+
+  &:first-child {
+    @include border-radius($card-inner-border-radius $card-inner-border-radius 0 0);
+  }
+
+  + .list-group {
+    .list-group-item:first-child {
+      border-top: 0;
+    }
+  }
+}
+
+.card-footer {
+  padding: $card-spacer-y $card-spacer-x;
+  background-color: $card-cap-bg;
+  border-top: $card-border-width solid $card-border-color;
+
+  &:last-child {
+    @include border-radius(0 0 $card-inner-border-radius $card-inner-border-radius);
+  }
+}
+
+
+//
+// Header navs
+//
+
+.card-header-tabs {
+  margin-right: -($card-spacer-x / 2);
+  margin-bottom: -$card-spacer-y;
+  margin-left: -($card-spacer-x / 2);
+  border-bottom: 0;
+}
+
+.card-header-pills {
+  margin-right: -($card-spacer-x / 2);
+  margin-left: -($card-spacer-x / 2);
+}
+
+// Card image
+.card-img-overlay {
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  padding: $card-img-overlay-padding;
+}
+
+.card-img {
+  width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
+  @include border-radius($card-inner-border-radius);
+}
+
+// Card image caps
+.card-img-top {
+  width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
+  @include border-top-radius($card-inner-border-radius);
+}
+
+.card-img-bottom {
+  width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
+  @include border-bottom-radius($card-inner-border-radius);
+}
+
+
+// Card deck
+
+.card-deck {
+  display: flex;
+  flex-direction: column;
+
+  .card {
+    margin-bottom: $card-deck-margin;
+  }
+
+  @include media-breakpoint-up(sm) {
+    flex-flow: row wrap;
+    margin-right: -$card-deck-margin;
+    margin-left: -$card-deck-margin;
+
+    .card {
+      display: flex;
+      // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4
+      flex: 1 0 0%;
+      flex-direction: column;
+      margin-right: $card-deck-margin;
+      margin-bottom: 0; // Override the default
+      margin-left: $card-deck-margin;
+    }
+  }
+}
+
+
+//
+// Card groups
+//
+
+.card-group {
+  display: flex;
+  flex-direction: column;
+
+  // The child selector allows nested `.card` within `.card-group`
+  // to display properly.
+  > .card {
+    margin-bottom: $card-group-margin;
+  }
+
+  @include media-breakpoint-up(sm) {
+    flex-flow: row wrap;
+    // The child selector allows nested `.card` within `.card-group`
+    // to display properly.
+    > .card {
+      // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4
+      flex: 1 0 0%;
+      margin-bottom: 0;
+
+      + .card {
+        margin-left: 0;
+        border-left: 0;
+      }
+
+      // Handle rounded corners
+      @if $enable-rounded {
+        &:first-child {
+          @include border-right-radius(0);
+
+          .card-img-top,
+          .card-header {
+            border-top-right-radius: 0;
+          }
+          .card-img-bottom,
+          .card-footer {
+            border-bottom-right-radius: 0;
+          }
+        }
+
+        &:last-child {
+          @include border-left-radius(0);
+
+          .card-img-top,
+          .card-header {
+            border-top-left-radius: 0;
+          }
+          .card-img-bottom,
+          .card-footer {
+            border-bottom-left-radius: 0;
+          }
+        }
+
+        &:only-child {
+          @include border-radius($card-border-radius);
+
+          .card-img-top,
+          .card-header {
+            @include border-top-radius($card-border-radius);
+          }
+          .card-img-bottom,
+          .card-footer {
+            @include border-bottom-radius($card-border-radius);
+          }
+        }
+
+        &:not(:first-child):not(:last-child):not(:only-child) {
+          @include border-radius(0);
+
+          .card-img-top,
+          .card-img-bottom,
+          .card-header,
+          .card-footer {
+            @include border-radius(0);
+          }
+        }
+      }
+    }
+  }
+}
+
+
+//
+// Columns
+//
+
+.card-columns {
+  .card {
+    margin-bottom: $card-columns-margin;
+  }
+
+  @include media-breakpoint-up(sm) {
+    column-count: $card-columns-count;
+    column-gap: $card-columns-gap;
+    orphans: 1;
+    widows: 1;
+
+    .card {
+      display: inline-block; // Don't let them vertically span multiple columns
+      width: 100%; // Don't let their width change
+    }
+  }
+}
+
+
+//
+// Accordion
+//
+
+.accordion {
+  .card:not(:first-of-type):not(:last-of-type) {
+    border-bottom: 0;
+    border-radius: 0;
+  }
+
+  .card:not(:first-of-type) {
+    .card-header:first-child {
+      border-radius: 0;
+    }
+  }
+
+  .card:first-of-type {
+    border-bottom: 0;
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .card:last-of-type {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+  }
+}
diff --git a/site/_sass/bootstrap/_carousel.scss b/site/_sass/bootstrap/_carousel.scss
new file mode 100644
index 0000000..91c23e5
--- /dev/null
+++ b/site/_sass/bootstrap/_carousel.scss
@@ -0,0 +1,236 @@
+// Notes on the classes:
+//
+// 1. The .carousel-item-left and .carousel-item-right is used to indicate where
+//    the active slide is heading.
+// 2. .active.carousel-item is the current slide.
+// 3. .active.carousel-item-left and .active.carousel-item-right is the current
+//    slide in its in-transition state. Only one of these occurs at a time.
+// 4. .carousel-item-next.carousel-item-left and .carousel-item-prev.carousel-item-right
+//    is the upcoming slide in transition.
+
+.carousel {
+  position: relative;
+}
+
+.carousel-inner {
+  position: relative;
+  width: 100%;
+  overflow: hidden;
+}
+
+.carousel-item {
+  position: relative;
+  display: none;
+  align-items: center;
+  width: 100%;
+  backface-visibility: hidden;
+  perspective: 1000px;
+}
+
+.carousel-item.active,
+.carousel-item-next,
+.carousel-item-prev {
+  display: block;
+  @include transition($carousel-transition);
+}
+
+.carousel-item-next,
+.carousel-item-prev {
+  position: absolute;
+  top: 0;
+}
+
+.carousel-item-next.carousel-item-left,
+.carousel-item-prev.carousel-item-right {
+  transform: translateX(0);
+
+  @supports (transform-style: preserve-3d) {
+    transform: translate3d(0, 0, 0);
+  }
+}
+
+.carousel-item-next,
+.active.carousel-item-right {
+  transform: translateX(100%);
+
+  @supports (transform-style: preserve-3d) {
+    transform: translate3d(100%, 0, 0);
+  }
+}
+
+.carousel-item-prev,
+.active.carousel-item-left {
+  transform: translateX(-100%);
+
+  @supports (transform-style: preserve-3d) {
+    transform: translate3d(-100%, 0, 0);
+  }
+}
+
+
+//
+// Alternate transitions
+//
+
+.carousel-fade {
+  .carousel-item {
+    opacity: 0;
+    transition-duration: .6s;
+    transition-property: opacity;
+  }
+
+  .carousel-item.active,
+  .carousel-item-next.carousel-item-left,
+  .carousel-item-prev.carousel-item-right {
+    opacity: 1;
+  }
+
+  .active.carousel-item-left,
+  .active.carousel-item-right {
+    opacity: 0;
+  }
+
+  .carousel-item-next,
+  .carousel-item-prev,
+  .carousel-item.active,
+  .active.carousel-item-left,
+  .active.carousel-item-prev {
+    transform: translateX(0);
+
+    @supports (transform-style: preserve-3d) {
+      transform: translate3d(0, 0, 0);
+    }
+  }
+}
+
+
+//
+// Left/right controls for nav
+//
+
+.carousel-control-prev,
+.carousel-control-next {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  // Use flex for alignment (1-3)
+  display: flex; // 1. allow flex styles
+  align-items: center; // 2. vertically center contents
+  justify-content: center; // 3. horizontally center contents
+  width: $carousel-control-width;
+  color: $carousel-control-color;
+  text-align: center;
+  opacity: $carousel-control-opacity;
+  // We can't have a transition here because WebKit cancels the carousel
+  // animation if you trip this while in the middle of another animation.
+
+  // Hover/focus state
+  @include hover-focus {
+    color: $carousel-control-color;
+    text-decoration: none;
+    outline: 0;
+    opacity: .9;
+  }
+}
+.carousel-control-prev {
+  left: 0;
+  @if $enable-gradients {
+    background: linear-gradient(90deg, rgba($black, .25), rgba($black, .001));
+  }
+}
+.carousel-control-next {
+  right: 0;
+  @if $enable-gradients {
+    background: linear-gradient(270deg, rgba($black, .25), rgba($black, .001));
+  }
+}
+
+// Icons for within
+.carousel-control-prev-icon,
+.carousel-control-next-icon {
+  display: inline-block;
+  width: $carousel-control-icon-width;
+  height: $carousel-control-icon-width;
+  background: transparent no-repeat center center;
+  background-size: 100% 100%;
+}
+.carousel-control-prev-icon {
+  background-image: $carousel-control-prev-icon-bg;
+}
+.carousel-control-next-icon {
+  background-image: $carousel-control-next-icon-bg;
+}
+
+
+// Optional indicator pips
+//
+// Add an ordered list with the following class and add a list item for each
+// slide your carousel holds.
+
+.carousel-indicators {
+  position: absolute;
+  right: 0;
+  bottom: 10px;
+  left: 0;
+  z-index: 15;
+  display: flex;
+  justify-content: center;
+  padding-left: 0; // override <ol> default
+  // Use the .carousel-control's width as margin so we don't overlay those
+  margin-right: $carousel-control-width;
+  margin-left: $carousel-control-width;
+  list-style: none;
+
+  li {
+    position: relative;
+    flex: 0 1 auto;
+    width: $carousel-indicator-width;
+    height: $carousel-indicator-height;
+    margin-right: $carousel-indicator-spacer;
+    margin-left: $carousel-indicator-spacer;
+    text-indent: -999px;
+    cursor: pointer;
+    background-color: rgba($carousel-indicator-active-bg, .5);
+
+    // Use pseudo classes to increase the hit area by 10px on top and bottom.
+    &::before {
+      position: absolute;
+      top: -10px;
+      left: 0;
+      display: inline-block;
+      width: 100%;
+      height: 10px;
+      content: "";
+    }
+    &::after {
+      position: absolute;
+      bottom: -10px;
+      left: 0;
+      display: inline-block;
+      width: 100%;
+      height: 10px;
+      content: "";
+    }
+  }
+
+  .active {
+    background-color: $carousel-indicator-active-bg;
+  }
+}
+
+
+// Optional captions
+//
+//
+
+.carousel-caption {
+  position: absolute;
+  right: ((100% - $carousel-caption-width) / 2);
+  bottom: 20px;
+  left: ((100% - $carousel-caption-width) / 2);
+  z-index: 10;
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: $carousel-caption-color;
+  text-align: center;
+}
diff --git a/site/_sass/bootstrap/_close.scss b/site/_sass/bootstrap/_close.scss
new file mode 100644
index 0000000..a0dd1e2
--- /dev/null
+++ b/site/_sass/bootstrap/_close.scss
@@ -0,0 +1,35 @@
+.close {
+  float: right;
+  font-size: $close-font-size;
+  font-weight: $close-font-weight;
+  line-height: 1;
+  color: $close-color;
+  text-shadow: $close-text-shadow;
+  opacity: .5;
+
+  &:not(:disabled):not(.disabled) {
+
+    @include hover-focus {
+      color: $close-color;
+      text-decoration: none;
+      opacity: .75;
+    }
+
+    // Opinionated: add "hand" cursor to non-disabled .close elements
+    cursor: pointer;
+  }
+}
+
+// Additional properties for button version
+// iOS requires the button element instead of an anchor tag.
+// If you want the anchor version, it requires `href="#"`.
+// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
+
+// stylelint-disable property-no-vendor-prefix, selector-no-qualifying-type
+button.close {
+  padding: 0;
+  background-color: transparent;
+  border: 0;
+  -webkit-appearance: none;
+}
+// stylelint-enable
diff --git a/site/_sass/bootstrap/_code.scss b/site/_sass/bootstrap/_code.scss
new file mode 100644
index 0000000..9b2e027
--- /dev/null
+++ b/site/_sass/bootstrap/_code.scss
@@ -0,0 +1,48 @@
+// Inline code
+code {
+  font-size: $code-font-size;
+  color: $code-color;
+  word-break: break-word;
+
+  // Streamline the style when inside anchors to avoid broken underline and more
+  a > & {
+    color: inherit;
+  }
+}
+
+// User input typically entered via keyboard
+kbd {
+  padding: $kbd-padding-y $kbd-padding-x;
+  font-size: $kbd-font-size;
+  color: $kbd-color;
+  background-color: $kbd-bg;
+  @include border-radius($border-radius-sm);
+  @include box-shadow($kbd-box-shadow);
+
+  kbd {
+    padding: 0;
+    font-size: 100%;
+    font-weight: $nested-kbd-font-weight;
+    @include box-shadow(none);
+  }
+}
+
+// Blocks of code
+pre {
+  display: block;
+  font-size: $code-font-size;
+  color: $pre-color;
+
+  // Account for some code outputs that place code tags in pre tags
+  code {
+    font-size: inherit;
+    color: inherit;
+    word-break: normal;
+  }
+}
+
+// Enable scrollable blocks of code
+.pre-scrollable {
+  max-height: $pre-scrollable-max-height;
+  overflow-y: scroll;
+}
diff --git a/site/_sass/bootstrap/_custom-forms.scss b/site/_sass/bootstrap/_custom-forms.scss
new file mode 100644
index 0000000..8348e26
--- /dev/null
+++ b/site/_sass/bootstrap/_custom-forms.scss
@@ -0,0 +1,433 @@
+// Embedded icons from Open Iconic.
+// Released under MIT and copyright 2014 Waybury.
+// https://useiconic.com/open
+
+
+// Checkboxes and radios
+//
+// Base class takes care of all the key behavioral aspects.
+
+.custom-control {
+  position: relative;
+  display: block;
+  min-height: ($font-size-base * $line-height-base);
+  padding-left: $custom-control-gutter;
+}
+
+.custom-control-inline {
+  display: inline-flex;
+  margin-right: $custom-control-spacer-x;
+}
+
+.custom-control-input {
+  position: absolute;
+  z-index: -1; // Put the input behind the label so it doesn't overlay text
+  opacity: 0;
+
+  &:checked ~ .custom-control-label::before {
+    color: $custom-control-indicator-checked-color;
+    @include gradient-bg($custom-control-indicator-checked-bg);
+    @include box-shadow($custom-control-indicator-checked-box-shadow);
+  }
+
+  &:focus ~ .custom-control-label::before {
+    // the mixin is not used here to make sure there is feedback
+    box-shadow: $custom-control-indicator-focus-box-shadow;
+  }
+
+  &:active ~ .custom-control-label::before {
+    color: $custom-control-indicator-active-color;
+    background-color: $custom-control-indicator-active-bg;
+    @include box-shadow($custom-control-indicator-active-box-shadow);
+  }
+
+  &:disabled {
+    ~ .custom-control-label {
+      color: $custom-control-label-disabled-color;
+
+      &::before {
+        background-color: $custom-control-indicator-disabled-bg;
+      }
+    }
+  }
+}
+
+// Custom control indicators
+//
+// Build the custom controls out of pseudo-elements.
+
+.custom-control-label {
+  position: relative;
+  margin-bottom: 0;
+
+  // Background-color and (when enabled) gradient
+  &::before {
+    position: absolute;
+    top: (($font-size-base * $line-height-base - $custom-control-indicator-size) / 2);
+    left: -$custom-control-gutter;
+    display: block;
+    width: $custom-control-indicator-size;
+    height: $custom-control-indicator-size;
+    pointer-events: none;
+    content: "";
+    user-select: none;
+    background-color: $custom-control-indicator-bg;
+    @include box-shadow($custom-control-indicator-box-shadow);
+  }
+
+  // Foreground (icon)
+  &::after {
+    position: absolute;
+    top: (($font-size-base * $line-height-base - $custom-control-indicator-size) / 2);
+    left: -$custom-control-gutter;
+    display: block;
+    width: $custom-control-indicator-size;
+    height: $custom-control-indicator-size;
+    content: "";
+    background-repeat: no-repeat;
+    background-position: center center;
+    background-size: $custom-control-indicator-bg-size;
+  }
+}
+
+
+// Checkboxes
+//
+// Tweak just a few things for checkboxes.
+
+.custom-checkbox {
+  .custom-control-label::before {
+    @include border-radius($custom-checkbox-indicator-border-radius);
+  }
+
+  .custom-control-input:checked ~ .custom-control-label {
+    &::before {
+      @include gradient-bg($custom-control-indicator-checked-bg);
+    }
+    &::after {
+      background-image: $custom-checkbox-indicator-icon-checked;
+    }
+  }
+
+  .custom-control-input:indeterminate ~ .custom-control-label {
+    &::before {
+      @include gradient-bg($custom-checkbox-indicator-indeterminate-bg);
+      @include box-shadow($custom-checkbox-indicator-indeterminate-box-shadow);
+    }
+    &::after {
+      background-image: $custom-checkbox-indicator-icon-indeterminate;
+    }
+  }
+
+  .custom-control-input:disabled {
+    &:checked ~ .custom-control-label::before {
+      background-color: $custom-control-indicator-checked-disabled-bg;
+    }
+    &:indeterminate ~ .custom-control-label::before {
+      background-color: $custom-control-indicator-checked-disabled-bg;
+    }
+  }
+}
+
+// Radios
+//
+// Tweak just a few things for radios.
+
+.custom-radio {
+  .custom-control-label::before {
+    border-radius: $custom-radio-indicator-border-radius;
+  }
+
+  .custom-control-input:checked ~ .custom-control-label {
+    &::before {
+      @include gradient-bg($custom-control-indicator-checked-bg);
+    }
+    &::after {
+      background-image: $custom-radio-indicator-icon-checked;
+    }
+  }
+
+  .custom-control-input:disabled {
+    &:checked ~ .custom-control-label::before {
+      background-color: $custom-control-indicator-checked-disabled-bg;
+    }
+  }
+}
+
+
+// Select
+//
+// Replaces the browser default select with a custom one, mostly pulled from
+// https://primer.github.io/.
+//
+
+.custom-select {
+  display: inline-block;
+  width: 100%;
+  height: $custom-select-height;
+  padding: $custom-select-padding-y ($custom-select-padding-x + $custom-select-indicator-padding) $custom-select-padding-y $custom-select-padding-x;
+  line-height: $custom-select-line-height;
+  color: $custom-select-color;
+  vertical-align: middle;
+  background: $custom-select-bg $custom-select-indicator no-repeat right $custom-select-padding-x center;
+  background-size: $custom-select-bg-size;
+  border: $custom-select-border-width solid $custom-select-border-color;
+  @if $enable-rounded {
+    border-radius: $custom-select-border-radius;
+  } @else {
+    border-radius: 0;
+  }
+  @include box-shadow($custom-select-box-shadow);
+  appearance: none;
+
+  &:focus {
+    border-color: $custom-select-focus-border-color;
+    outline: 0;
+    @if $enable-shadows {
+      box-shadow: $custom-select-box-shadow, $custom-select-focus-box-shadow;
+    } @else {
+      box-shadow: $custom-select-focus-box-shadow;
+    }
+
+    &::-ms-value {
+      // For visual consistency with other platforms/browsers,
+      // suppress the default white text on blue background highlight given to
+      // the selected option text when the (still closed) <select> receives focus
+      // in IE and (under certain conditions) Edge.
+      // See https://github.com/twbs/bootstrap/issues/19398.
+      color: $input-color;
+      background-color: $input-bg;
+    }
+  }
+
+  &[multiple],
+  &[size]:not([size="1"]) {
+    height: auto;
+    padding-right: $custom-select-padding-x;
+    background-image: none;
+  }
+
+  &:disabled {
+    color: $custom-select-disabled-color;
+    background-color: $custom-select-disabled-bg;
+  }
+
+  // Hides the default caret in IE11
+  &::-ms-expand {
+    opacity: 0;
+  }
+}
+
+.custom-select-sm {
+  height: $custom-select-height-sm;
+  padding-top: $custom-select-padding-y;
+  padding-bottom: $custom-select-padding-y;
+  font-size: $custom-select-font-size-sm;
+}
+
+.custom-select-lg {
+  height: $custom-select-height-lg;
+  padding-top: $custom-select-padding-y;
+  padding-bottom: $custom-select-padding-y;
+  font-size: $custom-select-font-size-lg;
+}
+
+
+// File
+//
+// Custom file input.
+
+.custom-file {
+  position: relative;
+  display: inline-block;
+  width: 100%;
+  height: $custom-file-height;
+  margin-bottom: 0;
+}
+
+.custom-file-input {
+  position: relative;
+  z-index: 2;
+  width: 100%;
+  height: $custom-file-height;
+  margin: 0;
+  opacity: 0;
+
+  &:focus ~ .custom-file-label {
+    border-color: $custom-file-focus-border-color;
+    box-shadow: $custom-file-focus-box-shadow;
+
+    &::after {
+      border-color: $custom-file-focus-border-color;
+    }
+  }
+
+  &:disabled ~ .custom-file-label {
+    background-color: $custom-file-disabled-bg;
+  }
+
+  @each $lang, $value in $custom-file-text {
+    &:lang(#{$lang}) ~ .custom-file-label::after {
+      content: $value;
+    }
+  }
+}
+
+.custom-file-label {
+  position: absolute;
+  top: 0;
+  right: 0;
+  left: 0;
+  z-index: 1;
+  height: $custom-file-height;
+  padding: $custom-file-padding-y $custom-file-padding-x;
+  line-height: $custom-file-line-height;
+  color: $custom-file-color;
+  background-color: $custom-file-bg;
+  border: $custom-file-border-width solid $custom-file-border-color;
+  @include border-radius($custom-file-border-radius);
+  @include box-shadow($custom-file-box-shadow);
+
+  &::after {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 3;
+    display: block;
+    height: $custom-file-height-inner;
+    padding: $custom-file-padding-y $custom-file-padding-x;
+    line-height: $custom-file-line-height;
+    color: $custom-file-button-color;
+    content: "Browse";
+    @include gradient-bg($custom-file-button-bg);
+    border-left: $custom-file-border-width solid $custom-file-border-color;
+    @include border-radius(0 $custom-file-border-radius $custom-file-border-radius 0);
+  }
+}
+
+// Range
+//
+// Style range inputs the same across browsers. Vendor-specific rules for pseudo
+// elements cannot be mixed. As such, there are no shared styles for focus or
+// active states on prefixed selectors.
+
+.custom-range {
+  width: 100%;
+  padding-left: 0; // Firefox specific
+  background-color: transparent;
+  appearance: none;
+
+  &:focus {
+    outline: none;
+
+    // Pseudo-elements must be split across multiple rulesets to have an affect.
+    // No box-shadow() mixin for focus accessibility.
+    &::-webkit-slider-thumb { box-shadow: $custom-range-thumb-focus-box-shadow; }
+    &::-moz-range-thumb     { box-shadow: $custom-range-thumb-focus-box-shadow; }
+    &::-ms-thumb            { box-shadow: $custom-range-thumb-focus-box-shadow; }
+  }
+
+  &::-moz-focus-outer {
+    border: 0;
+  }
+
+  &::-webkit-slider-thumb {
+    width: $custom-range-thumb-width;
+    height: $custom-range-thumb-height;
+    margin-top: (($custom-range-track-height - $custom-range-thumb-height) / 2); // Webkit specific
+    @include gradient-bg($custom-range-thumb-bg);
+    border: $custom-range-thumb-border;
+    @include border-radius($custom-range-thumb-border-radius);
+    @include box-shadow($custom-range-thumb-box-shadow);
+    @include transition($custom-forms-transition);
+    appearance: none;
+
+    &:active {
+      @include gradient-bg($custom-range-thumb-active-bg);
+    }
+  }
+
+  &::-webkit-slider-runnable-track {
+    width: $custom-range-track-width;
+    height: $custom-range-track-height;
+    color: transparent; // Why?
+    cursor: $custom-range-track-cursor;
+    background-color: $custom-range-track-bg;
+    border-color: transparent;
+    @include border-radius($custom-range-track-border-radius);
+    @include box-shadow($custom-range-track-box-shadow);
+  }
+
+  &::-moz-range-thumb {
+    width: $custom-range-thumb-width;
+    height: $custom-range-thumb-height;
+    @include gradient-bg($custom-range-thumb-bg);
+    border: $custom-range-thumb-border;
+    @include border-radius($custom-range-thumb-border-radius);
+    @include box-shadow($custom-range-thumb-box-shadow);
+    @include transition($custom-forms-transition);
+    appearance: none;
+
+    &:active {
+      @include gradient-bg($custom-range-thumb-active-bg);
+    }
+  }
+
+  &::-moz-range-track {
+    width: $custom-range-track-width;
+    height: $custom-range-track-height;
+    color: transparent;
+    cursor: $custom-range-track-cursor;
+    background-color: $custom-range-track-bg;
+    border-color: transparent; // Firefox specific?
+    @include border-radius($custom-range-track-border-radius);
+    @include box-shadow($custom-range-track-box-shadow);
+  }
+
+  &::-ms-thumb {
+    width: $custom-range-thumb-width;
+    height: $custom-range-thumb-height;
+    margin-top: 0; // Edge specific
+    margin-right: $custom-range-thumb-focus-box-shadow-width; // Workaround that overflowed box-shadow is hidden.
+    margin-left: $custom-range-thumb-focus-box-shadow-width;  // Workaround that overflowed box-shadow is hidden.
+    @include gradient-bg($custom-range-thumb-bg);
+    border: $custom-range-thumb-border;
+    @include border-radius($custom-range-thumb-border-radius);
+    @include box-shadow($custom-range-thumb-box-shadow);
+    @include transition($custom-forms-transition);
+    appearance: none;
+
+    &:active {
+      @include gradient-bg($custom-range-thumb-active-bg);
+    }
+  }
+
+  &::-ms-track {
+    width: $custom-range-track-width;
+    height: $custom-range-track-height;
+    color: transparent;
+    cursor: $custom-range-track-cursor;
+    background-color: transparent;
+    border-color: transparent;
+    border-width: ($custom-range-thumb-height * .5);
+    @include box-shadow($custom-range-track-box-shadow);
+  }
+
+  &::-ms-fill-lower {
+    background-color: $custom-range-track-bg;
+    @include border-radius($custom-range-track-border-radius);
+  }
+
+  &::-ms-fill-upper {
+    margin-right: 15px; // arbitrary?
+    background-color: $custom-range-track-bg;
+    @include border-radius($custom-range-track-border-radius);
+  }
+}
+
+.custom-control-label::before,
+.custom-file-label,
+.custom-select {
+  @include transition($custom-forms-transition);
+}
diff --git a/site/_sass/bootstrap/_dropdown.scss b/site/_sass/bootstrap/_dropdown.scss
new file mode 100644
index 0000000..ee6f658
--- /dev/null
+++ b/site/_sass/bootstrap/_dropdown.scss
@@ -0,0 +1,166 @@
+// The dropdown wrapper (`<div>`)
+.dropup,
+.dropright,
+.dropdown,
+.dropleft {
+  position: relative;
+}
+
+.dropdown-toggle {
+  // Generate the caret automatically
+  @include caret;
+}
+
+// The dropdown menu
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: $zindex-dropdown;
+  display: none; // none by default, but block on "open" of the menu
+  float: left;
+  min-width: $dropdown-min-width;
+  padding: $dropdown-padding-y 0;
+  margin: $dropdown-spacer 0 0; // override default ul
+  font-size: $font-size-base; // Redeclare because nesting can cause inheritance issues
+  color: $body-color;
+  text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
+  list-style: none;
+  background-color: $dropdown-bg;
+  background-clip: padding-box;
+  border: $dropdown-border-width solid $dropdown-border-color;
+  @include border-radius($dropdown-border-radius);
+  @include box-shadow($dropdown-box-shadow);
+}
+
+.dropdown-menu-right {
+  right: 0;
+  left: auto;
+}
+
+// Allow for dropdowns to go bottom up (aka, dropup-menu)
+// Just add .dropup after the standard .dropdown class and you're set.
+.dropup {
+  .dropdown-menu {
+    top: auto;
+    bottom: 100%;
+    margin-top: 0;
+    margin-bottom: $dropdown-spacer;
+  }
+
+  .dropdown-toggle {
+    @include caret(up);
+  }
+}
+
+.dropright {
+  .dropdown-menu {
+    top: 0;
+    right: auto;
+    left: 100%;
+    margin-top: 0;
+    margin-left: $dropdown-spacer;
+  }
+
+  .dropdown-toggle {
+    @include caret(right);
+    &::after {
+      vertical-align: 0;
+    }
+  }
+}
+
+.dropleft {
+  .dropdown-menu {
+    top: 0;
+    right: 100%;
+    left: auto;
+    margin-top: 0;
+    margin-right: $dropdown-spacer;
+  }
+
+  .dropdown-toggle {
+    @include caret(left);
+    &::before {
+      vertical-align: 0;
+    }
+  }
+}
+
+// When enabled Popper.js, reset basic dropdown position
+// stylelint-disable no-duplicate-selectors
+.dropdown-menu {
+  &[x-placement^="top"],
+  &[x-placement^="right"],
+  &[x-placement^="bottom"],
+  &[x-placement^="left"] {
+    right: auto;
+    bottom: auto;
+  }
+}
+// stylelint-enable no-duplicate-selectors
+
+// Dividers (basically an `<hr>`) within the dropdown
+.dropdown-divider {
+  @include nav-divider($dropdown-divider-bg);
+}
+
+// Links, buttons, and more within the dropdown menu
+//
+// `<button>`-specific styles are denoted with `// For <button>s`
+.dropdown-item {
+  display: block;
+  width: 100%; // For `<button>`s
+  padding: $dropdown-item-padding-y $dropdown-item-padding-x;
+  clear: both;
+  font-weight: $font-weight-normal;
+  color: $dropdown-link-color;
+  text-align: inherit; // For `<button>`s
+  white-space: nowrap; // prevent links from randomly breaking onto new lines
+  background-color: transparent; // For `<button>`s
+  border: 0; // For `<button>`s
+
+  @include hover-focus {
+    color: $dropdown-link-hover-color;
+    text-decoration: none;
+    @include gradient-bg($dropdown-link-hover-bg);
+  }
+
+  &.active,
+  &:active {
+    color: $dropdown-link-active-color;
+    text-decoration: none;
+    @include gradient-bg($dropdown-link-active-bg);
+  }
+
+  &.disabled,
+  &:disabled {
+    color: $dropdown-link-disabled-color;
+    background-color: transparent;
+    // Remove CSS gradients if they're enabled
+    @if $enable-gradients {
+      background-image: none;
+    }
+  }
+}
+
+.dropdown-menu.show {
+  display: block;
+}
+
+// Dropdown section headers
+.dropdown-header {
+  display: block;
+  padding: $dropdown-padding-y $dropdown-item-padding-x;
+  margin-bottom: 0; // for use with heading elements
+  font-size: $font-size-sm;
+  color: $dropdown-header-color;
+  white-space: nowrap; // as with > li > a
+}
+
+// Dropdown text
+.dropdown-item-text {
+  display: block;
+  padding: $dropdown-item-padding-y $dropdown-item-padding-x;
+  color: $dropdown-link-color;
+}
diff --git a/site/_sass/bootstrap/_forms.scss b/site/_sass/bootstrap/_forms.scss
new file mode 100644
index 0000000..5530630
--- /dev/null
+++ b/site/_sass/bootstrap/_forms.scss
@@ -0,0 +1,333 @@
+// stylelint-disable selector-no-qualifying-type
+
+//
+// Textual form controls
+//
+
+.form-control {
+  display: block;
+  width: 100%;
+  height: $input-height;
+  padding: $input-padding-y $input-padding-x;
+  font-size: $font-size-base;
+  line-height: $input-line-height;
+  color: $input-color;
+  background-color: $input-bg;
+  background-clip: padding-box;
+  border: $input-border-width solid $input-border-color;
+
+  // Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS.
+  @if $enable-rounded {
+    // Manually use the if/else instead of the mixin to account for iOS override
+    border-radius: $input-border-radius;
+  } @else {
+    // Otherwise undo the iOS default
+    border-radius: 0;
+  }
+
+  @include box-shadow($input-box-shadow);
+  @include transition($input-transition);
+
+  // Unstyle the caret on `<select>`s in IE10+.
+  &::-ms-expand {
+    background-color: transparent;
+    border: 0;
+  }
+
+  // Customize the `:focus` state to imitate native WebKit styles.
+  @include form-control-focus();
+
+  // Placeholder
+  &::placeholder {
+    color: $input-placeholder-color;
+    // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526.
+    opacity: 1;
+  }
+
+  // Disabled and read-only inputs
+  //
+  // HTML5 says that controls under a fieldset > legend:first-child won't be
+  // disabled if the fieldset is disabled. Due to implementation difficulty, we
+  // don't honor that edge case; we style them as disabled anyway.
+  &:disabled,
+  &[readonly] {
+    background-color: $input-disabled-bg;
+    // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
+    opacity: 1;
+  }
+}
+
+select.form-control {
+  &:focus::-ms-value {
+    // Suppress the nested default white text on blue background highlight given to
+    // the selected option text when the (still closed) <select> receives focus
+    // in IE and (under certain conditions) Edge, as it looks bad and cannot be made to
+    // match the appearance of the native widget.
+    // See https://github.com/twbs/bootstrap/issues/19398.
+    color: $input-color;
+    background-color: $input-bg;
+  }
+}
+
+// Make file inputs better match text inputs by forcing them to new lines.
+.form-control-file,
+.form-control-range {
+  display: block;
+  width: 100%;
+}
+
+
+//
+// Labels
+//
+
+// For use with horizontal and inline forms, when you need the label (or legend)
+// text to align with the form controls.
+.col-form-label {
+  padding-top: calc(#{$input-padding-y} + #{$input-border-width});
+  padding-bottom: calc(#{$input-padding-y} + #{$input-border-width});
+  margin-bottom: 0; // Override the `<label>/<legend>` default
+  font-size: inherit; // Override the `<legend>` default
+  line-height: $input-line-height;
+}
+
+.col-form-label-lg {
+  padding-top: calc(#{$input-padding-y-lg} + #{$input-border-width});
+  padding-bottom: calc(#{$input-padding-y-lg} + #{$input-border-width});
+  font-size: $font-size-lg;
+  line-height: $input-line-height-lg;
+}
+
+.col-form-label-sm {
+  padding-top: calc(#{$input-padding-y-sm} + #{$input-border-width});
+  padding-bottom: calc(#{$input-padding-y-sm} + #{$input-border-width});
+  font-size: $font-size-sm;
+  line-height: $input-line-height-sm;
+}
+
+
+// Readonly controls as plain text
+//
+// Apply class to a readonly input to make it appear like regular plain
+// text (without any border, background color, focus indicator)
+
+.form-control-plaintext {
+  display: block;
+  width: 100%;
+  padding-top: $input-padding-y;
+  padding-bottom: $input-padding-y;
+  margin-bottom: 0; // match inputs if this class comes on inputs with default margins
+  line-height: $input-line-height;
+  color: $input-plaintext-color;
+  background-color: transparent;
+  border: solid transparent;
+  border-width: $input-border-width 0;
+
+  &.form-control-sm,
+  &.form-control-lg {
+    padding-right: 0;
+    padding-left: 0;
+  }
+}
+
+
+// Form control sizing
+//
+// Build on `.form-control` with modifier classes to decrease or increase the
+// height and font-size of form controls.
+//
+// Repeated in `_input_group.scss` to avoid Sass extend issues.
+
+.form-control-sm {
+  height: $input-height-sm;
+  padding: $input-padding-y-sm $input-padding-x-sm;
+  font-size: $font-size-sm;
+  line-height: $input-line-height-sm;
+  @include border-radius($input-border-radius-sm);
+}
+
+.form-control-lg {
+  height: $input-height-lg;
+  padding: $input-padding-y-lg $input-padding-x-lg;
+  font-size: $font-size-lg;
+  line-height: $input-line-height-lg;
+  @include border-radius($input-border-radius-lg);
+}
+
+// stylelint-disable no-duplicate-selectors
+select.form-control {
+  &[size],
+  &[multiple] {
+    height: auto;
+  }
+}
+
+textarea.form-control {
+  height: auto;
+}
+// stylelint-enable no-duplicate-selectors
+
+// Form groups
+//
+// Designed to help with the organization and spacing of vertical forms. For
+// horizontal forms, use the predefined grid classes.
+
+.form-group {
+  margin-bottom: $form-group-margin-bottom;
+}
+
+.form-text {
+  display: block;
+  margin-top: $form-text-margin-top;
+}
+
+
+// Form grid
+//
+// Special replacement for our grid system's `.row` for tighter form layouts.
+
+.form-row {
+  display: flex;
+  flex-wrap: wrap;
+  margin-right: -5px;
+  margin-left: -5px;
+
+  > .col,
+  > [class*="col-"] {
+    padding-right: 5px;
+    padding-left: 5px;
+  }
+}
+
+
+// Checkboxes and radios
+//
+// Indent the labels to position radios/checkboxes as hanging controls.
+
+.form-check {
+  position: relative;
+  display: block;
+  padding-left: $form-check-input-gutter;
+}
+
+.form-check-input {
+  position: absolute;
+  margin-top: $form-check-input-margin-y;
+  margin-left: -$form-check-input-gutter;
+
+  &:disabled ~ .form-check-label {
+    color: $text-muted;
+  }
+}
+
+.form-check-label {
+  margin-bottom: 0; // Override default `<label>` bottom margin
+}
+
+.form-check-inline {
+  display: inline-flex;
+  align-items: center;
+  padding-left: 0; // Override base .form-check
+  margin-right: $form-check-inline-margin-x;
+
+  // Undo .form-check-input defaults and add some `margin-right`.
+  .form-check-input {
+    position: static;
+    margin-top: 0;
+    margin-right: $form-check-inline-input-margin-x;
+    margin-left: 0;
+  }
+}
+
+
+// Form validation
+//
+// Provide feedback to users when form field values are valid or invalid. Works
+// primarily for client-side validation via scoped `:invalid` and `:valid`
+// pseudo-classes but also includes `.is-invalid` and `.is-valid` classes for
+// server side validation.
+
+@include form-validation-state("valid", $form-feedback-valid-color);
+@include form-validation-state("invalid", $form-feedback-invalid-color);
+
+// Inline forms
+//
+// Make forms appear inline(-block) by adding the `.form-inline` class. Inline
+// forms begin stacked on extra small (mobile) devices and then go inline when
+// viewports reach <768px.
+//
+// Requires wrapping inputs and labels with `.form-group` for proper display of
+// default HTML form controls and our custom form controls (e.g., input groups).
+
+.form-inline {
+  display: flex;
+  flex-flow: row wrap;
+  align-items: center; // Prevent shorter elements from growing to same height as others (e.g., small buttons growing to normal sized button height)
+
+  // Because we use flex, the initial sizing of checkboxes is collapsed and
+  // doesn't occupy the full-width (which is what we want for xs grid tier),
+  // so we force that here.
+  .form-check {
+    width: 100%;
+  }
+
+  // Kick in the inline
+  @include media-breakpoint-up(sm) {
+    label {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      margin-bottom: 0;
+    }
+
+    // Inline-block all the things for "inline"
+    .form-group {
+      display: flex;
+      flex: 0 0 auto;
+      flex-flow: row wrap;
+      align-items: center;
+      margin-bottom: 0;
+    }
+
+    // Allow folks to *not* use `.form-group`
+    .form-control {
+      display: inline-block;
+      width: auto; // Prevent labels from stacking above inputs in `.form-group`
+      vertical-align: middle;
+    }
+
+    // Make static controls behave like regular ones
+    .form-control-plaintext {
+      display: inline-block;
+    }
+
+    .input-group,
+    .custom-select {
+      width: auto;
+    }
+
+    // Remove default margin on radios/checkboxes that were used for stacking, and
+    // then undo the floating of radios and checkboxes to match.
+    .form-check {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: auto;
+      padding-left: 0;
+    }
+    .form-check-input {
+      position: relative;
+      margin-top: 0;
+      margin-right: $form-check-input-margin-x;
+      margin-left: 0;
+    }
+
+    .custom-control {
+      align-items: center;
+      justify-content: center;
+    }
+    .custom-control-label {
+      margin-bottom: 0;
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/_functions.scss b/site/_sass/bootstrap/_functions.scss
new file mode 100644
index 0000000..bf8be9a
--- /dev/null
+++ b/site/_sass/bootstrap/_functions.scss
@@ -0,0 +1,86 @@
+// Bootstrap functions
+//
+// Utility mixins and functions for evaluating source code across our variables, maps, and mixins.
+
+// Ascending
+// Used to evaluate Sass maps like our grid breakpoints.
+@mixin _assert-ascending($map, $map-name) {
+  $prev-key: null;
+  $prev-num: null;
+  @each $key, $num in $map {
+    @if $prev-num == null {
+      // Do nothing
+    } @else if not comparable($prev-num, $num) {
+      @warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
+    } @else if $prev-num >= $num {
+      @warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
+    }
+    $prev-key: $key;
+    $prev-num: $num;
+  }
+}
+
+// Starts at zero
+// Another grid mixin that ensures the min-width of the lowest breakpoint starts at 0.
+@mixin _assert-starts-at-zero($map) {
+  $values: map-values($map);
+  $first-value: nth($values, 1);
+  @if $first-value != 0 {
+    @warn "First breakpoint in `$grid-breakpoints` must start at 0, but starts at #{$first-value}.";
+  }
+}
+
+// Replace `$search` with `$replace` in `$string`
+// Used on our SVG icon backgrounds for custom forms.
+//
+// @author Hugo Giraudel
+// @param {String} $string - Initial string
+// @param {String} $search - Substring to replace
+// @param {String} $replace ('') - New value
+// @return {String} - Updated string
+@function str-replace($string, $search, $replace: "") {
+  $index: str-index($string, $search);
+
+  @if $index {
+    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
+  }
+
+  @return $string;
+}
+
+// Color contrast
+@function color-yiq($color) {
+  $r: red($color);
+  $g: green($color);
+  $b: blue($color);
+
+  $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
+
+  @if ($yiq >= $yiq-contrasted-threshold) {
+    @return $yiq-text-dark;
+  } @else {
+    @return $yiq-text-light;
+  }
+}
+
+// Retrieve color Sass maps
+@function color($key: "blue") {
+  @return map-get($colors, $key);
+}
+
+@function theme-color($key: "primary") {
+  @return map-get($theme-colors, $key);
+}
+
+@function gray($key: "100") {
+  @return map-get($grays, $key);
+}
+
+// Request a theme color level
+@function theme-color-level($color-name: "primary", $level: 0) {
+  $color: theme-color($color-name);
+  $color-base: if($level > 0, $black, $white);
+  $level: abs($level);
+
+  @return mix($color-base, $color, $level * $theme-color-interval);
+}
diff --git a/site/_sass/bootstrap/_grid.scss b/site/_sass/bootstrap/_grid.scss
new file mode 100644
index 0000000..a227515
--- /dev/null
+++ b/site/_sass/bootstrap/_grid.scss
@@ -0,0 +1,52 @@
+// Container widths
+//
+// Set the container width, and override it for fixed navbars in media queries.
+
+@if $enable-grid-classes {
+  .container {
+    @include make-container();
+    @include make-container-max-widths();
+  }
+}
+
+// Fluid container
+//
+// Utilizes the mixin meant for fixed width containers, but with 100% width for
+// fluid, full width layouts.
+
+@if $enable-grid-classes {
+  .container-fluid {
+    @include make-container();
+  }
+}
+
+// Row
+//
+// Rows contain and clear the floats of your columns.
+
+@if $enable-grid-classes {
+  .row {
+    @include make-row();
+  }
+
+  // Remove the negative margin from default .row, then the horizontal padding
+  // from all immediate children columns (to prevent runaway style inheritance).
+  .no-gutters {
+    margin-right: 0;
+    margin-left: 0;
+
+    > .col,
+    > [class*="col-"] {
+      padding-right: 0;
+      padding-left: 0;
+    }
+  }
+}
+
+// Columns
+//
+// Common styles for small and large grid columns
+
+@if $enable-grid-classes {
+  @include make-grid-columns();
+}
diff --git a/site/_sass/bootstrap/_images.scss b/site/_sass/bootstrap/_images.scss
new file mode 100644
index 0000000..2bce02f
--- /dev/null
+++ b/site/_sass/bootstrap/_images.scss
@@ -0,0 +1,42 @@
+// Responsive images (ensure images don't scale beyond their parents)
+//
+// This is purposefully opt-in via an explicit class rather than being the default for all `<img>`s.
+// We previously tried the "images are responsive by default" approach in Bootstrap v2,
+// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)
+// which weren't expecting the images within themselves to be involuntarily resized.
+// See also https://github.com/twbs/bootstrap/issues/18178
+.img-fluid {
+  @include img-fluid;
+}
+
+
+// Image thumbnails
+.img-thumbnail {
+  padding: $thumbnail-padding;
+  background-color: $thumbnail-bg;
+  border: $thumbnail-border-width solid $thumbnail-border-color;
+  @include border-radius($thumbnail-border-radius);
+  @include box-shadow($thumbnail-box-shadow);
+
+  // Keep them at most 100% wide
+  @include img-fluid;
+}
+
+//
+// Figures
+//
+
+.figure {
+  // Ensures the caption's text aligns with the image.
+  display: inline-block;
+}
+
+.figure-img {
+  margin-bottom: ($spacer / 2);
+  line-height: 1;
+}
+
+.figure-caption {
+  font-size: $figure-caption-font-size;
+  color: $figure-caption-color;
+}
diff --git a/site/_sass/bootstrap/_input-group.scss b/site/_sass/bootstrap/_input-group.scss
new file mode 100644
index 0000000..2ed5f33
--- /dev/null
+++ b/site/_sass/bootstrap/_input-group.scss
@@ -0,0 +1,173 @@
+// stylelint-disable selector-no-qualifying-type
+
+//
+// Base styles
+//
+
+.input-group {
+  position: relative;
+  display: flex;
+  flex-wrap: wrap; // For form validation feedback
+  align-items: stretch;
+  width: 100%;
+
+  > .form-control,
+  > .custom-select,
+  > .custom-file {
+    position: relative; // For focus state's z-index
+    flex: 1 1 auto;
+    // Add width 1% and flex-basis auto to ensure that button will not wrap out
+    // the column. Applies to IE Edge+ and Firefox. Chrome does not require this.
+    width: 1%;
+    margin-bottom: 0;
+
+    + .form-control,
+    + .custom-select,
+    + .custom-file {
+      margin-left: -$input-border-width;
+    }
+  }
+
+  // Bring the "active" form control to the top of surrounding elements
+  > .form-control:focus,
+  > .custom-select:focus,
+  > .custom-file .custom-file-input:focus ~ .custom-file-label {
+    z-index: 3;
+  }
+
+  // Bring the custom file input above the label
+  > .custom-file .custom-file-input:focus {
+    z-index: 4;
+  }
+
+  > .form-control,
+  > .custom-select {
+    &:not(:last-child) { @include border-right-radius(0); }
+    &:not(:first-child) { @include border-left-radius(0); }
+  }
+
+  // Custom file inputs have more complex markup, thus requiring different
+  // border-radius overrides.
+  > .custom-file {
+    display: flex;
+    align-items: center;
+
+    &:not(:last-child) .custom-file-label,
+    &:not(:last-child) .custom-file-label::after { @include border-right-radius(0); }
+    &:not(:first-child) .custom-file-label { @include border-left-radius(0); }
+  }
+}
+
+
+// Prepend and append
+//
+// While it requires one extra layer of HTML for each, dedicated prepend and
+// append elements allow us to 1) be less clever, 2) simplify our selectors, and
+// 3) support HTML5 form validation.
+
+.input-group-prepend,
+.input-group-append {
+  display: flex;
+
+  // Ensure buttons are always above inputs for more visually pleasing borders.
+  // This isn't needed for `.input-group-text` since it shares the same border-color
+  // as our inputs.
+  .btn {
+    position: relative;
+    z-index: 2;
+  }
+
+  .btn + .btn,
+  .btn + .input-group-text,
+  .input-group-text + .input-group-text,
+  .input-group-text + .btn {
+    margin-left: -$input-border-width;
+  }
+}
+
+.input-group-prepend { margin-right: -$input-border-width; }
+.input-group-append { margin-left: -$input-border-width; }
+
+
+// Textual addons
+//
+// Serves as a catch-all element for any text or radio/checkbox input you wish
+// to prepend or append to an input.
+
+.input-group-text {
+  display: flex;
+  align-items: center;
+  padding: $input-padding-y $input-padding-x;
+  margin-bottom: 0; // Allow use of <label> elements by overriding our default margin-bottom
+  font-size: $font-size-base; // Match inputs
+  font-weight: $font-weight-normal;
+  line-height: $input-line-height;
+  color: $input-group-addon-color;
+  text-align: center;
+  white-space: nowrap;
+  background-color: $input-group-addon-bg;
+  border: $input-border-width solid $input-group-addon-border-color;
+  @include border-radius($input-border-radius);
+
+  // Nuke default margins from checkboxes and radios to vertically center within.
+  input[type="radio"],
+  input[type="checkbox"] {
+    margin-top: 0;
+  }
+}
+
+
+// Sizing
+//
+// Remix the default form control sizing classes into new ones for easier
+// manipulation.
+
+.input-group-lg > .form-control,
+.input-group-lg > .input-group-prepend > .input-group-text,
+.input-group-lg > .input-group-append > .input-group-text,
+.input-group-lg > .input-group-prepend > .btn,
+.input-group-lg > .input-group-append > .btn {
+  height: $input-height-lg;
+  padding: $input-padding-y-lg $input-padding-x-lg;
+  font-size: $font-size-lg;
+  line-height: $input-line-height-lg;
+  @include border-radius($input-border-radius-lg);
+}
+
+.input-group-sm > .form-control,
+.input-group-sm > .input-group-prepend > .input-group-text,
+.input-group-sm > .input-group-append > .input-group-text,
+.input-group-sm > .input-group-prepend > .btn,
+.input-group-sm > .input-group-append > .btn {
+  height: $input-height-sm;
+  padding: $input-padding-y-sm $input-padding-x-sm;
+  font-size: $font-size-sm;
+  line-height: $input-line-height-sm;
+  @include border-radius($input-border-radius-sm);
+}
+
+
+// Prepend and append rounded corners
+//
+// These rulesets must come after the sizing ones to properly override sm and lg
+// border-radius values when extending. They're more specific than we'd like
+// with the `.input-group >` part, but without it, we cannot override the sizing.
+
+
+.input-group > .input-group-prepend > .btn,
+.input-group > .input-group-prepend > .input-group-text,
+.input-group > .input-group-append:not(:last-child) > .btn,
+.input-group > .input-group-append:not(:last-child) > .input-group-text,
+.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),
+.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
+  @include border-right-radius(0);
+}
+
+.input-group > .input-group-append > .btn,
+.input-group > .input-group-append > .input-group-text,
+.input-group > .input-group-prepend:not(:first-child) > .btn,
+.input-group > .input-group-prepend:not(:first-child) > .input-group-text,
+.input-group > .input-group-prepend:first-child > .btn:not(:first-child),
+.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {
+  @include border-left-radius(0);
+}
diff --git a/site/_sass/bootstrap/_jumbotron.scss b/site/_sass/bootstrap/_jumbotron.scss
new file mode 100644
index 0000000..7966bba
--- /dev/null
+++ b/site/_sass/bootstrap/_jumbotron.scss
@@ -0,0 +1,16 @@
+.jumbotron {
+  padding: $jumbotron-padding ($jumbotron-padding / 2);
+  margin-bottom: $jumbotron-padding;
+  background-color: $jumbotron-bg;
+  @include border-radius($border-radius-lg);
+
+  @include media-breakpoint-up(sm) {
+    padding: ($jumbotron-padding * 2) $jumbotron-padding;
+  }
+}
+
+.jumbotron-fluid {
+  padding-right: 0;
+  padding-left: 0;
+  @include border-radius(0);
+}
diff --git a/site/_sass/bootstrap/_list-group.scss b/site/_sass/bootstrap/_list-group.scss
new file mode 100644
index 0000000..9f145c1
--- /dev/null
+++ b/site/_sass/bootstrap/_list-group.scss
@@ -0,0 +1,115 @@
+// Base class
+//
+// Easily usable on <ul>, <ol>, or <div>.
+
+.list-group {
+  display: flex;
+  flex-direction: column;
+
+  // No need to set list-style: none; since .list-group-item is block level
+  padding-left: 0; // reset padding because ul and ol
+  margin-bottom: 0;
+}
+
+
+// Interactive list items
+//
+// Use anchor or button elements instead of `li`s or `div`s to create interactive
+// list items. Includes an extra `.active` modifier class for selected items.
+
+.list-group-item-action {
+  width: 100%; // For `<button>`s (anchors become 100% by default though)
+  color: $list-group-action-color;
+  text-align: inherit; // For `<button>`s (anchors inherit)
+
+  // Hover state
+  @include hover-focus {
+    color: $list-group-action-hover-color;
+    text-decoration: none;
+    background-color: $list-group-hover-bg;
+  }
+
+  &:active {
+    color: $list-group-action-active-color;
+    background-color: $list-group-action-active-bg;
+  }
+}
+
+
+// Individual list items
+//
+// Use on `li`s or `div`s within the `.list-group` parent.
+
+.list-group-item {
+  position: relative;
+  display: block;
+  padding: $list-group-item-padding-y $list-group-item-padding-x;
+  // Place the border on the list items and negative margin up for better styling
+  margin-bottom: -$list-group-border-width;
+  background-color: $list-group-bg;
+  border: $list-group-border-width solid $list-group-border-color;
+
+  &:first-child {
+    @include border-top-radius($list-group-border-radius);
+  }
+
+  &:last-child {
+    margin-bottom: 0;
+    @include border-bottom-radius($list-group-border-radius);
+  }
+
+  @include hover-focus {
+    z-index: 1; // Place hover/active items above their siblings for proper border styling
+    text-decoration: none;
+  }
+
+  &.disabled,
+  &:disabled {
+    color: $list-group-disabled-color;
+    background-color: $list-group-disabled-bg;
+  }
+
+  // Include both here for `<a>`s and `<button>`s
+  &.active {
+    z-index: 2; // Place active items above their siblings for proper border styling
+    color: $list-group-active-color;
+    background-color: $list-group-active-bg;
+    border-color: $list-group-active-border-color;
+  }
+}
+
+
+// Flush list items
+//
+// Remove borders and border-radius to keep list group items edge-to-edge. Most
+// useful within other components (e.g., cards).
+
+.list-group-flush {
+  .list-group-item {
+    border-right: 0;
+    border-left: 0;
+    @include border-radius(0);
+  }
+
+  &:first-child {
+    .list-group-item:first-child {
+      border-top: 0;
+    }
+  }
+
+  &:last-child {
+    .list-group-item:last-child {
+      border-bottom: 0;
+    }
+  }
+}
+
+
+// Contextual variants
+//
+// Add modifier classes to change text and background color on individual items.
+// Organizationally, this must come after the `:hover` states.
+
+@each $color, $value in $theme-colors {
+  @include list-group-item-variant($color, theme-color-level($color, -9), theme-color-level($color, 6));
+}
diff --git a/site/_sass/bootstrap/_media.scss b/site/_sass/bootstrap/_media.scss
new file mode 100644
index 0000000..b573052
--- /dev/null
+++ b/site/_sass/bootstrap/_media.scss
@@ -0,0 +1,8 @@
+.media {
+  display: flex;
+  align-items: flex-start;
+}
+
+.media-body {
+  flex: 1;
+}
diff --git a/site/_sass/bootstrap/_mixins.scss b/site/_sass/bootstrap/_mixins.scss
new file mode 100644
index 0000000..8710166
--- /dev/null
+++ b/site/_sass/bootstrap/_mixins.scss
@@ -0,0 +1,41 @@
+// Toggles
+//
+// Used in conjunction with global variables to enable certain theme features.
+
+// Utilities
+@import "mixins/breakpoints";
+@import "mixins/hover";
+@import "mixins/image";
+@import "mixins/badge";
+@import "mixins/resize";
+@import "mixins/screen-reader";
+@import "mixins/size";
+@import "mixins/reset-text";
+@import "mixins/text-emphasis";
+@import "mixins/text-hide";
+@import "mixins/text-truncate";
+@import "mixins/visibility";
+
+// // Components
+@import "mixins/alert";
+@import "mixins/buttons";
+@import "mixins/caret";
+@import "mixins/pagination";
+@import "mixins/lists";
+@import "mixins/list-group";
+@import "mixins/nav-divider";
+@import "mixins/forms";
+@import "mixins/table-row";
+
+// // Skins
+@import "mixins/background-variant";
+@import "mixins/border-radius";
+@import "mixins/box-shadow";
+@import "mixins/gradients";
+@import "mixins/transition";
+
+// // Layout
+@import "mixins/clearfix";
+@import "mixins/grid-framework";
+@import "mixins/grid";
+@import "mixins/float";
diff --git a/site/_sass/bootstrap/_modal.scss b/site/_sass/bootstrap/_modal.scss
new file mode 100644
index 0000000..f20bb9d
--- /dev/null
+++ b/site/_sass/bootstrap/_modal.scss
@@ -0,0 +1,180 @@
+// .modal-open      - body class for killing the scroll
+// .modal           - container to scroll within
+// .modal-dialog    - positioning shell for the actual modal
+// .modal-content   - actual modal w/ bg and corners and stuff
+
+
+.modal-open {
+  // Kill the scroll on the body
+  overflow: hidden;
+
+  .modal {
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+}
+
+// Container that the modal scrolls within
+.modal {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: $zindex-modal;
+  display: none;
+  overflow: hidden;
+  // Prevent Chrome on Windows from adding a focus outline. For details, see
+  // https://github.com/twbs/bootstrap/pull/10951.
+  outline: 0;
+  // We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
+  // gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
+  // See also https://github.com/twbs/bootstrap/issues/17695
+}
+
+// Shell div to position the modal with bottom padding
+.modal-dialog {
+  position: relative;
+  width: auto;
+  margin: $modal-dialog-margin;
+  // allow clicks to pass through for custom click handling to close modal
+  pointer-events: none;
+
+  // When fading in the modal, animate it to slide down
+  .modal.fade & {
+    @include transition($modal-transition);
+    transform: translate(0, -25%);
+  }
+  .modal.show & {
+    transform: translate(0, 0);
+  }
+}
+
+.modal-dialog-centered {
+  display: flex;
+  align-items: center;
+  min-height: calc(100% - (#{$modal-dialog-margin} * 2));
+
+  // Ensure `modal-dialog-centered` extends the full height of the view (IE10/11)
+  &::before {
+    display: block; // IE10
+    height: calc(100vh - (#{$modal-dialog-margin} * 2));
+    content: "";
+  }
+}
+
+// Actual modal
+.modal-content {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog`
+  // counteract the pointer-events: none; in the .modal-dialog
+  pointer-events: auto;
+  background-color: $modal-content-bg;
+  background-clip: padding-box;
+  border: $modal-content-border-width solid $modal-content-border-color;
+  @include border-radius($modal-content-border-radius);
+  @include box-shadow($modal-content-box-shadow-xs);
+  // Remove focus outline from opened modal
+  outline: 0;
+}
+
+// Modal background
+.modal-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: $zindex-modal-backdrop;
+  background-color: $modal-backdrop-bg;
+
+  // Fade for backdrop
+  &.fade { opacity: 0; }
+  &.show { opacity: $modal-backdrop-opacity; }
+}
+
+// Modal header
+// Top section of the modal w/ title and dismiss
+.modal-header {
+  display: flex;
+  align-items: flex-start; // so the close btn always stays on the upper right corner
+  justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends
+  padding: $modal-header-padding;
+  border-bottom: $modal-header-border-width solid $modal-header-border-color;
+  @include border-top-radius($modal-content-border-radius);
+
+  .close {
+    padding: $modal-header-padding;
+    // auto on the left force icon to the right even when there is no .modal-title
+    margin: (-$modal-header-padding) (-$modal-header-padding) (-$modal-header-padding) auto;
+  }
+}
+
+// Title text within header
+.modal-title {
+  margin-bottom: 0;
+  line-height: $modal-title-line-height;
+}
+
+// Modal body
+// Where all modal content resides (sibling of .modal-header and .modal-footer)
+.modal-body {
+  position: relative;
+  // Enable `flex-grow: 1` so that the body take up as much space as possible
+  // when should there be a fixed height on `.modal-dialog`.
+  flex: 1 1 auto;
+  padding: $modal-inner-padding;
+}
+
+// Footer (for actions)
+.modal-footer {
+  display: flex;
+  align-items: center; // vertically center
+  justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
+  padding: $modal-inner-padding;
+  border-top: $modal-footer-border-width solid $modal-footer-border-color;
+
+  // Easily place margin between footer elements
+  > :not(:first-child) { margin-left: .25rem; }
+  > :not(:last-child) { margin-right: .25rem; }
+}
+
+// Measure scrollbar width for padding body during modal show/hide
+.modal-scrollbar-measure {
+  position: absolute;
+  top: -9999px;
+  width: 50px;
+  height: 50px;
+  overflow: scroll;
+}
+
+// Scale up the modal
+@include media-breakpoint-up(sm) {
+  // Automatically set modal's width for larger viewports
+  .modal-dialog {
+    max-width: $modal-md;
+    margin: $modal-dialog-margin-y-sm-up auto;
+  }
+
+  .modal-dialog-centered {
+    min-height: calc(100% - (#{$modal-dialog-margin-y-sm-up} * 2));
+
+    &::before {
+      height: calc(100vh - (#{$modal-dialog-margin-y-sm-up} * 2));
+    }
+
+  }
+
+  .modal-content {
+    @include box-shadow($modal-content-box-shadow-sm-up);
+  }
+
+  .modal-sm { max-width: $modal-sm; }
+
+}
+
+@include media-breakpoint-up(lg) {
+  .modal-lg { max-width: $modal-lg; }
+}
diff --git a/site/_sass/bootstrap/_nav.scss b/site/_sass/bootstrap/_nav.scss
new file mode 100644
index 0000000..fc82161
--- /dev/null
+++ b/site/_sass/bootstrap/_nav.scss
@@ -0,0 +1,118 @@
+// Base class
+//
+// Kickstart any navigation component with a set of style resets. Works with
+// `<nav>`s or `<ul>`s.
+
+.nav {
+  display: flex;
+  flex-wrap: wrap;
+  padding-left: 0;
+  margin-bottom: 0;
+  list-style: none;
+}
+
+.nav-link {
+  display: block;
+  padding: $nav-link-padding-y $nav-link-padding-x;
+
+  @include hover-focus {
+    text-decoration: none;
+  }
+
+  // Disabled state lightens text
+  &.disabled {
+    color: $nav-link-disabled-color;
+  }
+}
+
+//
+// Tabs
+//
+
+.nav-tabs {
+  border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
+
+  .nav-item {
+    margin-bottom: -$nav-tabs-border-width;
+  }
+
+  .nav-link {
+    border: $nav-tabs-border-width solid transparent;
+    @include border-top-radius($nav-tabs-border-radius);
+
+    @include hover-focus {
+      border-color: $nav-tabs-link-hover-border-color;
+    }
+
+    &.disabled {
+      color: $nav-link-disabled-color;
+      background-color: transparent;
+      border-color: transparent;
+    }
+  }
+
+  .nav-link.active,
+  .nav-item.show .nav-link {
+    color: $nav-tabs-link-active-color;
+    background-color: $nav-tabs-link-active-bg;
+    border-color: $nav-tabs-link-active-border-color;
+  }
+
+  .dropdown-menu {
+    // Make dropdown border overlap tab border
+    margin-top: -$nav-tabs-border-width;
+    // Remove the top rounded corners here since there is a hard edge above the menu
+    @include border-top-radius(0);
+  }
+}
+
+
+//
+// Pills
+//
+
+.nav-pills {
+  .nav-link {
+    @include border-radius($nav-pills-border-radius);
+  }
+
+  .nav-link.active,
+  .show > .nav-link {
+    color: $nav-pills-link-active-color;
+    background-color: $nav-pills-link-active-bg;
+  }
+}
+
+
+//
+// Justified variants
+//
+
+.nav-fill {
+  .nav-item {
+    flex: 1 1 auto;
+    text-align: center;
+  }
+}
+
+.nav-justified {
+  .nav-item {
+    flex-basis: 0;
+    flex-grow: 1;
+    text-align: center;
+  }
+}
+
+
+// Tabbable tabs
+//
+// Hide tabbable panes to start, show them when `.active`
+
+.tab-content {
+  > .tab-pane {
+    display: none;
+  }
+  > .active {
+    display: block;
+  }
+}
diff --git a/site/_sass/bootstrap/_navbar.scss b/site/_sass/bootstrap/_navbar.scss
new file mode 100644
index 0000000..52de505
--- /dev/null
+++ b/site/_sass/bootstrap/_navbar.scss
@@ -0,0 +1,299 @@
+// Contents
+//
+// Navbar
+// Navbar brand
+// Navbar nav
+// Navbar text
+// Navbar divider
+// Responsive navbar
+// Navbar position
+// Navbar themes
+
+
+// Navbar
+//
+// Provide a static navbar from which we expand to create full-width, fixed, and
+// other navbar variations.
+
+.navbar {
+  position: relative;
+  display: flex;
+  flex-wrap: wrap; // allow us to do the line break for collapsing content
+  align-items: center;
+  justify-content: space-between; // space out brand from logo
+  padding: $navbar-padding-y $navbar-padding-x;
+
+  // Because flex properties aren't inherited, we need to redeclare these first
+  // few properties so that content nested within behave properly.
+  > .container,
+  > .container-fluid {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    justify-content: space-between;
+  }
+}
+
+
+// Navbar brand
+//
+// Used for brand, project, or site names.
+
+.navbar-brand {
+  display: inline-block;
+  padding-top: $navbar-brand-padding-y;
+  padding-bottom: $navbar-brand-padding-y;
+  margin-right: $navbar-padding-x;
+  font-size: $navbar-brand-font-size;
+  line-height: inherit;
+  white-space: nowrap;
+
+  @include hover-focus {
+    text-decoration: none;
+  }
+}
+
+
+// Navbar nav
+//
+// Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`).
+
+.navbar-nav {
+  display: flex;
+  flex-direction: column; // cannot use `inherit` to get the `.navbar`s value
+  padding-left: 0;
+  margin-bottom: 0;
+  list-style: none;
+
+  .nav-link {
+    padding-right: 0;
+    padding-left: 0;
+  }
+
+  .dropdown-menu {
+    position: static;
+    float: none;
+  }
+}
+
+
+// Navbar text
+//
+//
+
+.navbar-text {
+  display: inline-block;
+  padding-top: $nav-link-padding-y;
+  padding-bottom: $nav-link-padding-y;
+}
+
+
+// Responsive navbar
+//
+// Custom styles for responsive collapsing and toggling of navbar contents.
+// Powered by the collapse Bootstrap JavaScript plugin.
+
+// When collapsed, prevent the toggleable navbar contents from appearing in
+// the default flexbox row orientation. Requires the use of `flex-wrap: wrap`
+// on the `.navbar` parent.
+.navbar-collapse {
+  flex-basis: 100%;
+  flex-grow: 1;
+  // For always expanded or extra full navbars, ensure content aligns itself
+  // properly vertically. Can be easily overridden with flex utilities.
+  align-items: center;
+}
+
+// Button for toggling the navbar when in its collapsed state
+.navbar-toggler {
+  padding: $navbar-toggler-padding-y $navbar-toggler-padding-x;
+  font-size: $navbar-toggler-font-size;
+  line-height: 1;
+  background-color: transparent; // remove default button style
+  border: $border-width solid transparent; // remove default button style
+  @include border-radius($navbar-toggler-border-radius);
+
+  @include hover-focus {
+    text-decoration: none;
+  }
+
+  // Opinionated: add "hand" cursor to non-disabled .navbar-toggler elements
+  &:not(:disabled):not(.disabled) {
+    cursor: pointer;
+  }
+}
+
+// Keep as a separate element so folks can easily override it with another icon
+// or image file as needed.
+.navbar-toggler-icon {
+  display: inline-block;
+  width: 1.5em;
+  height: 1.5em;
+  vertical-align: middle;
+  content: "";
+  background: no-repeat center center;
+  background-size: 100% 100%;
+}
+
+// Generate series of `.navbar-expand-*` responsive classes for configuring
+// where your navbar collapses.
+.navbar-expand {
+  @each $breakpoint in map-keys($grid-breakpoints) {
+    $next: breakpoint-next($breakpoint, $grid-breakpoints);
+    $infix: breakpoint-infix($next, $grid-breakpoints);
+
+    &#{$infix} {
+      @include media-breakpoint-down($breakpoint) {
+        > .container,
+        > .container-fluid {
+          padding-right: 0;
+          padding-left: 0;
+        }
+      }
+
+      @include media-breakpoint-up($next) {
+        flex-flow: row nowrap;
+        justify-content: flex-start;
+
+        .navbar-nav {
+          flex-direction: row;
+
+          .dropdown-menu {
+            position: absolute;
+          }
+
+          .nav-link {
+            padding-right: $navbar-nav-link-padding-x;
+            padding-left: $navbar-nav-link-padding-x;
+          }
+        }
+
+        // For nesting containers, have to redeclare for alignment purposes
+        > .container,
+        > .container-fluid {
+          flex-wrap: nowrap;
+        }
+
+        .navbar-collapse {
+          display: flex !important;  // stylelint-disable-line declaration-no-important
+
+          // Changes flex-bases to auto because of an IE10 bug
+          flex-basis: auto;
+        }
+
+        .navbar-toggler {
+          display: none;
+        }
+      }
+    }
+  }
+}
+
+
+// Navbar themes
+//
+// Styles for switching between navbars with light or dark background.
+
+// Dark links against a light background
+.navbar-light {
+  .navbar-brand {
+    color: $navbar-light-active-color;
+
+    @include hover-focus {
+      color: $navbar-light-active-color;
+    }
+  }
+
+  .navbar-nav {
+    .nav-link {
+      color: $navbar-light-color;
+
+      @include hover-focus {
+        color: $navbar-light-hover-color;
+      }
+
+      &.disabled {
+        color: $navbar-light-disabled-color;
+      }
+    }
+
+    .show > .nav-link,
+    .active > .nav-link,
+    .nav-link.show,
+    .nav-link.active {
+      color: $navbar-light-active-color;
+    }
+  }
+
+  .navbar-toggler {
+    color: $navbar-light-color;
+    border-color: $navbar-light-toggler-border-color;
+  }
+
+  .navbar-toggler-icon {
+    background-image: $navbar-light-toggler-icon-bg;
+  }
+
+  .navbar-text {
+    color: $navbar-light-color;
+    a {
+      color: $navbar-light-active-color;
+
+      @include hover-focus {
+        color: $navbar-light-active-color;
+      }
+    }
+  }
+}
+
+// White links against a dark background
+.navbar-dark {
+  .navbar-brand {
+    color: $navbar-dark-active-color;
+
+    @include hover-focus {
+      color: $navbar-dark-active-color;
+    }
+  }
+
+  .navbar-nav {
+    .nav-link {
+      color: $navbar-dark-color;
+
+      @include hover-focus {
+        color: $navbar-dark-hover-color;
+      }
+
+      &.disabled {
+        color: $navbar-dark-disabled-color;
+      }
+    }
+
+    .show > .nav-link,
+    .active > .nav-link,
+    .nav-link.show,
+    .nav-link.active {
+      color: $navbar-dark-active-color;
+    }
+  }
+
+  .navbar-toggler {
+    color: $navbar-dark-color;
+    border-color: $navbar-dark-toggler-border-color;
+  }
+
+  .navbar-toggler-icon {
+    background-image: $navbar-dark-toggler-icon-bg;
+  }
+
+  .navbar-text {
+    color: $navbar-dark-color;
+    a {
+      color: $navbar-dark-active-color;
+
+      @include hover-focus {
+        color: $navbar-dark-active-color;
+      }
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/_pagination.scss b/site/_sass/bootstrap/_pagination.scss
new file mode 100644
index 0000000..9349f3f
--- /dev/null
+++ b/site/_sass/bootstrap/_pagination.scss
@@ -0,0 +1,78 @@
+.pagination {
+  display: flex;
+  @include list-unstyled();
+  @include border-radius();
+}
+
+.page-link {
+  position: relative;
+  display: block;
+  padding: $pagination-padding-y $pagination-padding-x;
+  margin-left: -$pagination-border-width;
+  line-height: $pagination-line-height;
+  color: $pagination-color;
+  background-color: $pagination-bg;
+  border: $pagination-border-width solid $pagination-border-color;
+
+  &:hover {
+    z-index: 2;
+    color: $pagination-hover-color;
+    text-decoration: none;
+    background-color: $pagination-hover-bg;
+    border-color: $pagination-hover-border-color;
+  }
+
+  &:focus {
+    z-index: 2;
+    outline: $pagination-focus-outline;
+    box-shadow: $pagination-focus-box-shadow;
+  }
+
+  // Opinionated: add "hand" cursor to non-disabled .page-link elements
+  &:not(:disabled):not(.disabled) {
+    cursor: pointer;
+  }
+}
+
+.page-item {
+  &:first-child {
+    .page-link {
+      margin-left: 0;
+      @include border-left-radius($border-radius);
+    }
+  }
+  &:last-child {
+    .page-link {
+      @include border-right-radius($border-radius);
+    }
+  }
+
+  &.active .page-link {
+    z-index: 1;
+    color: $pagination-active-color;
+    background-color: $pagination-active-bg;
+    border-color: $pagination-active-border-color;
+  }
+
+  &.disabled .page-link {
+    color: $pagination-disabled-color;
+    pointer-events: none;
+    // Opinionated: remove the "hand" cursor set previously for .page-link
+    cursor: auto;
+    background-color: $pagination-disabled-bg;
+    border-color: $pagination-disabled-border-color;
+  }
+}
+
+
+//
+// Sizing
+//
+
+.pagination-lg {
+  @include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $line-height-lg, $border-radius-lg);
+}
+
+.pagination-sm {
+  @include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $line-height-sm, $border-radius-sm);
+}
diff --git a/site/_sass/bootstrap/_popover.scss b/site/_sass/bootstrap/_popover.scss
new file mode 100644
index 0000000..3ef5f62
--- /dev/null
+++ b/site/_sass/bootstrap/_popover.scss
@@ -0,0 +1,183 @@
+.popover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: $zindex-popover;
+  display: block;
+  max-width: $popover-max-width;
+  // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
+  // So reset our font and text properties to avoid inheriting weird values.
+  @include reset-text();
+  font-size: $popover-font-size;
+  // Allow breaking very long words so they don't overflow the popover's bounds
+  word-wrap: break-word;
+  background-color: $popover-bg;
+  background-clip: padding-box;
+  border: $popover-border-width solid $popover-border-color;
+  @include border-radius($popover-border-radius);
+  @include box-shadow($popover-box-shadow);
+
+  .arrow {
+    position: absolute;
+    display: block;
+    width: $popover-arrow-width;
+    height: $popover-arrow-height;
+    margin: 0 $border-radius-lg;
+
+    &::before,
+    &::after {
+      position: absolute;
+      display: block;
+      content: "";
+      border-color: transparent;
+      border-style: solid;
+    }
+  }
+}
+
+.bs-popover-top {
+  margin-bottom: $popover-arrow-height;
+
+  .arrow {
+    bottom: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
+  }
+
+  .arrow::before,
+  .arrow::after {
+    border-width: $popover-arrow-height ($popover-arrow-width / 2) 0;
+  }
+
+  .arrow::before {
+    bottom: 0;
+    border-top-color: $popover-arrow-outer-color;
+  }
+
+  .arrow::after {
+    bottom: $popover-border-width;
+    border-top-color: $popover-arrow-color;
+  }
+}
+
+.bs-popover-right {
+  margin-left: $popover-arrow-height;
+
+  .arrow {
+    left: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
+    width: $popover-arrow-height;
+    height: $popover-arrow-width;
+    margin: $border-radius-lg 0; // make sure the arrow does not touch the popover's rounded corners
+  }
+
+  .arrow::before,
+  .arrow::after {
+    border-width: ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2) 0;
+  }
+
+  .arrow::before {
+    left: 0;
+    border-right-color: $popover-arrow-outer-color;
+  }
+
+  .arrow::after {
+    left: $popover-border-width;
+    border-right-color: $popover-arrow-color;
+  }
+}
+
+.bs-popover-bottom {
+  margin-top: $popover-arrow-height;
+
+  .arrow {
+    top: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
+  }
+
+  .arrow::before,
+  .arrow::after {
+    border-width: 0 ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2);
+  }
+
+  .arrow::before {
+    top: 0;
+    border-bottom-color: $popover-arrow-outer-color;
+  }
+
+  .arrow::after {
+    top: $popover-border-width;
+    border-bottom-color: $popover-arrow-color;
+  }
+
+  // This will remove the popover-header's border just below the arrow
+  .popover-header::before {
+    position: absolute;
+    top: 0;
+    left: 50%;
+    display: block;
+    width: $popover-arrow-width;
+    margin-left: ($popover-arrow-width / -2);
+    content: "";
+    border-bottom: $popover-border-width solid $popover-header-bg;
+  }
+}
+
+.bs-popover-left {
+  margin-right: $popover-arrow-height;
+
+  .arrow {
+    right: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
+    width: $popover-arrow-height;
+    height: $popover-arrow-width;
+    margin: $border-radius-lg 0; // make sure the arrow does not touch the popover's rounded corners
+  }
+
+  .arrow::before,
+  .arrow::after {
+    border-width: ($popover-arrow-width / 2) 0 ($popover-arrow-width / 2) $popover-arrow-height;
+  }
+
+  .arrow::before {
+    right: 0;
+    border-left-color: $popover-arrow-outer-color;
+  }
+
+  .arrow::after {
+    right: $popover-border-width;
+    border-left-color: $popover-arrow-color;
+  }
+}
+
+.bs-popover-auto {
+  &[x-placement^="top"] {
+    @extend .bs-popover-top;
+  }
+  &[x-placement^="right"] {
+    @extend .bs-popover-right;
+  }
+  &[x-placement^="bottom"] {
+    @extend .bs-popover-bottom;
+  }
+  &[x-placement^="left"] {
+    @extend .bs-popover-left;
+  }
+}
+
+
+// Offset the popover to account for the popover arrow
+.popover-header {
+  padding: $popover-header-padding-y $popover-header-padding-x;
+  margin-bottom: 0; // Reset the default from Reboot
+  font-size: $font-size-base;
+  color: $popover-header-color;
+  background-color: $popover-header-bg;
+  border-bottom: $popover-border-width solid darken($popover-header-bg, 5%);
+  $offset-border-width: calc(#{$border-radius-lg} - #{$popover-border-width});
+  @include border-top-radius($offset-border-width);
+
+  &:empty {
+    display: none;
+  }
+}
+
+.popover-body {
+  padding: $popover-body-padding-y $popover-body-padding-x;
+  color: $popover-body-color;
+}
diff --git a/site/_sass/bootstrap/_print.scss b/site/_sass/bootstrap/_print.scss
new file mode 100644
index 0000000..1df9487
--- /dev/null
+++ b/site/_sass/bootstrap/_print.scss
@@ -0,0 +1,141 @@
+// stylelint-disable declaration-no-important, selector-no-qualifying-type
+
+// Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css
+
+// ==========================================================================
+// Print styles.
+// Inlined to avoid the additional HTTP request:
+// https://www.phpied.com/delay-loading-your-print-css/
+// ==========================================================================
+
+@if $enable-print-styles {
+  @media print {
+    *,
+    *::before,
+    *::after {
+      // Bootstrap specific; comment out `color` and `background`
+      //color: $black !important; // Black prints faster
+      text-shadow: none !important;
+      //background: transparent !important;
+      box-shadow: none !important;
+    }
+
+    a {
+      &:not(.btn) {
+        text-decoration: underline;
+      }
+    }
+
+    // Bootstrap specific; comment the following selector out
+    //a[href]::after {
+    //  content: " (" attr(href) ")";
+    //}
+
+    abbr[title]::after {
+      content: " (" attr(title) ")";
+    }
+
+    // Bootstrap specific; comment the following selector out
+    //
+    // Don't show links that are fragment identifiers,
+    // or use the `javascript:` pseudo protocol
+    //
+
+    //a[href^="#"]::after,
+    //a[href^="javascript:"]::after {
+    // content: "";
+    //}
+
+    pre {
+      white-space: pre-wrap !important;
+    }
+    pre,
+    blockquote {
+      border: $border-width solid $gray-500;   // Bootstrap custom code; using `$border-width` instead of 1px
+      page-break-inside: avoid;
+    }
+
+    //
+    // Printing Tables:
+    // http://css-discuss.incutio.com/wiki/Printing_Tables
+    //
+
+    thead {
+      display: table-header-group;
+    }
+
+    tr,
+    img {
+      page-break-inside: avoid;
+    }
+
+    p,
+    h2,
+    h3 {
+      orphans: 3;
+      widows: 3;
+    }
+
+    h2,
+    h3 {
+      page-break-after: avoid;
+    }
+
+    // Bootstrap specific changes start
+
+    // Specify a size and min-width to make printing closer across browsers.
+    // We don't set margin here because it breaks `size` in Chrome. We also
+    // don't use `!important` on `size` as it breaks in Chrome.
+    @page {
+      size: $print-page-size;
+    }
+    body {
+      min-width: $print-body-min-width !important;
+    }
+    .container {
+      min-width: $print-body-min-width !important;
+    }
+
+    // Bootstrap components
+    .navbar {
+      display: none;
+    }
+    .badge {
+      border: $border-width solid $black;
+    }
+
+    .table {
+      border-collapse: collapse !important;
+
+      td,
+      th {
+        background-color: $white !important;
+      }
+    }
+
+    .table-bordered {
+      th,
+      td {
+        border: 1px solid $gray-300 !important;
+      }
+    }
+
+    .table-dark {
+      color: inherit;
+
+      th,
+      td,
+      thead th,
+      tbody + tbody {
+        border-color: $table-border-color;
+      }
+    }
+
+    .table .thead-dark th {
+      color: inherit;
+      border-color: $table-border-color;
+    }
+
+    // Bootstrap specific changes end
+  }
+}
diff --git a/site/_sass/bootstrap/_progress.scss b/site/_sass/bootstrap/_progress.scss
new file mode 100644
index 0000000..0ac3e0c
--- /dev/null
+++ b/site/_sass/bootstrap/_progress.scss
@@ -0,0 +1,34 @@
+@keyframes progress-bar-stripes {
+  from { background-position: $progress-height 0; }
+  to { background-position: 0 0; }
+}
+
+.progress {
+  display: flex;
+  height: $progress-height;
+  overflow: hidden; // force rounded corners by cropping it
+  font-size: $progress-font-size;
+  background-color: $progress-bg;
+  @include border-radius($progress-border-radius);
+  @include box-shadow($progress-box-shadow);
+}
+
+.progress-bar {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  color: $progress-bar-color;
+  text-align: center;
+  white-space: nowrap;
+  background-color: $progress-bar-bg;
+  @include transition($progress-bar-transition);
+}
+
+.progress-bar-striped {
+  @include gradient-striped();
+  background-size: $progress-height $progress-height;
+}
+
+.progress-bar-animated {
+  animation: progress-bar-stripes $progress-bar-animation-timing;
+}
diff --git a/site/_sass/bootstrap/_reboot.scss b/site/_sass/bootstrap/_reboot.scss
new file mode 100644
index 0000000..f297d09
--- /dev/null
+++ b/site/_sass/bootstrap/_reboot.scss
@@ -0,0 +1,483 @@
+// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
+
+// Reboot
+//
+// Normalization of HTML elements, manually forked from Normalize.css to remove
+// styles targeting irrelevant browsers while applying new styles.
+//
+// Normalize is licensed MIT. https://github.com/necolas/normalize.css
+
+
+// Document
+//
+// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
+// 2. Change the default font family in all browsers.
+// 3. Correct the line height in all browsers.
+// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.
+// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so
+//    we force a non-overlapping, non-auto-hiding scrollbar to counteract.
+// 6. Change the default tap highlight to be completely transparent in iOS.
+
+*,
+*::before,
+*::after {
+  box-sizing: border-box; // 1
+}
+
+html {
+  font-family: sans-serif; // 2
+  line-height: 1.15; // 3
+  -webkit-text-size-adjust: 100%; // 4
+  -ms-text-size-adjust: 100%; // 4
+  -ms-overflow-style: scrollbar; // 5
+  -webkit-tap-highlight-color: rgba($black, 0); // 6
+}
+
+// IE10+ doesn't honor `<meta name="viewport">` in some cases.
+@at-root {
+  @-ms-viewport {
+    width: device-width;
+  }
+}
+
+// stylelint-disable selector-list-comma-newline-after
+// Shim for "new" HTML5 structural elements to display correctly (IE10, older browsers)
+article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
+  display: block;
+}
+// stylelint-enable selector-list-comma-newline-after
+
+// Body
+//
+// 1. Remove the margin in all browsers.
+// 2. As a best practice, apply a default `background-color`.
+// 3. Set an explicit initial text-align value so that we can later use the
+//    the `inherit` value on things like `<th>` elements.
+
+body {
+  margin: 0; // 1
+  font-family: $font-family-base;
+  font-size: $font-size-base;
+  font-weight: $font-weight-base;
+  line-height: $line-height-base;
+  color: $body-color;
+  text-align: left; // 3
+  background-color: $body-bg; // 2
+}
+
+// Suppress the focus outline on elements that cannot be accessed via keyboard.
+// This prevents an unwanted focus outline from appearing around elements that
+// might still respond to pointer events.
+//
+// Credit: https://github.com/suitcss/base
+[tabindex="-1"]:focus {
+  outline: 0 !important;
+}
+
+
+// Content grouping
+//
+// 1. Add the correct box sizing in Firefox.
+// 2. Show the overflow in Edge and IE.
+
+hr {
+  box-sizing: content-box; // 1
+  height: 0; // 1
+  overflow: visible; // 2
+}
+
+
+//
+// Typography
+//
+
+// Remove top margins from headings
+//
+// By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top
+// margin for easier control within type scales as it avoids margin collapsing.
+// stylelint-disable selector-list-comma-newline-after
+h1, h2, h3, h4, h5, h6 {
+  margin-top: 0;
+  margin-bottom: $headings-margin-bottom;
+}
+// stylelint-enable selector-list-comma-newline-after
+
+// Reset margins on paragraphs
+//
+// Similarly, the top margin on `<p>`s get reset. However, we also reset the
+// bottom margin to use `rem` units instead of `em`.
+p {
+  margin-top: 0;
+  margin-bottom: $paragraph-margin-bottom;
+}
+
+// Abbreviations
+//
+// 1. Remove the bottom border in Firefox 39-.
+// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+// 3. Add explicit cursor to indicate changed behavior.
+// 4. Duplicate behavior to the data-* attribute for our tooltip plugin
+
+abbr[title],
+abbr[data-original-title] { // 4
+  text-decoration: underline; // 2
+  text-decoration: underline dotted; // 2
+  cursor: help; // 3
+  border-bottom: 0; // 1
+}
+
+address {
+  margin-bottom: 1rem;
+  font-style: normal;
+  line-height: inherit;
+}
+
+ol,
+ul,
+dl {
+  margin-top: 0;
+  margin-bottom: 1rem;
+}
+
+ol ol,
+ul ul,
+ol ul,
+ul ol {
+  margin-bottom: 0;
+}
+
+dt {
+  font-weight: $dt-font-weight;
+}
+
+dd {
+  margin-bottom: .5rem;
+  margin-left: 0; // Undo browser default
+}
+
+blockquote {
+  margin: 0 0 1rem;
+}
+
+dfn {
+  font-style: italic; // Add the correct font style in Android 4.3-
+}
+
+// stylelint-disable font-weight-notation
+b,
+strong {
+  font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari
+}
+// stylelint-enable font-weight-notation
+
+small {
+  font-size: 80%; // Add the correct font size in all browsers
+}
+
+//
+// Prevent `sub` and `sup` elements from affecting the line height in
+// all browsers.
+//
+
+sub,
+sup {
+  position: relative;
+  font-size: 75%;
+  line-height: 0;
+  vertical-align: baseline;
+}
+
+sub { bottom: -.25em; }
+sup { top: -.5em; }
+
+
+//
+// Links
+//
+
+a {
+  color: $link-color;
+  text-decoration: $link-decoration;
+  background-color: transparent; // Remove the gray background on active links in IE 10.
+  -webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.
+
+  @include hover {
+    color: $link-hover-color;
+    text-decoration: $link-hover-decoration;
+  }
+}
+
+// And undo these styles for placeholder links/named anchors (without href)
+// which have not been made explicitly keyboard-focusable (without tabindex).
+// It would be more straightforward to just use a[href] in previous block, but that
+// causes specificity issues in many other styles that are too complex to fix.
+// See https://github.com/twbs/bootstrap/issues/19402
+
+a:not([href]):not([tabindex]) {
+  color: inherit;
+  text-decoration: none;
+
+  @include hover-focus {
+    color: inherit;
+    text-decoration: none;
+  }
+
+  &:focus {
+    outline: 0;
+  }
+}
+
+
+//
+// Code
+//
+
+pre,
+code,
+kbd,
+samp {
+  font-family: $font-family-monospace;
+  font-size: 1em; // Correct the odd `em` font sizing in all browsers.
+}
+
+pre {
+  // Remove browser default top margin
+  margin-top: 0;
+  // Reset browser default of `1em` to use `rem`s
+  margin-bottom: 1rem;
+  // Don't allow content to break outside
+  overflow: auto;
+  // We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so
+  // we force a non-overlapping, non-auto-hiding scrollbar to counteract.
+  -ms-overflow-style: scrollbar;
+}
+
+
+//
+// Figures
+//
+
+figure {
+  // Apply a consistent margin strategy (matches our type styles).
+  margin: 0 0 1rem;
+}
+
+
+//
+// Images and content
+//
+
+img {
+  vertical-align: middle;
+  border-style: none; // Remove the border on images inside links in IE 10-.
+}
+
+svg {
+  // Workaround for the SVG overflow bug in IE10/11 is still required.
+  // See https://github.com/twbs/bootstrap/issues/26878
+  overflow: hidden;
+  vertical-align: middle;
+}
+
+
+//
+// Tables
+//
+
+table {
+  border-collapse: collapse; // Prevent double borders
+}
+
+caption {
+  padding-top: $table-cell-padding;
+  padding-bottom: $table-cell-padding;
+  color: $table-caption-color;
+  text-align: left;
+  caption-side: bottom;
+}
+
+th {
+  // Matches default `<td>` alignment by inheriting from the `<body>`, or the
+  // closest parent with a set `text-align`.
+  text-align: inherit;
+}
+
+
+//
+// Forms
+//
+
+label {
+  // Allow labels to use `margin` for spacing.
+  display: inline-block;
+  margin-bottom: $label-margin-bottom;
+}
+
+// Remove the default `border-radius` that macOS Chrome adds.
+//
+// Details at https://github.com/twbs/bootstrap/issues/24093
+button {
+  border-radius: 0;
+}
+
+// Work around a Firefox/IE bug where the transparent `button` background
+// results in a loss of the default `button` focus styles.
+//
+// Credit: https://github.com/suitcss/base/
+button:focus {
+  outline: 1px dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+}
+
+input,
+button,
+select,
+optgroup,
+textarea {
+  margin: 0; // Remove the margin in Firefox and Safari
+  font-family: inherit;
+  font-size: inherit;
+  line-height: inherit;
+}
+
+button,
+input {
+  overflow: visible; // Show the overflow in Edge
+}
+
+button,
+select {
+  text-transform: none; // Remove the inheritance of text transform in Firefox
+}
+
+// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
+//    controls in Android 4.
+// 2. Correct the inability to style clickable types in iOS and Safari.
+button,
+html [type="button"], // 1
+[type="reset"],
+[type="submit"] {
+  -webkit-appearance: button; // 2
+}
+
+// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+  padding: 0;
+  border-style: none;
+}
+
+input[type="radio"],
+input[type="checkbox"] {
+  box-sizing: border-box; // 1. Add the correct box sizing in IE 10-
+  padding: 0; // 2. Remove the padding in IE 10-
+}
+
+
+input[type="date"],
+input[type="time"],
+input[type="datetime-local"],
+input[type="month"] {
+  // Remove the default appearance of temporal inputs to avoid a Mobile Safari
+  // bug where setting a custom line-height prevents text from being vertically
+  // centered within the input.
+  // See https://bugs.webkit.org/show_bug.cgi?id=139848
+  // and https://github.com/twbs/bootstrap/issues/11266
+  -webkit-appearance: listbox;
+}
+
+textarea {
+  overflow: auto; // Remove the default vertical scrollbar in IE.
+  // Textareas should really only resize vertically so they don't break their (horizontal) containers.
+  resize: vertical;
+}
+
+fieldset {
+  // Browsers set a default `min-width: min-content;` on fieldsets,
+  // unlike e.g. `<div>`s, which have `min-width: 0;` by default.
+  // So we reset that to ensure fieldsets behave more like a standard block element.
+  // See https://github.com/twbs/bootstrap/issues/12359
+  // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements
+  min-width: 0;
+  // Reset the default outline behavior of fieldsets so they don't affect page layout.
+  padding: 0;
+  margin: 0;
+  border: 0;
+}
+
+// 1. Correct the text wrapping in Edge and IE.
+// 2. Correct the color inheritance from `fieldset` elements in IE.
+legend {
+  display: block;
+  width: 100%;
+  max-width: 100%; // 1
+  padding: 0;
+  margin-bottom: .5rem;
+  font-size: 1.5rem;
+  line-height: inherit;
+  color: inherit; // 2
+  white-space: normal; // 1
+}
+
+progress {
+  vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.
+}
+
+// Correct the cursor style of increment and decrement buttons in Chrome.
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+
+[type="search"] {
+  // This overrides the extra rounded corners on search inputs in iOS so that our
+  // `.form-control` class can properly style them. Note that this cannot simply
+  // be added to `.form-control` as it's not specific enough. For details, see
+  // https://github.com/twbs/bootstrap/issues/11586.
+  outline-offset: -2px; // 2. Correct the outline style in Safari.
+  -webkit-appearance: none;
+}
+
+//
+// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
+//
+
+[type="search"]::-webkit-search-cancel-button,
+[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+
+//
+// 1. Correct the inability to style clickable types in iOS and Safari.
+// 2. Change font properties to `inherit` in Safari.
+//
+
+::-webkit-file-upload-button {
+  font: inherit; // 2
+  -webkit-appearance: button; // 1
+}
+
+//
+// Correct element displays
+//
+
+output {
+  display: inline-block;
+}
+
+summary {
+  display: list-item; // Add the correct display in all browsers
+  cursor: pointer;
+}
+
+template {
+  display: none; // Add the correct display in IE
+}
+
+// Always hide an element with the `hidden` HTML attribute (from PureCSS).
+// Needed for proper display in IE 10-.
+[hidden] {
+  display: none !important;
+}
diff --git a/site/_sass/bootstrap/_root.scss b/site/_sass/bootstrap/_root.scss
new file mode 100644
index 0000000..ad550df
--- /dev/null
+++ b/site/_sass/bootstrap/_root.scss
@@ -0,0 +1,19 @@
+:root {
+  // Custom variable values only support SassScript inside `#{}`.
+  @each $color, $value in $colors {
+    --#{$color}: #{$value};
+  }
+
+  @each $color, $value in $theme-colors {
+    --#{$color}: #{$value};
+  }
+
+  @each $bp, $value in $grid-breakpoints {
+    --breakpoint-#{$bp}: #{$value};
+  }
+
+  // Use `inspect` for lists so that quoted items keep the quotes.
+  // See https://github.com/sass/sass/issues/2383#issuecomment-336349172
+  --font-family-sans-serif: #{inspect($font-family-sans-serif)};
+  --font-family-monospace: #{inspect($font-family-monospace)};
+}
diff --git a/site/_sass/bootstrap/_tables.scss b/site/_sass/bootstrap/_tables.scss
new file mode 100644
index 0000000..5fa6a86
--- /dev/null
+++ b/site/_sass/bootstrap/_tables.scss
@@ -0,0 +1,187 @@
+//
+// Basic Bootstrap table
+//
+
+.table {
+  width: 100%;
+  margin-bottom: $spacer;
+  background-color: $table-bg; // Reset for nesting within parents with `background-color`.
+
+  th,
+  td {
+    padding: $table-cell-padding;
+    vertical-align: top;
+    border-top: $table-border-width solid $table-border-color;
+  }
+
+  thead th {
+    vertical-align: bottom;
+    border-bottom: (2 * $table-border-width) solid $table-border-color;
+  }
+
+  tbody + tbody {
+    border-top: (2 * $table-border-width) solid $table-border-color;
+  }
+
+  .table {
+    background-color: $body-bg;
+  }
+}
+
+
+//
+// Condensed table w/ half padding
+//
+
+.table-sm {
+  th,
+  td {
+    padding: $table-cell-padding-sm;
+  }
+}
+
+
+// Border versions
+//
+// Add or remove borders all around the table and between all the columns.
+
+.table-bordered {
+  border: $table-border-width solid $table-border-color;
+
+  th,
+  td {
+    border: $table-border-width solid $table-border-color;
+  }
+
+  thead {
+    th,
+    td {
+      border-bottom-width: (2 * $table-border-width);
+    }
+  }
+}
+
+.table-borderless {
+  th,
+  td,
+  thead th,
+  tbody + tbody {
+    border: 0;
+  }
+}
+
+// Zebra-striping
+//
+// Default zebra-stripe styles (alternating gray and transparent backgrounds)
+
+.table-striped {
+  tbody tr:nth-of-type(#{$table-striped-order}) {
+    background-color: $table-accent-bg;
+  }
+}
+
+
+// Hover effect
+//
+// Placed here since it has to come after the potential zebra striping
+
+.table-hover {
+  tbody tr {
+    @include hover {
+      background-color: $table-hover-bg;
+    }
+  }
+}
+
+
+// Table backgrounds
+//
+// Exact selectors below required to override `.table-striped` and prevent
+// inheritance to nested tables.
+
+@each $color, $value in $theme-colors {
+  @include table-row-variant($color, theme-color-level($color, -9));
+}
+
+@include table-row-variant(active, $table-active-bg);
+
+
+// Dark styles
+//
+// Same table markup, but inverted color scheme: dark background and light text.
+
+// stylelint-disable-next-line no-duplicate-selectors
+.table {
+  .thead-dark {
+    th {
+      color: $table-dark-color;
+      background-color: $table-dark-bg;
+      border-color: $table-dark-border-color;
+    }
+  }
+
+  .thead-light {
+    th {
+      color: $table-head-color;
+      background-color: $table-head-bg;
+      border-color: $table-border-color;
+    }
+  }
+}
+
+.table-dark {
+  color: $table-dark-color;
+  background-color: $table-dark-bg;
+
+  th,
+  td,
+  thead th {
+    border-color: $table-dark-border-color;
+  }
+
+  &.table-bordered {
+    border: 0;
+  }
+
+  &.table-striped {
+    tbody tr:nth-of-type(odd) {
+      background-color: $table-dark-accent-bg;
+    }
+  }
+
+  &.table-hover {
+    tbody tr {
+      @include hover {
+        background-color: $table-dark-hover-bg;
+      }
+    }
+  }
+}
+
+
+// Responsive tables
+//
+// Generate series of `.table-responsive-*` classes for configuring the screen
+// size of where your table will overflow.
+
+.table-responsive {
+  @each $breakpoint in map-keys($grid-breakpoints) {
+    $next: breakpoint-next($breakpoint, $grid-breakpoints);
+    $infix: breakpoint-infix($next, $grid-breakpoints);
+
+    &#{$infix} {
+      @include media-breakpoint-down($breakpoint) {
+        display: block;
+        width: 100%;
+        overflow-x: auto;
+        -webkit-overflow-scrolling: touch;
+        -ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057
+
+        // Prevent double border on horizontal scroll due to use of `display: block;`
+        > .table-bordered {
+          border: 0;
+        }
+      }
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/_tooltip.scss b/site/_sass/bootstrap/_tooltip.scss
new file mode 100644
index 0000000..1286ebf
--- /dev/null
+++ b/site/_sass/bootstrap/_tooltip.scss
@@ -0,0 +1,115 @@
+// Base class
+.tooltip {
+  position: absolute;
+  z-index: $zindex-tooltip;
+  display: block;
+  margin: $tooltip-margin;
+  // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
+  // So reset our font and text properties to avoid inheriting weird values.
+  @include reset-text();
+  font-size: $tooltip-font-size;
+  // Allow breaking very long words so they don't overflow the tooltip's bounds
+  word-wrap: break-word;
+  opacity: 0;
+
+  &.show { opacity: $tooltip-opacity; }
+
+  .arrow {
+    position: absolute;
+    display: block;
+    width: $tooltip-arrow-width;
+    height: $tooltip-arrow-height;
+
+    &::before {
+      position: absolute;
+      content: "";
+      border-color: transparent;
+      border-style: solid;
+    }
+  }
+}
+
+.bs-tooltip-top {
+  padding: $tooltip-arrow-height 0;
+
+  .arrow {
+    bottom: 0;
+
+    &::before {
+      top: 0;
+      border-width: $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
+      border-top-color: $tooltip-arrow-color;
+    }
+  }
+}
+
+.bs-tooltip-right {
+  padding: 0 $tooltip-arrow-height;
+
+  .arrow {
+    left: 0;
+    width: $tooltip-arrow-height;
+    height: $tooltip-arrow-width;
+
+    &::before {
+      right: 0;
+      border-width: ($tooltip-arrow-width / 2) $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
+      border-right-color: $tooltip-arrow-color;
+    }
+  }
+}
+
+.bs-tooltip-bottom {
+  padding: $tooltip-arrow-height 0;
+
+  .arrow {
+    top: 0;
+
+    &::before {
+      bottom: 0;
+      border-width: 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
+      border-bottom-color: $tooltip-arrow-color;
+    }
+  }
+}
+
+.bs-tooltip-left {
+  padding: 0 $tooltip-arrow-height;
+
+  .arrow {
+    right: 0;
+    width: $tooltip-arrow-height;
+    height: $tooltip-arrow-width;
+
+    &::before {
+      left: 0;
+      border-width: ($tooltip-arrow-width / 2) 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
+      border-left-color: $tooltip-arrow-color;
+    }
+  }
+}
+
+.bs-tooltip-auto {
+  &[x-placement^="top"] {
+    @extend .bs-tooltip-top;
+  }
+  &[x-placement^="right"] {
+    @extend .bs-tooltip-right;
+  }
+  &[x-placement^="bottom"] {
+    @extend .bs-tooltip-bottom;
+  }
+  &[x-placement^="left"] {
+    @extend .bs-tooltip-left;
+  }
+}
+
+// Wrapper for the tooltip content
+.tooltip-inner {
+  max-width: $tooltip-max-width;
+  padding: $tooltip-padding-y $tooltip-padding-x;
+  color: $tooltip-color;
+  text-align: center;
+  background-color: $tooltip-bg;
+  @include border-radius($tooltip-border-radius);
+}
diff --git a/site/_sass/bootstrap/_transitions.scss b/site/_sass/bootstrap/_transitions.scss
new file mode 100644
index 0000000..c8d91e2
--- /dev/null
+++ b/site/_sass/bootstrap/_transitions.scss
@@ -0,0 +1,22 @@
+// stylelint-disable selector-no-qualifying-type
+
+.fade {
+  @include transition($transition-fade);
+
+  &:not(.show) {
+    opacity: 0;
+  }
+}
+
+.collapse {
+  &:not(.show) {
+    display: none;
+  }
+}
+
+.collapsing {
+  position: relative;
+  height: 0;
+  overflow: hidden;
+  @include transition($transition-collapse);
+}
diff --git a/site/_sass/bootstrap/_type.scss b/site/_sass/bootstrap/_type.scss
new file mode 100644
index 0000000..57d610f
--- /dev/null
+++ b/site/_sass/bootstrap/_type.scss
@@ -0,0 +1,125 @@
+// stylelint-disable declaration-no-important, selector-list-comma-newline-after
+
+//
+// Headings
+//
+
+h1, h2, h3, h4, h5, h6,
+.h1, .h2, .h3, .h4, .h5, .h6 {
+  margin-bottom: $headings-margin-bottom;
+  font-family: $headings-font-family;
+  font-weight: $headings-font-weight;
+  line-height: $headings-line-height;
+  color: $headings-color;
+}
+
+h1, .h1 { font-size: $h1-font-size; }
+h2, .h2 { font-size: $h2-font-size; }
+h3, .h3 { font-size: $h3-font-size; }
+h4, .h4 { font-size: $h4-font-size; }
+h5, .h5 { font-size: $h5-font-size; }
+h6, .h6 { font-size: $h6-font-size; }
+
+.lead {
+  font-size: $lead-font-size;
+  font-weight: $lead-font-weight;
+}
+
+// Type display classes
+.display-1 {
+  font-size: $display1-size;
+  font-weight: $display1-weight;
+  line-height: $display-line-height;
+}
+.display-2 {
+  font-size: $display2-size;
+  font-weight: $display2-weight;
+  line-height: $display-line-height;
+}
+.display-3 {
+  font-size: $display3-size;
+  font-weight: $display3-weight;
+  line-height: $display-line-height;
+}
+.display-4 {
+  font-size: $display4-size;
+  font-weight: $display4-weight;
+  line-height: $display-line-height;
+}
+
+
+//
+// Horizontal rules
+//
+
+hr {
+  margin-top: $hr-margin-y;
+  margin-bottom: $hr-margin-y;
+  border: 0;
+  border-top: $hr-border-width solid $hr-border-color;
+}
+
+
+//
+// Emphasis
+//
+
+small,
+.small {
+  font-size: $small-font-size;
+  font-weight: $font-weight-normal;
+}
+
+mark,
+.mark {
+  padding: $mark-padding;
+  background-color: $mark-bg;
+}
+
+
+//
+// Lists
+//
+
+.list-unstyled {
+  @include list-unstyled;
+}
+
+// Inline turns list items into inline-block
+.list-inline {
+  @include list-unstyled;
+}
+.list-inline-item {
+  display: inline-block;
+
+  &:not(:last-child) {
+    margin-right: $list-inline-padding;
+  }
+}
+
+
+//
+// Misc
+//
+
+// Builds on `abbr`
+.initialism {
+  font-size: 90%;
+  text-transform: uppercase;
+}
+
+// Blockquotes
+.blockquote {
+  margin-bottom: $spacer;
+  font-size: $blockquote-font-size;
+}
+
+.blockquote-footer {
+  display: block;
+  font-size: 80%; // back to default font-size
+  color: $blockquote-small-color;
+
+  &::before {
+    content: "\2014 \00A0"; // em dash, nbsp
+  }
+}
diff --git a/site/_sass/bootstrap/_utilities.scss b/site/_sass/bootstrap/_utilities.scss
new file mode 100644
index 0000000..6c7a7cd
--- /dev/null
+++ b/site/_sass/bootstrap/_utilities.scss
@@ -0,0 +1,15 @@
+@import "utilities/align";
+@import "utilities/background";
+@import "utilities/borders";
+@import "utilities/clearfix";
+@import "utilities/display";
+@import "utilities/embed";
+@import "utilities/flex";
+@import "utilities/float";
+@import "utilities/position";
+@import "utilities/screenreaders";
+@import "utilities/shadows";
+@import "utilities/sizing";
+@import "utilities/spacing";
+@import "utilities/text";
+@import "utilities/visibility";
diff --git a/site/_sass/bootstrap/_variables.scss b/site/_sass/bootstrap/_variables.scss
new file mode 100644
index 0000000..5cf118f
--- /dev/null
+++ b/site/_sass/bootstrap/_variables.scss
@@ -0,0 +1,952 @@
+// Variables
+//
+// Variables should follow the `$component-state-property-size` formula for
+// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.
+
+
+//
+// Color system
+//
+
+$white:    #fff !default;
+$gray-100: #f8f9fa !default;
+$gray-200: #e9ecef !default;
+$gray-300: #dee2e6 !default;
+$gray-400: #ced4da !default;
+$gray-500: #adb5bd !default;
+$gray-600: #6c757d !default;
+$gray-700: #495057 !default;
+$gray-800: #343a40 !default;
+$gray-900: #212529 !default;
+$black:    #000 !default;
+
+$grays: () !default;
+// stylelint-disable-next-line scss/dollar-variable-default
+$grays: map-merge(
+  (
+    "100": $gray-100,
+    "200": $gray-200,
+    "300": $gray-300,
+    "400": $gray-400,
+    "500": $gray-500,
+    "600": $gray-600,
+    "700": $gray-700,
+    "800": $gray-800,
+    "900": $gray-900
+  ),
+  $grays
+);
+
+
+$blue:    #007bff !default;
+$indigo:  #6610f2 !default;
+$purple:  #6f42c1 !default;
+$pink:    #e83e8c !default;
+$red:     #dc3545 !default;
+$orange:  #fd7e14 !default;
+$yellow:  #ffc107 !default;
+$green:   #28a745 !default;
+$teal:    #20c997 !default;
+$cyan:    #17a2b8 !default;
+
+$colors: () !default;
+// stylelint-disable-next-line scss/dollar-variable-default
+$colors: map-merge(
+  (
+    "blue":       $blue,
+    "indigo":     $indigo,
+    "purple":     $purple,
+    "pink":       $pink,
+    "red":        $red,
+    "orange":     $orange,
+    "yellow":     $yellow,
+    "green":      $green,
+    "teal":       $teal,
+    "cyan":       $cyan,
+    "white":      $white,
+    "gray":       $gray-600,
+    "gray-dark":  $gray-800
+  ),
+  $colors
+);
+
+$primary:       $blue !default;
+$secondary:     $gray-600 !default;
+$success:       $green !default;
+$info:          $cyan !default;
+$warning:       $yellow !default;
+$danger:        $red !default;
+$light:         $gray-100 !default;
+$dark:          $gray-800 !default;
+
+$theme-colors: () !default;
+// stylelint-disable-next-line scss/dollar-variable-default
+$theme-colors: map-merge(
+  (
+    "primary":    $primary,
+    "secondary":  $secondary,
+    "success":    $success,
+    "info":       $info,
+    "warning":    $warning,
+    "danger":     $danger,
+    "light":      $light,
+    "dark":       $dark
+  ),
+  $theme-colors
+);
+
+// Set a specific jump point for requesting color jumps
+$theme-color-interval:      8% !default;
+
+// The yiq lightness value that determines when the lightness of color changes from "dark" to "light". Acceptable values are between 0 and 255.
+$yiq-contrasted-threshold:  150 !default;
+
+// Customize the light and dark text colors for use in our YIQ color contrast function.
+$yiq-text-dark:             $gray-900 !default;
+$yiq-text-light:            $white !default;
+
+// Options
+//
+// Quickly modify global styling by enabling or disabling optional features.
+
+$enable-caret:              true !default;
+$enable-rounded:            true !default;
+$enable-shadows:            false !default;
+$enable-gradients:          false !default;
+$enable-transitions:        true !default;
+$enable-hover-media-query:  false !default; // Deprecated, no longer affects any compiled CSS
+$enable-grid-classes:       true !default;
+$enable-print-styles:       true !default;
+
+
+// Spacing
+//
+// Control the default styling of most Bootstrap elements by modifying these
+// variables. Mostly focused on spacing.
+// You can add more entries to the $spacers map, should you need more variation.
+
+$spacer: 1rem !default;
+$spacers: () !default;
+// stylelint-disable-next-line scss/dollar-variable-default
+$spacers: map-merge(
+  (
+    0: 0,
+    1: ($spacer * .25),
+    2: ($spacer * .5),
+    3: $spacer,
+    4: ($spacer * 1.5),
+    5: ($spacer * 3)
+  ),
+  $spacers
+);
+
+// This variable affects the `.h-*` and `.w-*` classes.
+$sizes: () !default;
+// stylelint-disable-next-line scss/dollar-variable-default
+$sizes: map-merge(
+  (
+    25: 25%,
+    50: 50%,
+    75: 75%,
+    100: 100%,
+    auto: auto
+  ),
+  $sizes
+);
+
+// Body
+//
+// Settings for the `<body>` element.
+
+$body-bg:                   $white !default;
+$body-color:                $gray-900 !default;
+
+// Links
+//
+// Style anchor elements.
+
+$link-color:                theme-color("primary") !default;
+$link-decoration:           none !default;
+$link-hover-color:          darken($link-color, 15%) !default;
+$link-hover-decoration:     underline !default;
+
+// Paragraphs
+//
+// Style p element.
+
+$paragraph-margin-bottom:   1rem !default;
+
+
+// Grid breakpoints
+//
+// Define the minimum dimensions at which your layout will change,
+// adapting to different screen sizes, for use in media queries.
+
+$grid-breakpoints: (
+  xs: 0,
+  sm: 576px,
+  md: 768px,
+  lg: 992px,
+  xl: 1200px
+) !default;
+
+@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
+@include _assert-starts-at-zero($grid-breakpoints);
+
+
+// Grid containers
+//
+// Define the maximum width of `.container` for different screen sizes.
+
+$container-max-widths: (
+  sm: 540px,
+  md: 720px,
+  lg: 960px,
+  xl: 1140px
+) !default;
+
+@include _assert-ascending($container-max-widths, "$container-max-widths");
+
+
+// Grid columns
+//
+// Set the number of columns and specify the width of the gutters.
+
+$grid-columns:                12 !default;
+$grid-gutter-width:           30px !default;
+
+// Components
+//
+// Define common padding and border radius sizes and more.
+
+$line-height-lg:              1.5 !default;
+$line-height-sm:              1.5 !default;
+
+$border-width:                1px !default;
+$border-color:                $gray-300 !default;
+
+$border-radius:               .25rem !default;
+$border-radius-lg:            .3rem !default;
+$border-radius-sm:            .2rem !default;
+
+$box-shadow-sm:               0 .125rem .25rem rgba($black, .075) !default;
+$box-shadow:                  0 .5rem 1rem rgba($black, .15) !default;
+$box-shadow-lg:               0 1rem 3rem rgba($black, .175) !default;
+
+$component-active-color:      $white !default;
+$component-active-bg:         theme-color("primary") !default;
+
+$caret-width:                 .3em !default;
+
+$transition-base:             all .2s ease-in-out !default;
+$transition-fade:             opacity .15s linear !default;
+$transition-collapse:         height .35s ease !default;
+
+
+// Fonts
+//
+// Font, line-height, and color for body text, headings, and more.
+
+// stylelint-disable value-keyword-case
+$font-family-sans-serif:      -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default;
+$font-family-monospace:       SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
+$font-family-base:            $font-family-sans-serif !default;
+// stylelint-enable value-keyword-case
+
+$font-size-base:              1rem !default; // Assumes the browser default, typically `16px`
+$font-size-lg:                ($font-size-base * 1.25) !default;
+$font-size-sm:                ($font-size-base * .875) !default;
+
+$font-weight-light:           300 !default;
+$font-weight-normal:          400 !default;
+$font-weight-bold:            700 !default;
+
+$font-weight-base:            $font-weight-normal !default;
+$line-height-base:            1.5 !default;
+
+$h1-font-size:                $font-size-base * 2.5 !default;
+$h2-font-size:                $font-size-base * 2 !default;
+$h3-font-size:                $font-size-base * 1.75 !default;
+$h4-font-size:                $font-size-base * 1.5 !default;
+$h5-font-size:                $font-size-base * 1.25 !default;
+$h6-font-size:                $font-size-base !default;
+
+$headings-margin-bottom:      ($spacer / 2) !default;
+$headings-font-family:        inherit !default;
+$headings-font-weight:        500 !default;
+$headings-line-height:        1.2 !default;
+$headings-color:              inherit !default;
+
+$display1-size:               6rem !default;
+$display2-size:               5.5rem !default;
+$display3-size:               4.5rem !default;
+$display4-size:               3.5rem !default;
+
+$display1-weight:             300 !default;
+$display2-weight:             300 !default;
+$display3-weight:             300 !default;
+$display4-weight:             300 !default;
+$display-line-height:         $headings-line-height !default;
+
+$lead-font-size:              ($font-size-base * 1.25) !default;
+$lead-font-weight:            300 !default;
+
+$small-font-size:             80% !default;
+
+$text-muted:                  $gray-600 !default;
+
+$blockquote-small-color:      $gray-600 !default;
+$blockquote-font-size:        ($font-size-base * 1.25) !default;
+
+$hr-border-color:             rgba($black, .1) !default;
+$hr-border-width:             $border-width !default;
+
+$mark-padding:                .2em !default;
+
+$dt-font-weight:              $font-weight-bold !default;
+
+$kbd-box-shadow:              inset 0 -.1rem 0 rgba($black, .25) !default;
+$nested-kbd-font-weight:      $font-weight-bold !default;
+
+$list-inline-padding:         .5rem !default;
+
+$mark-bg:                     #fcf8e3 !default;
+
+$hr-margin-y:                 $spacer !default;
+
+
+// Tables
+//
+// Customizes the `.table` component with basic values, each used across all table variations.
+
+$table-cell-padding:          .75rem !default;
+$table-cell-padding-sm:       .3rem !default;
+
+$table-bg:                    transparent !default;
+$table-accent-bg:             rgba($black, .05) !default;
+$table-hover-bg:              rgba($black, .075) !default;
+$table-active-bg:             $table-hover-bg !default;
+
+$table-border-width:          $border-width !default;
+$table-border-color:          $gray-300 !default;
+
+$table-head-bg:               $gray-200 !default;
+$table-head-color:            $gray-700 !default;
+
+$table-dark-bg:               $gray-900 !default;
+$table-dark-accent-bg:        rgba($white, .05) !default;
+$table-dark-hover-bg:         rgba($white, .075) !default;
+$table-dark-border-color:     lighten($gray-900, 7.5%) !default;
+$table-dark-color:            $body-bg !default;
+
+$table-striped-order:         odd !default;
+
+$table-caption-color:         $text-muted !default;
+
+// Buttons + Forms
+//
+// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.
+
+$input-btn-padding-y:         .375rem !default;
+$input-btn-padding-x:         .75rem !default;
+$input-btn-line-height:       $line-height-base !default;
+
+$input-btn-focus-width:       .2rem !default;
+$input-btn-focus-color:       rgba($component-active-bg, .25) !default;
+$input-btn-focus-box-shadow:  0 0 0 $input-btn-focus-width $input-btn-focus-color !default;
+
+$input-btn-padding-y-sm:      .25rem !default;
+$input-btn-padding-x-sm:      .5rem !default;
+$input-btn-line-height-sm:    $line-height-sm !default;
+
+$input-btn-padding-y-lg:      .5rem !default;
+$input-btn-padding-x-lg:      1rem !default;
+$input-btn-line-height-lg:    $line-height-lg !default;
+
+$input-btn-border-width:      $border-width !default;
+
+
+// Buttons
+//
+// For each of Bootstrap's buttons, define text, background, and border color.
+
+$btn-padding-y:               $input-btn-padding-y !default;
+$btn-padding-x:               $input-btn-padding-x !default;
+$btn-line-height:             $input-btn-line-height !default;
+
+$btn-padding-y-sm:            $input-btn-padding-y-sm !default;
+$btn-padding-x-sm:            $input-btn-padding-x-sm !default;
+$btn-line-height-sm:          $input-btn-line-height-sm !default;
+
+$btn-padding-y-lg:            $input-btn-padding-y-lg !default;
+$btn-padding-x-lg:            $input-btn-padding-x-lg !default;
+$btn-line-height-lg:          $input-btn-line-height-lg !default;
+
+$btn-border-width:            $input-btn-border-width !default;
+
+$btn-font-weight:             $font-weight-normal !default;
+$btn-box-shadow:              inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;
+$btn-focus-width:             $input-btn-focus-width !default;
+$btn-focus-box-shadow:        $input-btn-focus-box-shadow !default;
+$btn-disabled-opacity:        .65 !default;
+$btn-active-box-shadow:       inset 0 3px 5px rgba($black, .125) !default;
+
+$btn-link-disabled-color:     $gray-600 !default;
+
+$btn-block-spacing-y:         .5rem !default;
+
+// Allows for customizing button radius independently from global border radius
+$btn-border-radius:           $border-radius !default;
+$btn-border-radius-lg:        $border-radius-lg !default;
+$btn-border-radius-sm:        $border-radius-sm !default;
+
+$btn-transition:              color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+
+// Forms
+
+$label-margin-bottom:                   .5rem !default;
+
+$input-padding-y:                       $input-btn-padding-y !default;
+$input-padding-x:                       $input-btn-padding-x !default;
+$input-line-height:                     $input-btn-line-height !default;
+
+$input-padding-y-sm:                    $input-btn-padding-y-sm !default;
+$input-padding-x-sm:                    $input-btn-padding-x-sm !default;
+$input-line-height-sm:                  $input-btn-line-height-sm !default;
+
+$input-padding-y-lg:                    $input-btn-padding-y-lg !default;
+$input-padding-x-lg:                    $input-btn-padding-x-lg !default;
+$input-line-height-lg:                  $input-btn-line-height-lg !default;
+
+$input-bg:                              $white !default;
+$input-disabled-bg:                     $gray-200 !default;
+
+$input-color:                           $gray-700 !default;
+$input-border-color:                    $gray-400 !default;
+$input-border-width:                    $input-btn-border-width !default;
+$input-box-shadow:                      inset 0 1px 1px rgba($black, .075) !default;
+
+$input-border-radius:                   $border-radius !default;
+$input-border-radius-lg:                $border-radius-lg !default;
+$input-border-radius-sm:                $border-radius-sm !default;
+
+$input-focus-bg:                        $input-bg !default;
+$input-focus-border-color:              lighten($component-active-bg, 25%) !default;
+$input-focus-color:                     $input-color !default;
+$input-focus-width:                     $input-btn-focus-width !default;
+$input-focus-box-shadow:                $input-btn-focus-box-shadow !default;
+
+$input-placeholder-color:               $gray-600 !default;
+$input-plaintext-color:                 $body-color !default;
+
+$input-height-border:                   $input-border-width * 2 !default;
+
+$input-height-inner:                    ($font-size-base * $input-btn-line-height) + ($input-btn-padding-y * 2) !default;
+$input-height:                          calc(#{$input-height-inner} + #{$input-height-border}) !default;
+
+$input-height-inner-sm:                 ($font-size-sm * $input-btn-line-height-sm) + ($input-btn-padding-y-sm * 2) !default;
+$input-height-sm:                       calc(#{$input-height-inner-sm} + #{$input-height-border}) !default;
+
+$input-height-inner-lg:                 ($font-size-lg * $input-btn-line-height-lg) + ($input-btn-padding-y-lg * 2) !default;
+$input-height-lg:                       calc(#{$input-height-inner-lg} + #{$input-height-border}) !default;
+
+$input-transition:                      border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+$form-text-margin-top:                  .25rem !default;
+
+$form-check-input-gutter:               1.25rem !default;
+$form-check-input-margin-y:             .3rem !default;
+$form-check-input-margin-x:             .25rem !default;
+
+$form-check-inline-margin-x:            .75rem !default;
+$form-check-inline-input-margin-x:      .3125rem !default;
+
+$form-group-margin-bottom:              1rem !default;
+
+$input-group-addon-color:               $input-color !default;
+$input-group-addon-bg:                  $gray-200 !default;
+$input-group-addon-border-color:        $input-border-color !default;
+
+$custom-forms-transition:               background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+$custom-control-gutter:                 1.5rem !default;
+$custom-control-spacer-x:               1rem !default;
+
+$custom-control-indicator-size:         1rem !default;
+$custom-control-indicator-bg:           $gray-300 !default;
+$custom-control-indicator-bg-size:      50% 50% !default;
+$custom-control-indicator-box-shadow:   inset 0 .25rem .25rem rgba($black, .1) !default;
+
+$custom-control-indicator-disabled-bg:          $gray-200 !default;
+$custom-control-label-disabled-color:           $gray-600 !default;
+
+$custom-control-indicator-checked-color:        $component-active-color !default;
+$custom-control-indicator-checked-bg:           $component-active-bg !default;
+$custom-control-indicator-checked-disabled-bg:  rgba(theme-color("primary"), .5) !default;
+$custom-control-indicator-checked-box-shadow:   none !default;
+
+$custom-control-indicator-focus-box-shadow:     0 0 0 1px $body-bg, $input-btn-focus-box-shadow !default;
+
+$custom-control-indicator-active-color:         $component-active-color !default;
+$custom-control-indicator-active-bg:            lighten($component-active-bg, 35%) !default;
+$custom-control-indicator-active-box-shadow:    none !default;
+
+$custom-checkbox-indicator-border-radius:       $border-radius !default;
+$custom-checkbox-indicator-icon-checked:        str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$custom-control-indicator-checked-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$custom-checkbox-indicator-indeterminate-bg:          $component-active-bg !default;
+$custom-checkbox-indicator-indeterminate-color:       $custom-control-indicator-checked-color !default;
+$custom-checkbox-indicator-icon-indeterminate:        str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='#{$custom-checkbox-indicator-indeterminate-color}' d='M0 2h4'/%3E%3C/svg%3E"), "#", "%23") !default;
+$custom-checkbox-indicator-indeterminate-box-shadow:  none !default;
+
+$custom-radio-indicator-border-radius:          50% !default;
+$custom-radio-indicator-icon-checked:           str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$custom-control-indicator-checked-color}'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$custom-select-padding-y:           .375rem !default;
+$custom-select-padding-x:           .75rem !default;
+$custom-select-height:              $input-height !default;
+$custom-select-indicator-padding:   1rem !default; // Extra padding to account for the presence of the background-image based indicator
+$custom-select-line-height:         $input-btn-line-height !default;
+$custom-select-color:               $input-color !default;
+$custom-select-disabled-color:      $gray-600 !default;
+$custom-select-bg:                  $input-bg !default;
+$custom-select-disabled-bg:         $gray-200 !default;
+$custom-select-bg-size:             8px 10px !default; // In pixels because image dimensions
+$custom-select-indicator-color:     $gray-800 !default;
+$custom-select-indicator:           str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E"), "#", "%23") !default;
+$custom-select-border-width:        $input-btn-border-width !default;
+$custom-select-border-color:        $input-border-color !default;
+$custom-select-border-radius:       $border-radius !default;
+$custom-select-box-shadow:          inset 0 1px 2px rgba($black, .075) !default;
+
+$custom-select-focus-border-color:  $input-focus-border-color !default;
+$custom-select-focus-width:         $input-btn-focus-width !default;
+$custom-select-focus-box-shadow:    0 0 0 $custom-select-focus-width rgba($custom-select-focus-border-color, .5) !default;
+
+$custom-select-font-size-sm:        75% !default;
+$custom-select-height-sm:           $input-height-sm !default;
+
+$custom-select-font-size-lg:        125% !default;
+$custom-select-height-lg:           $input-height-lg !default;
+
+$custom-range-track-width:          100% !default;
+$custom-range-track-height:         .5rem !default;
+$custom-range-track-cursor:         pointer !default;
+$custom-range-track-bg:             $gray-300 !default;
+$custom-range-track-border-radius:  1rem !default;
+$custom-range-track-box-shadow:     inset 0 .25rem .25rem rgba($black, .1) !default;
+
+$custom-range-thumb-width:                   1rem !default;
+$custom-range-thumb-height:                  $custom-range-thumb-width !default;
+$custom-range-thumb-bg:                      $component-active-bg !default;
+$custom-range-thumb-border:                  0 !default;
+$custom-range-thumb-border-radius:           1rem !default;
+$custom-range-thumb-box-shadow:              0 .1rem .25rem rgba($black, .1) !default;
+$custom-range-thumb-focus-box-shadow:        0 0 0 1px $body-bg, $input-btn-focus-box-shadow !default;
+$custom-range-thumb-focus-box-shadow-width:  $input-btn-focus-width !default; // For focus box shadow issue in IE/Edge
+$custom-range-thumb-active-bg:               lighten($component-active-bg, 35%) !default;
+
+$custom-file-height:                $input-height !default;
+$custom-file-height-inner:          $input-height-inner !default;
+$custom-file-focus-border-color:    $input-focus-border-color !default;
+$custom-file-focus-box-shadow:      $input-btn-focus-box-shadow !default;
+$custom-file-disabled-bg:           $input-disabled-bg !default;
+
+$custom-file-padding-y:             $input-btn-padding-y !default;
+$custom-file-padding-x:             $input-btn-padding-x !default;
+$custom-file-line-height:           $input-btn-line-height !default;
+$custom-file-color:                 $input-color !default;
+$custom-file-bg:                    $input-bg !default;
+$custom-file-border-width:          $input-btn-border-width !default;
+$custom-file-border-color:          $input-border-color !default;
+$custom-file-border-radius:         $input-border-radius !default;
+$custom-file-box-shadow:            $input-box-shadow !default;
+$custom-file-button-color:          $custom-file-color !default;
+$custom-file-button-bg:             $input-group-addon-bg !default;
+$custom-file-text: (
+  en: "Browse"
+) !default;
+
+
+// Form validation
+$form-feedback-margin-top:          $form-text-margin-top !default;
+$form-feedback-font-size:           $small-font-size !default;
+$form-feedback-valid-color:         theme-color("success") !default;
+$form-feedback-invalid-color:       theme-color("danger") !default;
+
+
+// Dropdowns
+//
+// Dropdown menu container and contents.
+
+$dropdown-min-width:                10rem !default;
+$dropdown-padding-y:                .5rem !default;
+$dropdown-spacer:                   .125rem !default;
+$dropdown-bg:                       $white !default;
+$dropdown-border-color:             rgba($black, .15) !default;
+$dropdown-border-radius:            $border-radius !default;
+$dropdown-border-width:             $border-width !default;
+$dropdown-divider-bg:               $gray-200 !default;
+$dropdown-box-shadow:               0 .5rem 1rem rgba($black, .175) !default;
+
+$dropdown-link-color:               $gray-900 !default;
+$dropdown-link-hover-color:         darken($gray-900, 5%) !default;
+$dropdown-link-hover-bg:            $gray-100 !default;
+
+$dropdown-link-active-color:        $component-active-color !default;
+$dropdown-link-active-bg:           $component-active-bg !default;
+
+$dropdown-link-disabled-color:      $gray-600 !default;
+
+$dropdown-item-padding-y:           .25rem !default;
+$dropdown-item-padding-x:           1.5rem !default;
+
+$dropdown-header-color:             $gray-600 !default;
+
+
+// Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+
+$zindex-dropdown:                   1000 !default;
+$zindex-sticky:                     1020 !default;
+$zindex-fixed:                      1030 !default;
+$zindex-modal-backdrop:             1040 !default;
+$zindex-modal:                      1050 !default;
+$zindex-popover:                    1060 !default;
+$zindex-tooltip:                    1070 !default;
+
+// Navs
+
+$nav-link-padding-y:                .5rem !default;
+$nav-link-padding-x:                1rem !default;
+$nav-link-disabled-color:           $gray-600 !default;
+
+$nav-tabs-border-color:             $gray-300 !default;
+$nav-tabs-border-width:             $border-width !default;
+$nav-tabs-border-radius:            $border-radius !default;
+$nav-tabs-link-hover-border-color:  $gray-200 $gray-200 $nav-tabs-border-color !default;
+$nav-tabs-link-active-color:        $gray-700 !default;
+$nav-tabs-link-active-bg:           $body-bg !default;
+$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default;
+
+$nav-pills-border-radius:           $border-radius !default;
+$nav-pills-link-active-color:       $component-active-color !default;
+$nav-pills-link-active-bg:          $component-active-bg !default;
+
+$nav-divider-color:                 $gray-200 !default;
+$nav-divider-margin-y:              ($spacer / 2) !default;
+
+// Navbar
+
+$navbar-padding-y:                  ($spacer / 2) !default;
+$navbar-padding-x:                  $spacer !default;
+
+$navbar-nav-link-padding-x:         .5rem !default;
+
+$navbar-brand-font-size:            $font-size-lg !default;
+// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link
+$nav-link-height:                   ($font-size-base * $line-height-base + $nav-link-padding-y * 2) !default;
+$navbar-brand-height:               $navbar-brand-font-size * $line-height-base !default;
+$navbar-brand-padding-y:            ($nav-link-height - $navbar-brand-height) / 2 !default;
+
+$navbar-toggler-padding-y:          .25rem !default;
+$navbar-toggler-padding-x:          .75rem !default;
+$navbar-toggler-font-size:          $font-size-lg !default;
+$navbar-toggler-border-radius:      $btn-border-radius !default;
+
+$navbar-dark-color:                 rgba($white, .5) !default;
+$navbar-dark-hover-color:           rgba($white, .75) !default;
+$navbar-dark-active-color:          $white !default;
+$navbar-dark-disabled-color:        rgba($white, .25) !default;
+$navbar-dark-toggler-icon-bg:       str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-dark-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23") !default;
+$navbar-dark-toggler-border-color:  rgba($white, .1) !default;
+
+$navbar-light-color:                rgba($black, .5) !default;
+$navbar-light-hover-color:          rgba($black, .7) !default;
+$navbar-light-active-color:         rgba($black, .9) !default;
+$navbar-light-disabled-color:       rgba($black, .3) !default;
+$navbar-light-toggler-icon-bg:      str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23") !default;
+$navbar-light-toggler-border-color: rgba($black, .1) !default;
+
+// Pagination
+
+$pagination-padding-y:              .5rem !default;
+$pagination-padding-x:              .75rem !default;
+$pagination-padding-y-sm:           .25rem !default;
+$pagination-padding-x-sm:           .5rem !default;
+$pagination-padding-y-lg:           .75rem !default;
+$pagination-padding-x-lg:           1.5rem !default;
+$pagination-line-height:            1.25 !default;
+
+$pagination-color:                  $link-color !default;
+$pagination-bg:                     $white !default;
+$pagination-border-width:           $border-width !default;
+$pagination-border-color:           $gray-300 !default;
+
+$pagination-focus-box-shadow:       $input-btn-focus-box-shadow !default;
+$pagination-focus-outline:          0 !default;
+
+$pagination-hover-color:            $link-hover-color !default;
+$pagination-hover-bg:               $gray-200 !default;
+$pagination-hover-border-color:     $gray-300 !default;
+
+$pagination-active-color:           $component-active-color !default;
+$pagination-active-bg:              $component-active-bg !default;
+$pagination-active-border-color:    $pagination-active-bg !default;
+
+$pagination-disabled-color:         $gray-600 !default;
+$pagination-disabled-bg:            $white !default;
+$pagination-disabled-border-color:  $gray-300 !default;
+
+
+// Jumbotron
+
+$jumbotron-padding:                 2rem !default;
+$jumbotron-bg:                      $gray-200 !default;
+
+
+// Cards
+
+$card-spacer-y:                     .75rem !default;
+$card-spacer-x:                     1.25rem !default;
+$card-border-width:                 $border-width !default;
+$card-border-radius:                $border-radius !default;
+$card-border-color:                 rgba($black, .125) !default;
+$card-inner-border-radius:          calc(#{$card-border-radius} - #{$card-border-width}) !default;
+$card-cap-bg:                       rgba($black, .03) !default;
+$card-bg:                           $white !default;
+
+$card-img-overlay-padding:          1.25rem !default;
+
+$card-group-margin:                 ($grid-gutter-width / 2) !default;
+$card-deck-margin:                  $card-group-margin !default;
+
+$card-columns-count:                3 !default;
+$card-columns-gap:                  1.25rem !default;
+$card-columns-margin:               $card-spacer-y !default;
+
+
+// Tooltips
+
+$tooltip-font-size:                 $font-size-sm !default;
+$tooltip-max-width:                 200px !default;
+$tooltip-color:                     $white !default;
+$tooltip-bg:                        $black !default;
+$tooltip-border-radius:             $border-radius !default;
+$tooltip-opacity:                   .9 !default;
+$tooltip-padding-y:                 .25rem !default;
+$tooltip-padding-x:                 .5rem !default;
+$tooltip-margin:                    0 !default;
+
+$tooltip-arrow-width:               .8rem !default;
+$tooltip-arrow-height:              .4rem !default;
+$tooltip-arrow-color:               $tooltip-bg !default;
+
+
+// Popovers
+
+$popover-font-size:                 $font-size-sm !default;
+$popover-bg:                        $white !default;
+$popover-max-width:                 276px !default;
+$popover-border-width:              $border-width !default;
+$popover-border-color:              rgba($black, .2) !default;
+$popover-border-radius:             $border-radius-lg !default;
+$popover-box-shadow:                0 .25rem .5rem rgba($black, .2) !default;
+
+$popover-header-bg:                 darken($popover-bg, 3%) !default;
+$popover-header-color:              $headings-color !default;
+$popover-header-padding-y:          .5rem !default;
+$popover-header-padding-x:          .75rem !default;
+
+$popover-body-color:                $body-color !default;
+$popover-body-padding-y:            $popover-header-padding-y !default;
+$popover-body-padding-x:            $popover-header-padding-x !default;
+
+$popover-arrow-width:               1rem !default;
+$popover-arrow-height:              .5rem !default;
+$popover-arrow-color:               $popover-bg !default;
+
+$popover-arrow-outer-color:         fade-in($popover-border-color, .05) !default;
+
+
+// Badges
+
+$badge-font-size:                   75% !default;
+$badge-font-weight:                 $font-weight-bold !default;
+$badge-padding-y:                   .25em !default;
+$badge-padding-x:                   .4em !default;
+$badge-border-radius:               $border-radius !default;
+
+$badge-pill-padding-x:              .6em !default;
+// Use a higher than normal value to ensure completely rounded edges when
+// customizing padding or font-size on labels.
+$badge-pill-border-radius:          10rem !default;
+
+
+// Modals
+
+// Padding applied to the modal body
+$modal-inner-padding:               1rem !default;
+
+$modal-dialog-margin:               .5rem !default;
+$modal-dialog-margin-y-sm-up:       1.75rem !default;
+
+$modal-title-line-height:           $line-height-base !default;
+
+$modal-content-bg:                  $white !default;
+$modal-content-border-color:        rgba($black, .2) !default;
+$modal-content-border-width:        $border-width !default;
+$modal-content-border-radius:       $border-radius-lg !default;
+$modal-content-box-shadow-xs:       0 .25rem .5rem rgba($black, .5) !default;
+$modal-content-box-shadow-sm-up:    0 .5rem 1rem rgba($black, .5) !default;
+
+$modal-backdrop-bg:                 $black !default;
+$modal-backdrop-opacity:            .5 !default;
+$modal-header-border-color:         $gray-200 !default;
+$modal-footer-border-color:         $modal-header-border-color !default;
+$modal-header-border-width:         $modal-content-border-width !default;
+$modal-footer-border-width:         $modal-header-border-width !default;
+$modal-header-padding:              1rem !default;
+
+$modal-lg:                          800px !default;
+$modal-md:                          500px !default;
+$modal-sm:                          300px !default;
+
+$modal-transition:                  transform .3s ease-out !default;
+
+
+// Alerts
+//
+// Define alert colors, border radius, and padding.
+
+$alert-padding-y:                   .75rem !default;
+$alert-padding-x:                   1.25rem !default;
+$alert-margin-bottom:               1rem !default;
+$alert-border-radius:               $border-radius !default;
+$alert-link-font-weight:            $font-weight-bold !default;
+$alert-border-width:                $border-width !default;
+
+$alert-bg-level:                    -10 !default;
+$alert-border-level:                -9 !default;
+$alert-color-level:                 6 !default;
+
+
+// Progress bars
+
+$progress-height:                   1rem !default;
+$progress-font-size:                ($font-size-base * .75) !default;
+$progress-bg:                       $gray-200 !default;
+$progress-border-radius:            $border-radius !default;
+$progress-box-shadow:               inset 0 .1rem .1rem rgba($black, .1) !default;
+$progress-bar-color:                $white !default;
+$progress-bar-bg:                   theme-color("primary") !default;
+$progress-bar-animation-timing:     1s linear infinite !default;
+$progress-bar-transition:           width .6s ease !default;
+
+// List group
+
+$list-group-bg:                     $white !default;
+$list-group-border-color:           rgba($black, .125) !default;
+$list-group-border-width:           $border-width !default;
+$list-group-border-radius:          $border-radius !default;
+
+$list-group-item-padding-y:         .75rem !default;
+$list-group-item-padding-x:         1.25rem !default;
+
+$list-group-hover-bg:               $gray-100 !default;
+$list-group-active-color:           $component-active-color !default;
+$list-group-active-bg:              $component-active-bg !default;
+$list-group-active-border-color:    $list-group-active-bg !default;
+
+$list-group-disabled-color:         $gray-600 !default;
+$list-group-disabled-bg:            $list-group-bg !default;
+
+$list-group-action-color:           $gray-700 !default;
+$list-group-action-hover-color:     $list-group-action-color !default;
+
+$list-group-action-active-color:    $body-color !default;
+$list-group-action-active-bg:       $gray-200 !default;
+
+
+// Image thumbnails
+
+$thumbnail-padding:                 .25rem !default;
+$thumbnail-bg:                      $body-bg !default;
+$thumbnail-border-width:            $border-width !default;
+$thumbnail-border-color:            $gray-300 !default;
+$thumbnail-border-radius:           $border-radius !default;
+$thumbnail-box-shadow:              0 1px 2px rgba($black, .075) !default;
+
+
+// Figures
+
+$figure-caption-font-size:          90% !default;
+$figure-caption-color:              $gray-600 !default;
+
+
+// Breadcrumbs
+
+$breadcrumb-padding-y:              .75rem !default;
+$breadcrumb-padding-x:              1rem !default;
+$breadcrumb-item-padding:           .5rem !default;
+
+$breadcrumb-margin-bottom:          1rem !default;
+
+$breadcrumb-bg:                     $gray-200 !default;
+$breadcrumb-divider-color:          $gray-600 !default;
+$breadcrumb-active-color:           $gray-600 !default;
+$breadcrumb-divider:                quote("/") !default;
+
+$breadcrumb-border-radius:          $border-radius !default;
+
+
+// Carousel
+
+$carousel-control-color:            $white !default;
+$carousel-control-width:            15% !default;
+$carousel-control-opacity:          .5 !default;
+
+$carousel-indicator-width:          30px !default;
+$carousel-indicator-height:         3px !default;
+$carousel-indicator-spacer:         3px !default;
+$carousel-indicator-active-bg:      $white !default;
+
+$carousel-caption-width:            70% !default;
+$carousel-caption-color:            $white !default;
+
+$carousel-control-icon-width:       20px !default;
+
+$carousel-control-prev-icon-bg:     str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"), "#", "%23") !default;
+$carousel-control-next-icon-bg:     str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$carousel-transition:               transform .6s ease !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)
+
+
+// Close
+
+$close-font-size:                   $font-size-base * 1.5 !default;
+$close-font-weight:                 $font-weight-bold !default;
+$close-color:                       $black !default;
+$close-text-shadow:                 0 1px 0 $white !default;
+
+// Code
+
+$code-font-size:                    87.5% !default;
+$code-color:                        $pink !default;
+
+$kbd-padding-y:                     .2rem !default;
+$kbd-padding-x:                     .4rem !default;
+$kbd-font-size:                     $code-font-size !default;
+$kbd-color:                         $white !default;
+$kbd-bg:                            $gray-900 !default;
+
+$pre-color:                         $gray-900 !default;
+$pre-scrollable-max-height:         340px !default;
+
+
+// Printing
+$print-page-size:                   a3 !default;
+$print-body-min-width:              map-get($grid-breakpoints, "lg") !default;
diff --git a/site/_sass/bootstrap/mixins/_alert.scss b/site/_sass/bootstrap/mixins/_alert.scss
new file mode 100644
index 0000000..db5a7eb
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_alert.scss
@@ -0,0 +1,13 @@
+@mixin alert-variant($background, $border, $color) {
+  color: $color;
+  @include gradient-bg($background);
+  border-color: $border;
+
+  hr {
+    border-top-color: darken($border, 5%);
+  }
+
+  .alert-link {
+    color: darken($color, 10%);
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_background-variant.scss b/site/_sass/bootstrap/mixins/_background-variant.scss
new file mode 100644
index 0000000..494439d
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_background-variant.scss
@@ -0,0 +1,21 @@
+// stylelint-disable declaration-no-important
+
+// Contextual backgrounds
+
+@mixin bg-variant($parent, $color) {
+  #{$parent} {
+    background-color: $color !important;
+  }
+  a#{$parent},
+  button#{$parent} {
+    @include hover-focus {
+      background-color: darken($color, 10%) !important;
+    }
+  }
+}
+
+@mixin bg-gradient-variant($parent, $color) {
+  #{$parent} {
+    background: $color linear-gradient(180deg, mix($body-bg, $color, 15%), $color) repeat-x !important;
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_badge.scss b/site/_sass/bootstrap/mixins/_badge.scss
new file mode 100644
index 0000000..eeca0b4
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_badge.scss
@@ -0,0 +1,12 @@
+@mixin badge-variant($bg) {
+  color: color-yiq($bg);
+  background-color: $bg;
+
+  &[href] {
+    @include hover-focus {
+      color: color-yiq($bg);
+      text-decoration: none;
+      background-color: darken($bg, 10%);
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_border-radius.scss b/site/_sass/bootstrap/mixins/_border-radius.scss
new file mode 100644
index 0000000..2024feb
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_border-radius.scss
@@ -0,0 +1,35 @@
+// Single side border-radius
+
+@mixin border-radius($radius: $border-radius) {
+  @if $enable-rounded {
+    border-radius: $radius;
+  }
+}
+
+@mixin border-top-radius($radius) {
+  @if $enable-rounded {
+    border-top-left-radius: $radius;
+    border-top-right-radius: $radius;
+  }
+}
+
+@mixin border-right-radius($radius) {
+  @if $enable-rounded {
+    border-top-right-radius: $radius;
+    border-bottom-right-radius: $radius;
+  }
+}
+
+@mixin border-bottom-radius($radius) {
+  @if $enable-rounded {
+    border-bottom-right-radius: $radius;
+    border-bottom-left-radius: $radius;
+  }
+}
+
+@mixin border-left-radius($radius) {
+  @if $enable-rounded {
+    border-top-left-radius: $radius;
+    border-bottom-left-radius: $radius;
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_box-shadow.scss b/site/_sass/bootstrap/mixins/_box-shadow.scss
new file mode 100644
index 0000000..b2410e5
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_box-shadow.scss
@@ -0,0 +1,5 @@
+@mixin box-shadow($shadow...) {
+  @if $enable-shadows {
+    box-shadow: $shadow;
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_breakpoints.scss b/site/_sass/bootstrap/mixins/_breakpoints.scss
new file mode 100644
index 0000000..59f25a2
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_breakpoints.scss
@@ -0,0 +1,123 @@
+// Breakpoint viewport sizes and media queries.
+//
+// Breakpoints are defined as a map of (name: minimum width), order from small to large:
+//
+//    (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)
+//
+// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.
+
+// Name of the next breakpoint, or null for the last breakpoint.
+//
+//    >> breakpoint-next(sm)
+//    md
+//    >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+//    md
+//    >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))
+//    md
+@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
+  $n: index($breakpoint-names, $name);
+  @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
+}
+
+// Minimum breakpoint width. Null for the smallest (first) breakpoint.
+//
+//    >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+//    576px
+@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
+  $min: map-get($breakpoints, $name);
+  @return if($min != 0, $min, null);
+}
+
+// Maximum breakpoint width. Null for the largest (last) breakpoint.
+// The maximum value is calculated as the minimum of the next one less 0.02px
+// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.
+// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max
+// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.
+// See https://bugs.webkit.org/show_bug.cgi?id=178261
+//
+//    >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+//    767.98px
+@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
+  $next: breakpoint-next($name, $breakpoints);
+  @return if($next, breakpoint-min($next, $breakpoints) - .02px, null);
+}
+
+// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.
+// Useful for making responsive utilities.
+//
+//    >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+//    ""  (Returns a blank string)
+//    >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+//    "-sm"
+@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
+  @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}");
+}
+
+// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.
+// Makes the @content apply to the given breakpoint and wider.
+@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
+  $min: breakpoint-min($name, $breakpoints);
+  @if $min {
+    @media (min-width: $min) {
+      @content;
+    }
+  } @else {
+    @content;
+  }
+}
+
+// Media of at most the maximum breakpoint width. No query for the largest breakpoint.
+// Makes the @content apply to the given breakpoint and narrower.
+@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
+  $max: breakpoint-max($name, $breakpoints);
+  @if $max {
+    @media (max-width: $max) {
+      @content;
+    }
+  } @else {
+    @content;
+  }
+}
+
+// Media that spans multiple breakpoint widths.
+// Makes the @content apply between the min and max breakpoints
+@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {
+  $min: breakpoint-min($lower, $breakpoints);
+  $max: breakpoint-max($upper, $breakpoints);
+
+  @if $min != null and $max != null {
+    @media (min-width: $min) and (max-width: $max) {
+      @content;
+    }
+  } @else if $max == null {
+    @include media-breakpoint-up($lower, $breakpoints) {
+      @content;
+    }
+  } @else if $min == null {
+    @include media-breakpoint-down($upper, $breakpoints) {
+      @content;
+    }
+  }
+}
+
+// Media between the breakpoint's minimum and maximum widths.
+// No minimum for the smallest breakpoint, and no maximum for the largest one.
+// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.
+@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {
+  $min: breakpoint-min($name, $breakpoints);
+  $max: breakpoint-max($name, $breakpoints);
+
+  @if $min != null and $max != null {
+    @media (min-width: $min) and (max-width: $max) {
+      @content;
+    }
+  } @else if $max == null {
+    @include media-breakpoint-up($name, $breakpoints) {
+      @content;
+    }
+  } @else if $min == null {
+    @include media-breakpoint-down($name, $breakpoints) {
+      @content;
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_buttons.scss b/site/_sass/bootstrap/mixins/_buttons.scss
new file mode 100644
index 0000000..06ad677
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_buttons.scss
@@ -0,0 +1,109 @@
+// Button variants
+//
+// Easily pump out default styles, as well as :hover, :focus, :active,
+// and disabled options for all buttons
+
+@mixin button-variant($background, $border, $hover-background: darken($background, 7.5%), $hover-border: darken($border, 10%), $active-background: darken($background, 10%), $active-border: darken($border, 12.5%)) {
+  color: color-yiq($background);
+  @include gradient-bg($background);
+  border-color: $border;
+  @include box-shadow($btn-box-shadow);
+
+  @include hover {
+    color: color-yiq($hover-background);
+    @include gradient-bg($hover-background);
+    border-color: $hover-border;
+  }
+
+  &:focus,
+  &.focus {
+    // Avoid using mixin so we can pass custom focus shadow properly
+    @if $enable-shadows {
+      box-shadow: $btn-box-shadow, 0 0 0 $btn-focus-width rgba($border, .5);
+    } @else {
+      box-shadow: 0 0 0 $btn-focus-width rgba($border, .5);
+    }
+  }
+
+  // Disabled comes first so active can properly restyle
+  &.disabled,
+  &:disabled {
+    color: color-yiq($background);
+    background-color: $background;
+    border-color: $border;
+  }
+
+  &:not(:disabled):not(.disabled):active,
+  &:not(:disabled):not(.disabled).active,
+  .show > &.dropdown-toggle {
+    color: color-yiq($active-background);
+    background-color: $active-background;
+    @if $enable-gradients {
+      background-image: none; // Remove the gradient for the pressed/active state
+    }
+    border-color: $active-border;
+
+    &:focus {
+      // Avoid using mixin so we can pass custom focus shadow properly
+      @if $enable-shadows {
+        box-shadow: $btn-active-box-shadow, 0 0 0 $btn-focus-width rgba($border, .5);
+      } @else {
+        box-shadow: 0 0 0 $btn-focus-width rgba($border, .5);
+      }
+    }
+  }
+}
+
+@mixin button-outline-variant($color, $color-hover: color-yiq($color), $active-background: $color, $active-border: $color) {
+  color: $color;
+  background-color: transparent;
+  background-image: none;
+  border-color: $color;
+
+  &:hover {
+    color: $color-hover;
+    background-color: $active-background;
+    border-color: $active-border;
+  }
+
+  &:focus,
+  &.focus {
+    box-shadow: 0 0 0 $btn-focus-width rgba($color, .5);
+  }
+
+  &.disabled,
+  &:disabled {
+    color: $color;
+    background-color: transparent;
+  }
+
+  &:not(:disabled):not(.disabled):active,
+  &:not(:disabled):not(.disabled).active,
+  .show > &.dropdown-toggle {
+    color: color-yiq($active-background);
+    background-color: $active-background;
+    border-color: $active-border;
+
+    &:focus {
+      // Avoid using mixin so we can pass custom focus shadow properly
+      @if $enable-shadows and $btn-active-box-shadow != none {
+        box-shadow: $btn-active-box-shadow, 0 0 0 $btn-focus-width rgba($color, .5);
+      } @else {
+        box-shadow: 0 0 0 $btn-focus-width rgba($color, .5);
+      }
+    }
+  }
+}
+
+// Button sizes
+@mixin button-size($padding-y, $padding-x, $font-size, $line-height, $border-radius) {
+  padding: $padding-y $padding-x;
+  font-size: $font-size;
+  line-height: $line-height;
+  // Manually declare to provide an override to the browser default
+  @if $enable-rounded {
+    border-radius: $border-radius;
+  } @else {
+    border-radius: 0;
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_caret.scss b/site/_sass/bootstrap/mixins/_caret.scss
new file mode 100644
index 0000000..82aea42
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_caret.scss
@@ -0,0 +1,66 @@
+@mixin caret-down {
+  border-top: $caret-width solid;
+  border-right: $caret-width solid transparent;
+  border-bottom: 0;
+  border-left: $caret-width solid transparent;
+}
+
+@mixin caret-up {
+  border-top: 0;
+  border-right: $caret-width solid transparent;
+  border-bottom: $caret-width solid;
+  border-left: $caret-width solid transparent;
+}
+
+@mixin caret-right {
+  border-top: $caret-width solid transparent;
+  border-right: 0;
+  border-bottom: $caret-width solid transparent;
+  border-left: $caret-width solid;
+}
+
+@mixin caret-left {
+  border-top: $caret-width solid transparent;
+  border-right: $caret-width solid;
+  border-bottom: $caret-width solid transparent;
+}
+
+@mixin caret($direction: down) {
+  @if $enable-caret {
+    &::after {
+      display: inline-block;
+      width: 0;
+      height: 0;
+      margin-left: $caret-width * .85;
+      vertical-align: $caret-width * .85;
+      content: "";
+      @if $direction == down {
+        @include caret-down;
+      } @else if $direction == up {
+        @include caret-up;
+      } @else if $direction == right {
+        @include caret-right;
+      }
+    }
+
+    @if $direction == left {
+      &::after {
+        display: none;
+      }
+
+      &::before {
+        display: inline-block;
+        width: 0;
+        height: 0;
+        margin-right: $caret-width * .85;
+        vertical-align: $caret-width * .85;
+        content: "";
+        @include caret-left;
+      }
+    }
+
+    &:empty::after {
+      margin-left: 0;
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_clearfix.scss b/site/_sass/bootstrap/mixins/_clearfix.scss
new file mode 100644
index 0000000..11a977b
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_clearfix.scss
@@ -0,0 +1,7 @@
+@mixin clearfix() {
+  &::after {
+    display: block;
+    clear: both;
+    content: "";
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_float.scss b/site/_sass/bootstrap/mixins/_float.scss
new file mode 100644
index 0000000..48fa8b6
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_float.scss
@@ -0,0 +1,11 @@
+// stylelint-disable declaration-no-important
+
+@mixin float-left {
+  float: left !important;
+}
+@mixin float-right {
+  float: right !important;
+}
+@mixin float-none {
+  float: none !important;
+}
diff --git a/site/_sass/bootstrap/mixins/_forms.scss b/site/_sass/bootstrap/mixins/_forms.scss
new file mode 100644
index 0000000..3a61878
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_forms.scss
@@ -0,0 +1,147 @@
+// Form control focus state
+//
+// Generate a customized focus state and for any input with the specified color,
+// which defaults to the `$input-focus-border-color` variable.
+//
+// We highly encourage you to not customize the default value, but instead use
+// this to tweak colors on an as-needed basis. This aesthetic change is based on
+// WebKit's default styles, but applicable to a wider range of browsers. Its
+// usability and accessibility should be taken into account with any change.
+//
+// Example usage: change the default blue border and shadow to white for better
+// contrast against a dark gray background.
+@mixin form-control-focus() {
+  &:focus {
+    color: $input-focus-color;
+    background-color: $input-focus-bg;
+    border-color: $input-focus-border-color;
+    outline: 0;
+    // Avoid using mixin so we can pass custom focus shadow properly
+    @if $enable-shadows {
+      box-shadow: $input-box-shadow, $input-focus-box-shadow;
+    } @else {
+      box-shadow: $input-focus-box-shadow;
+    }
+  }
+}
+
+
+@mixin form-validation-state($state, $color) {
+  .#{$state}-feedback {
+    display: none;
+    width: 100%;
+    margin-top: $form-feedback-margin-top;
+    font-size: $form-feedback-font-size;
+    color: $color;
+  }
+
+  .#{$state}-tooltip {
+    position: absolute;
+    top: 100%;
+    z-index: 5;
+    display: none;
+    max-width: 100%; // Contain to parent when possible
+    padding: $tooltip-padding-y $tooltip-padding-x;
+    margin-top: .1rem;
+    font-size: $tooltip-font-size;
+    line-height: $line-height-base;
+    color: color-yiq($color);
+    background-color: rgba($color, $tooltip-opacity);
+    @include border-radius($tooltip-border-radius);
+  }
+
+  .form-control,
+  .custom-select {
+    .was-validated &:#{$state},
+    &.is-#{$state} {
+      border-color: $color;
+
+      &:focus {
+        border-color: $color;
+        box-shadow: 0 0 0 $input-focus-width rgba($color, .25);
+      }
+
+      ~ .#{$state}-feedback,
+      ~ .#{$state}-tooltip {
+        display: block;
+      }
+    }
+  }
+
+  .form-control-file {
+    .was-validated &:#{$state},
+    &.is-#{$state} {
+      ~ .#{$state}-feedback,
+      ~ .#{$state}-tooltip {
+        display: block;
+      }
+    }
+  }
+
+  .form-check-input {
+    .was-validated &:#{$state},
+    &.is-#{$state} {
+      ~ .form-check-label {
+        color: $color;
+      }
+
+      ~ .#{$state}-feedback,
+      ~ .#{$state}-tooltip {
+        display: block;
+      }
+    }
+  }
+
+  .custom-control-input {
+    .was-validated &:#{$state},
+    &.is-#{$state} {
+      ~ .custom-control-label {
+        color: $color;
+
+        &::before {
+          background-color: lighten($color, 25%);
+        }
+      }
+
+      ~ .#{$state}-feedback,
+      ~ .#{$state}-tooltip {
+        display: block;
+      }
+
+      &:checked {
+        ~ .custom-control-label::before {
+          @include gradient-bg(lighten($color, 10%));
+        }
+      }
+
+      &:focus {
+        ~ .custom-control-label::before {
+          box-shadow: 0 0 0 1px $body-bg, 0 0 0 $input-focus-width rgba($color, .25);
+        }
+      }
+    }
+  }
+
+  // custom file
+  .custom-file-input {
+    .was-validated &:#{$state},
+    &.is-#{$state} {
+      ~ .custom-file-label {
+        border-color: $color;
+
+        &::after { border-color: inherit; }
+      }
+
+      ~ .#{$state}-feedback,
+      ~ .#{$state}-tooltip {
+        display: block;
+      }
+
+      &:focus {
+        ~ .custom-file-label {
+          box-shadow: 0 0 0 $input-focus-width rgba($color, .25);
+        }
+      }
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_gradients.scss b/site/_sass/bootstrap/mixins/_gradients.scss
new file mode 100644
index 0000000..88c4d64
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_gradients.scss
@@ -0,0 +1,45 @@
+// Gradients
+
+@mixin gradient-bg($color) {
+  @if $enable-gradients {
+    background: $color linear-gradient(180deg, mix($body-bg, $color, 15%), $color) repeat-x;
+  } @else {
+    background-color: $color;
+  }
+}
+
+// Horizontal gradient, from left to right
+//
+// Creates two color stops, start and end, by specifying a color and position for each color stop.
+@mixin gradient-x($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) {
+  background-image: linear-gradient(to right, $start-color $start-percent, $end-color $end-percent);
+  background-repeat: repeat-x;
+}
+
+// Vertical gradient, from top to bottom
+//
+// Creates two color stops, start and end, by specifying a color and position for each color stop.
+@mixin gradient-y($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) {
+  background-image: linear-gradient(to bottom, $start-color $start-percent, $end-color $end-percent);
+  background-repeat: repeat-x;
+}
+
+@mixin gradient-directional($start-color: $gray-700, $end-color: $gray-800, $deg: 45deg) {
+  background-image: linear-gradient($deg, $start-color, $end-color);
+  background-repeat: repeat-x;
+}
+@mixin gradient-x-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) {
+  background-image: linear-gradient(to right, $start-color, $mid-color $color-stop, $end-color);
+  background-repeat: no-repeat;
+}
+@mixin gradient-y-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) {
+  background-image: linear-gradient($start-color, $mid-color $color-stop, $end-color);
+  background-repeat: no-repeat;
+}
+@mixin gradient-radial($inner-color: $gray-700, $outer-color: $gray-800) {
+  background-image: radial-gradient(circle, $inner-color, $outer-color);
+  background-repeat: no-repeat;
+}
+@mixin gradient-striped($color: rgba($white, .15), $angle: 45deg) {
+  background-image: linear-gradient($angle, $color 25%, transparent 25%, transparent 50%, $color 50%, $color 75%, transparent 75%, transparent);
+}
diff --git a/site/_sass/bootstrap/mixins/_grid-framework.scss b/site/_sass/bootstrap/mixins/_grid-framework.scss
new file mode 100644
index 0000000..7b37f86
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_grid-framework.scss
@@ -0,0 +1,67 @@
+// Framework grid generation
+//
+// Used only by Bootstrap to generate the correct number of grid classes given
+// any value of `$grid-columns`.
+
+@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {
+  // Common properties for all breakpoints
+  %grid-column {
+    position: relative;
+    width: 100%;
+    min-height: 1px; // Prevent columns from collapsing when empty
+    padding-right: ($gutter / 2);
+    padding-left: ($gutter / 2);
+  }
+
+  @each $breakpoint in map-keys($breakpoints) {
+    $infix: breakpoint-infix($breakpoint, $breakpoints);
+
+    // Allow columns to stretch full width below their breakpoints
+    @for $i from 1 through $columns {
+      .col#{$infix}-#{$i} {
+        @extend %grid-column;
+      }
+    }
+    .col#{$infix},
+    .col#{$infix}-auto {
+      @extend %grid-column;
+    }
+
+    @include media-breakpoint-up($breakpoint, $breakpoints) {
+      // Provide basic `.col-{bp}` classes for equal-width flexbox columns
+      .col#{$infix} {
+        flex-basis: 0;
+        flex-grow: 1;
+        max-width: 100%;
+      }
+      .col#{$infix}-auto {
+        flex: 0 0 auto;
+        width: auto;
+        max-width: none; // Reset earlier grid tiers
+      }
+
+      @for $i from 1 through $columns {
+        .col#{$infix}-#{$i} {
+          @include make-col($i, $columns);
+        }
+      }
+
+      .order#{$infix}-first { order: -1; }
+
+      .order#{$infix}-last { order: $columns + 1; }
+
+      @for $i from 0 through $columns {
+        .order#{$infix}-#{$i} { order: $i; }
+      }
+
+      // `$columns - 1` because offsetting by the width of an entire row isn't possible
+      @for $i from 0 through ($columns - 1) {
+        @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0
+          .offset#{$infix}-#{$i} {
+            @include make-col-offset($i, $columns);
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_grid.scss b/site/_sass/bootstrap/mixins/_grid.scss
new file mode 100644
index 0000000..b75ebcb
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_grid.scss
@@ -0,0 +1,52 @@
+/// Grid system
+//
+// Generate semantic grid columns with these mixins.
+
+@mixin make-container() {
+  width: 100%;
+  padding-right: ($grid-gutter-width / 2);
+  padding-left: ($grid-gutter-width / 2);
+  margin-right: auto;
+  margin-left: auto;
+}
+
+
+// For each breakpoint, define the maximum width of the container in a media query
+@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {
+  @each $breakpoint, $container-max-width in $max-widths {
+    @include media-breakpoint-up($breakpoint, $breakpoints) {
+      max-width: $container-max-width;
+    }
+  }
+}
+
+@mixin make-row() {
+  display: flex;
+  flex-wrap: wrap;
+  margin-right: ($grid-gutter-width / -2);
+  margin-left: ($grid-gutter-width / -2);
+}
+
+@mixin make-col-ready() {
+  position: relative;
+  // Prevent columns from becoming too narrow when at smaller grid tiers by
+  // always setting `width: 100%;`. This works because we use `flex` values
+  // later on to override this initial width.
+  width: 100%;
+  min-height: 1px; // Prevent collapsing
+  padding-right: ($grid-gutter-width / 2);
+  padding-left: ($grid-gutter-width / 2);
+}
+
+@mixin make-col($size, $columns: $grid-columns) {
+  flex: 0 0 percentage($size / $columns);
+  // Add a `max-width` to ensure content within each column does not blow out
+  // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari
+  // do not appear to require this.
+  max-width: percentage($size / $columns);
+}
+
+@mixin make-col-offset($size, $columns: $grid-columns) {
+  $num: $size / $columns;
+  margin-left: if($num == 0, 0, percentage($num));
+}
diff --git a/site/_sass/bootstrap/mixins/_hover.scss b/site/_sass/bootstrap/mixins/_hover.scss
new file mode 100644
index 0000000..192f847
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_hover.scss
@@ -0,0 +1,37 @@
+// Hover mixin and `$enable-hover-media-query` are deprecated.
+//
+// Originally added during our alphas and maintained during betas, this mixin was
+// designed to prevent `:hover` stickiness on iOS-an issue where hover styles
+// would persist after initial touch.
+//
+// For backward compatibility, we've kept these mixins and updated them to
+// always return their regular pseudo-classes instead of a shimmed media query.
+//
+// Issue: https://github.com/twbs/bootstrap/issues/25195
+
+@mixin hover {
+  &:hover { @content; }
+}
+
+@mixin hover-focus {
+  &:hover,
+  &:focus {
+    @content;
+  }
+}
+
+@mixin plain-hover-focus {
+  &,
+  &:hover,
+  &:focus {
+    @content;
+  }
+}
+
+@mixin hover-focus-active {
+  &:hover,
+  &:focus,
+  &:active {
+    @content;
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_image.scss b/site/_sass/bootstrap/mixins/_image.scss
new file mode 100644
index 0000000..0544f0d
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_image.scss
@@ -0,0 +1,36 @@
+// Image Mixins
+// - Responsive image
+// - Retina image
+
+
+// Responsive image
+//
+// Keep images from scaling beyond the width of their parents.
+
+@mixin img-fluid {
+  // Part 1: Set a maximum relative to the parent
+  max-width: 100%;
+  // Part 2: Override the height to auto, otherwise images will be stretched
+  // when setting a width and height attribute on the img element.
+  height: auto;
+}
+
+
+// Retina image
+//
+// Short retina mixin for setting background-image and -size.
+
+// stylelint-disable indentation, media-query-list-comma-newline-after
+@mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) {
+  background-image: url($file-1x);
+
+  // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,
+  // but doesn't convert dppx=>dpi.
+  // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.
+  // Compatibility info: https://caniuse.com/#feat=css-media-resolution
+  @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx
+  only screen and (min-resolution: 2dppx) { // Standardized
+    background-image: url($file-2x);
+    background-size: $width-1x $height-1x;
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_list-group.scss b/site/_sass/bootstrap/mixins/_list-group.scss
new file mode 100644
index 0000000..cd47a4e
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_list-group.scss
@@ -0,0 +1,21 @@
+// List Groups
+
+@mixin list-group-item-variant($state, $background, $color) {
+  .list-group-item-#{$state} {
+    color: $color;
+    background-color: $background;
+
+    &.list-group-item-action {
+      @include hover-focus {
+        color: $color;
+        background-color: darken($background, 5%);
+      }
+
+      &.active {
+        color: $white;
+        background-color: $color;
+        border-color: $color;
+      }
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_lists.scss b/site/_sass/bootstrap/mixins/_lists.scss
new file mode 100644
index 0000000..2518562
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_lists.scss
@@ -0,0 +1,7 @@
+// Lists
+
+// Unstyled keeps list items block level, just removes default browser padding and list-style
+@mixin list-unstyled {
+  padding-left: 0;
+  list-style: none;
+}
diff --git a/site/_sass/bootstrap/mixins/_nav-divider.scss b/site/_sass/bootstrap/mixins/_nav-divider.scss
new file mode 100644
index 0000000..4fb37b6
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_nav-divider.scss
@@ -0,0 +1,10 @@
+// Horizontal dividers
+//
+// Dividers (basically an hr) within dropdowns and nav lists
+
+@mixin nav-divider($color: $nav-divider-color, $margin-y: $nav-divider-margin-y) {
+  height: 0;
+  margin: $margin-y 0;
+  overflow: hidden;
+  border-top: 1px solid $color;
+}
diff --git a/site/_sass/bootstrap/mixins/_pagination.scss b/site/_sass/bootstrap/mixins/_pagination.scss
new file mode 100644
index 0000000..ff36eb6
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_pagination.scss
@@ -0,0 +1,22 @@
+// Pagination
+
+@mixin pagination-size($padding-y, $padding-x, $font-size, $line-height, $border-radius) {
+  .page-link {
+    padding: $padding-y $padding-x;
+    font-size: $font-size;
+    line-height: $line-height;
+  }
+
+  .page-item {
+    &:first-child {
+      .page-link {
+        @include border-left-radius($border-radius);
+      }
+    }
+    &:last-child {
+      .page-link {
+        @include border-right-radius($border-radius);
+      }
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_reset-text.scss b/site/_sass/bootstrap/mixins/_reset-text.scss
new file mode 100644
index 0000000..71edb00
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_reset-text.scss
@@ -0,0 +1,17 @@
+@mixin reset-text {
+  font-family: $font-family-base;
+  // We deliberately do NOT reset font-size or word-wrap.
+  font-style: normal;
+  font-weight: $font-weight-normal;
+  line-height: $line-height-base;
+  text-align: left; // Fallback for where `start` is not supported
+  text-align: start; // stylelint-disable-line declaration-block-no-duplicate-properties
+  text-decoration: none;
+  text-shadow: none;
+  text-transform: none;
+  letter-spacing: normal;
+  word-break: normal;
+  word-spacing: normal;
+  white-space: normal;
+  line-break: auto;
+}
diff --git a/site/_sass/bootstrap/mixins/_resize.scss b/site/_sass/bootstrap/mixins/_resize.scss
new file mode 100644
index 0000000..66f233a
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_resize.scss
@@ -0,0 +1,6 @@
+// Resize anything
+
+@mixin resizable($direction) {
+  overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible`
+  resize: $direction; // Options: horizontal, vertical, both
+}
diff --git a/site/_sass/bootstrap/mixins/_screen-reader.scss b/site/_sass/bootstrap/mixins/_screen-reader.scss
new file mode 100644
index 0000000..812591b
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_screen-reader.scss
@@ -0,0 +1,33 @@
+// Only display content to screen readers
+//
+// See: https://a11yproject.com/posts/how-to-hide-content/
+// See: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/
+
+@mixin sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border: 0;
+}
+
+// Use in conjunction with .sr-only to only display content when it's focused.
+//
+// Useful for "Skip to main content" links; see https://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
+//
+// Credit: HTML5 Boilerplate
+
+@mixin sr-only-focusable {
+  &:active,
+  &:focus {
+    position: static;
+    width: auto;
+    height: auto;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_size.scss b/site/_sass/bootstrap/mixins/_size.scss
new file mode 100644
index 0000000..b9dd48e
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_size.scss
@@ -0,0 +1,6 @@
+// Sizing shortcuts
+
+@mixin size($width, $height: $width) {
+  width: $width;
+  height: $height;
+}
diff --git a/site/_sass/bootstrap/mixins/_table-row.scss b/site/_sass/bootstrap/mixins/_table-row.scss
new file mode 100644
index 0000000..84f1d30
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_table-row.scss
@@ -0,0 +1,30 @@
+// Tables
+
+@mixin table-row-variant($state, $background) {
+  // Exact selectors below required to override `.table-striped` and prevent
+  // inheritance to nested tables.
+  .table-#{$state} {
+    &,
+    > th,
+    > td {
+      background-color: $background;
+    }
+  }
+
+  // Hover states for `.table-hover`
+  // Note: this is not available for cells or rows within `thead` or `tfoot`.
+  .table-hover {
+    $hover-background: darken($background, 5%);
+
+    .table-#{$state} {
+      @include hover {
+        background-color: $hover-background;
+
+        > td,
+        > th {
+          background-color: $hover-background;
+        }
+      }
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_text-emphasis.scss b/site/_sass/bootstrap/mixins/_text-emphasis.scss
new file mode 100644
index 0000000..58db3e0
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_text-emphasis.scss
@@ -0,0 +1,14 @@
+// stylelint-disable declaration-no-important
+
+// Typography
+
+@mixin text-emphasis-variant($parent, $color) {
+  #{$parent} {
+    color: $color !important;
+  }
+  a#{$parent} {
+    @include hover-focus {
+      color: darken($color, 10%) !important;
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_text-hide.scss b/site/_sass/bootstrap/mixins/_text-hide.scss
new file mode 100644
index 0000000..9ffab16
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_text-hide.scss
@@ -0,0 +1,13 @@
+// CSS image replacement
+@mixin text-hide($ignore-warning: false) {
+  // stylelint-disable-next-line font-family-no-missing-generic-family-keyword
+  font: 0/0 a;
+  color: transparent;
+  text-shadow: none;
+  background-color: transparent;
+  border: 0;
+
+  @if ($ignore-warning != true) {
+    @warn "The `text-hide()` mixin has been deprecated as of v4.1.0. It will be removed entirely in v5.";
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_text-truncate.scss b/site/_sass/bootstrap/mixins/_text-truncate.scss
new file mode 100644
index 0000000..3504bb1
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_text-truncate.scss
@@ -0,0 +1,8 @@
+// Text truncate
+// Requires inline-block or block for proper styling
+
+@mixin text-truncate() {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
diff --git a/site/_sass/bootstrap/mixins/_transition.scss b/site/_sass/bootstrap/mixins/_transition.scss
new file mode 100644
index 0000000..f853821
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_transition.scss
@@ -0,0 +1,13 @@
+@mixin transition($transition...) {
+  @if $enable-transitions {
+    @if length($transition) == 0 {
+      transition: $transition-base;
+    } @else {
+      transition: $transition;
+    }
+  }
+
+  @media screen and (prefers-reduced-motion: reduce) {
+    transition: none;
+  }
+}
diff --git a/site/_sass/bootstrap/mixins/_visibility.scss b/site/_sass/bootstrap/mixins/_visibility.scss
new file mode 100644
index 0000000..fe523d0
--- /dev/null
+++ b/site/_sass/bootstrap/mixins/_visibility.scss
@@ -0,0 +1,7 @@
+// stylelint-disable declaration-no-important
+
+// Visibility
+
+@mixin invisible($visibility) {
+  visibility: $visibility !important;
+}
diff --git a/site/_sass/bootstrap/utilities/_align.scss b/site/_sass/bootstrap/utilities/_align.scss
new file mode 100644
index 0000000..8b7df9f
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_align.scss
@@ -0,0 +1,8 @@
+// stylelint-disable declaration-no-important
+
+.align-baseline    { vertical-align: baseline !important; } // Browser default
+.align-top         { vertical-align: top !important; }
+.align-middle      { vertical-align: middle !important; }
+.align-bottom      { vertical-align: bottom !important; }
+.align-text-bottom { vertical-align: text-bottom !important; }
+.align-text-top    { vertical-align: text-top !important; }
diff --git a/site/_sass/bootstrap/utilities/_background.scss b/site/_sass/bootstrap/utilities/_background.scss
new file mode 100644
index 0000000..1f18b2f
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_background.scss
@@ -0,0 +1,19 @@
+// stylelint-disable declaration-no-important
+
+@each $color, $value in $theme-colors {
+  @include bg-variant(".bg-#{$color}", $value);
+}
+
+@if $enable-gradients {
+  @each $color, $value in $theme-colors {
+    @include bg-gradient-variant(".bg-gradient-#{$color}", $value);
+  }
+}
+
+.bg-white {
+  background-color: $white !important;
+}
+
+.bg-transparent {
+  background-color: transparent !important;
+}
diff --git a/site/_sass/bootstrap/utilities/_borders.scss b/site/_sass/bootstrap/utilities/_borders.scss
new file mode 100644
index 0000000..b8832ef
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_borders.scss
@@ -0,0 +1,59 @@
+// stylelint-disable declaration-no-important
+
+//
+// Border
+//
+
+.border         { border: $border-width solid $border-color !important; }
+.border-top     { border-top: $border-width solid $border-color !important; }
+.border-right   { border-right: $border-width solid $border-color !important; }
+.border-bottom  { border-bottom: $border-width solid $border-color !important; }
+.border-left    { border-left: $border-width solid $border-color !important; }
+
+.border-0        { border: 0 !important; }
+.border-top-0    { border-top: 0 !important; }
+.border-right-0  { border-right: 0 !important; }
+.border-bottom-0 { border-bottom: 0 !important; }
+.border-left-0   { border-left: 0 !important; }
+
+@each $color, $value in $theme-colors {
+  .border-#{$color} {
+    border-color: $value !important;
+  }
+}
+
+.border-white {
+  border-color: $white !important;
+}
+
+//
+// Border-radius
+//
+
+.rounded {
+  border-radius: $border-radius !important;
+}
+.rounded-top {
+  border-top-left-radius: $border-radius !important;
+  border-top-right-radius: $border-radius !important;
+}
+.rounded-right {
+  border-top-right-radius: $border-radius !important;
+  border-bottom-right-radius: $border-radius !important;
+}
+.rounded-bottom {
+  border-bottom-right-radius: $border-radius !important;
+  border-bottom-left-radius: $border-radius !important;
+}
+.rounded-left {
+  border-top-left-radius: $border-radius !important;
+  border-bottom-left-radius: $border-radius !important;
+}
+
+.rounded-circle {
+  border-radius: 50% !important;
+}
+
+.rounded-0 {
+  border-radius: 0 !important;
+}
diff --git a/site/_sass/bootstrap/utilities/_clearfix.scss b/site/_sass/bootstrap/utilities/_clearfix.scss
new file mode 100644
index 0000000..e92522a
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_clearfix.scss
@@ -0,0 +1,3 @@
+.clearfix {
+  @include clearfix();
+}
diff --git a/site/_sass/bootstrap/utilities/_display.scss b/site/_sass/bootstrap/utilities/_display.scss
new file mode 100644
index 0000000..20aeeb5
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_display.scss
@@ -0,0 +1,38 @@
+// stylelint-disable declaration-no-important
+
+//
+// Utilities for common `display` values
+//
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .d#{$infix}-none         { display: none !important; }
+    .d#{$infix}-inline       { display: inline !important; }
+    .d#{$infix}-inline-block { display: inline-block !important; }
+    .d#{$infix}-block        { display: block !important; }
+    .d#{$infix}-table        { display: table !important; }
+    .d#{$infix}-table-row    { display: table-row !important; }
+    .d#{$infix}-table-cell   { display: table-cell !important; }
+    .d#{$infix}-flex         { display: flex !important; }
+    .d#{$infix}-inline-flex  { display: inline-flex !important; }
+  }
+}
+
+
+//
+// Utilities for toggling `display` in print
+//
+
+@media print {
+  .d-print-none         { display: none !important; }
+  .d-print-inline       { display: inline !important; }
+  .d-print-inline-block { display: inline-block !important; }
+  .d-print-block        { display: block !important; }
+  .d-print-table        { display: table !important; }
+  .d-print-table-row    { display: table-row !important; }
+  .d-print-table-cell   { display: table-cell !important; }
+  .d-print-flex         { display: flex !important; }
+  .d-print-inline-flex  { display: inline-flex !important; }
+}
diff --git a/site/_sass/bootstrap/utilities/_embed.scss b/site/_sass/bootstrap/utilities/_embed.scss
new file mode 100644
index 0000000..d3362b6
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_embed.scss
@@ -0,0 +1,52 @@
+// Credit: Nicolas Gallagher and SUIT CSS.
+
+.embed-responsive {
+  position: relative;
+  display: block;
+  width: 100%;
+  padding: 0;
+  overflow: hidden;
+
+  &::before {
+    display: block;
+    content: "";
+  }
+
+  .embed-responsive-item,
+  iframe,
+  embed,
+  object,
+  video {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    border: 0;
+  }
+}
+
+.embed-responsive-21by9 {
+  &::before {
+    padding-top: percentage(9 / 21);
+  }
+}
+
+.embed-responsive-16by9 {
+  &::before {
+    padding-top: percentage(9 / 16);
+  }
+}
+
+.embed-responsive-4by3 {
+  &::before {
+    padding-top: percentage(3 / 4);
+  }
+}
+
+.embed-responsive-1by1 {
+  &::before {
+    padding-top: percentage(1 / 1);
+  }
+}
diff --git a/site/_sass/bootstrap/utilities/_flex.scss b/site/_sass/bootstrap/utilities/_flex.scss
new file mode 100644
index 0000000..3d4266e
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_flex.scss
@@ -0,0 +1,51 @@
+// stylelint-disable declaration-no-important
+
+// Flex variation
+//
+// Custom styles for additional flex alignment options.
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .flex#{$infix}-row            { flex-direction: row !important; }
+    .flex#{$infix}-column         { flex-direction: column !important; }
+    .flex#{$infix}-row-reverse    { flex-direction: row-reverse !important; }
+    .flex#{$infix}-column-reverse { flex-direction: column-reverse !important; }
+
+    .flex#{$infix}-wrap         { flex-wrap: wrap !important; }
+    .flex#{$infix}-nowrap       { flex-wrap: nowrap !important; }
+    .flex#{$infix}-wrap-reverse { flex-wrap: wrap-reverse !important; }
+    .flex#{$infix}-fill         { flex: 1 1 auto !important; }
+    .flex#{$infix}-grow-0       { flex-grow: 0 !important; }
+    .flex#{$infix}-grow-1       { flex-grow: 1 !important; }
+    .flex#{$infix}-shrink-0     { flex-shrink: 0 !important; }
+    .flex#{$infix}-shrink-1     { flex-shrink: 1 !important; }
+
+    .justify-content#{$infix}-start   { justify-content: flex-start !important; }
+    .justify-content#{$infix}-end     { justify-content: flex-end !important; }
+    .justify-content#{$infix}-center  { justify-content: center !important; }
+    .justify-content#{$infix}-between { justify-content: space-between !important; }
+    .justify-content#{$infix}-around  { justify-content: space-around !important; }
+
+    .align-items#{$infix}-start    { align-items: flex-start !important; }
+    .align-items#{$infix}-end      { align-items: flex-end !important; }
+    .align-items#{$infix}-center   { align-items: center !important; }
+    .align-items#{$infix}-baseline { align-items: baseline !important; }
+    .align-items#{$infix}-stretch  { align-items: stretch !important; }
+
+    .align-content#{$infix}-start   { align-content: flex-start !important; }
+    .align-content#{$infix}-end     { align-content: flex-end !important; }
+    .align-content#{$infix}-center  { align-content: center !important; }
+    .align-content#{$infix}-between { align-content: space-between !important; }
+    .align-content#{$infix}-around  { align-content: space-around !important; }
+    .align-content#{$infix}-stretch { align-content: stretch !important; }
+
+    .align-self#{$infix}-auto     { align-self: auto !important; }
+    .align-self#{$infix}-start    { align-self: flex-start !important; }
+    .align-self#{$infix}-end      { align-self: flex-end !important; }
+    .align-self#{$infix}-center   { align-self: center !important; }
+    .align-self#{$infix}-baseline { align-self: baseline !important; }
+    .align-self#{$infix}-stretch  { align-self: stretch !important; }
+  }
+}
diff --git a/site/_sass/bootstrap/utilities/_float.scss b/site/_sass/bootstrap/utilities/_float.scss
new file mode 100644
index 0000000..01655e9
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_float.scss
@@ -0,0 +1,9 @@
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .float#{$infix}-left  { @include float-left; }
+    .float#{$infix}-right { @include float-right; }
+    .float#{$infix}-none  { @include float-none; }
+  }
+}
diff --git a/site/_sass/bootstrap/utilities/_position.scss b/site/_sass/bootstrap/utilities/_position.scss
new file mode 100644
index 0000000..9ecdeeb
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_position.scss
@@ -0,0 +1,37 @@
+// stylelint-disable declaration-no-important
+
+// Common values
+
+// Sass list not in variables since it's not intended for customization.
+// stylelint-disable-next-line scss/dollar-variable-default
+$positions: static, relative, absolute, fixed, sticky;
+
+@each $position in $positions {
+  .position-#{$position} { position: $position !important; }
+}
+
+// Shorthand
+
+.fixed-top {
+  position: fixed;
+  top: 0;
+  right: 0;
+  left: 0;
+  z-index: $zindex-fixed;
+}
+
+.fixed-bottom {
+  position: fixed;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: $zindex-fixed;
+}
+
+.sticky-top {
+  @supports (position: sticky) {
+    position: sticky;
+    top: 0;
+    z-index: $zindex-sticky;
+  }
+}
diff --git a/site/_sass/bootstrap/utilities/_screenreaders.scss b/site/_sass/bootstrap/utilities/_screenreaders.scss
new file mode 100644
index 0000000..9f26fde
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_screenreaders.scss
@@ -0,0 +1,11 @@
+//
+// Screenreaders
+//
+
+.sr-only {
+  @include sr-only();
+}
+
+.sr-only-focusable {
+  @include sr-only-focusable();
+}
diff --git a/site/_sass/bootstrap/utilities/_shadows.scss b/site/_sass/bootstrap/utilities/_shadows.scss
new file mode 100644
index 0000000..f5d03fc
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_shadows.scss
@@ -0,0 +1,6 @@
+// stylelint-disable declaration-no-important
+
+.shadow-sm { box-shadow: $box-shadow-sm !important; }
+.shadow { box-shadow: $box-shadow !important; }
+.shadow-lg { box-shadow: $box-shadow-lg !important; }
+.shadow-none { box-shadow: none !important; }
diff --git a/site/_sass/bootstrap/utilities/_sizing.scss b/site/_sass/bootstrap/utilities/_sizing.scss
new file mode 100644
index 0000000..e95a4db
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_sizing.scss
@@ -0,0 +1,12 @@
+// stylelint-disable declaration-no-important
+
+// Width and height
+
+@each $prop, $abbrev in (width: w, height: h) {
+  @each $size, $length in $sizes {
+    .#{$abbrev}-#{$size} { #{$prop}: $length !important; }
+  }
+}
+
+.mw-100 { max-width: 100% !important; }
+.mh-100 { max-height: 100% !important; }
diff --git a/site/_sass/bootstrap/utilities/_spacing.scss b/site/_sass/bootstrap/utilities/_spacing.scss
new file mode 100644
index 0000000..b2e2354
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_spacing.scss
@@ -0,0 +1,51 @@
+// stylelint-disable declaration-no-important
+
+// Margin and Padding
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    @each $prop, $abbrev in (margin: m, padding: p) {
+      @each $size, $length in $spacers {
+
+        .#{$abbrev}#{$infix}-#{$size} { #{$prop}: $length !important; }
+        .#{$abbrev}t#{$infix}-#{$size},
+        .#{$abbrev}y#{$infix}-#{$size} {
+          #{$prop}-top: $length !important;
+        }
+        .#{$abbrev}r#{$infix}-#{$size},
+        .#{$abbrev}x#{$infix}-#{$size} {
+          #{$prop}-right: $length !important;
+        }
+        .#{$abbrev}b#{$infix}-#{$size},
+        .#{$abbrev}y#{$infix}-#{$size} {
+          #{$prop}-bottom: $length !important;
+        }
+        .#{$abbrev}l#{$infix}-#{$size},
+        .#{$abbrev}x#{$infix}-#{$size} {
+          #{$prop}-left: $length !important;
+        }
+      }
+    }
+
+    // Some special margin utils
+    .m#{$infix}-auto { margin: auto !important; }
+    .mt#{$infix}-auto,
+    .my#{$infix}-auto {
+      margin-top: auto !important;
+    }
+    .mr#{$infix}-auto,
+    .mx#{$infix}-auto {
+      margin-right: auto !important;
+    }
+    .mb#{$infix}-auto,
+    .my#{$infix}-auto {
+      margin-bottom: auto !important;
+    }
+    .ml#{$infix}-auto,
+    .mx#{$infix}-auto {
+      margin-left: auto !important;
+    }
+  }
+}
diff --git a/site/_sass/bootstrap/utilities/_text.scss b/site/_sass/bootstrap/utilities/_text.scss
new file mode 100644
index 0000000..9d96c46
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_text.scss
@@ -0,0 +1,58 @@
+// stylelint-disable declaration-no-important
+
+//
+// Text
+//
+
+.text-monospace { font-family: $font-family-monospace; }
+
+// Alignment
+
+.text-justify  { text-align: justify !important; }
+.text-nowrap   { white-space: nowrap !important; }
+.text-truncate { @include text-truncate; }
+
+// Responsive alignment
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .text#{$infix}-left   { text-align: left !important; }
+    .text#{$infix}-right  { text-align: right !important; }
+    .text#{$infix}-center { text-align: center !important; }
+  }
+}
+
+// Transformation
+
+.text-lowercase  { text-transform: lowercase !important; }
+.text-uppercase  { text-transform: uppercase !important; }
+.text-capitalize { text-transform: capitalize !important; }
+
+// Weight and italics
+
+.font-weight-light  { font-weight: $font-weight-light !important; }
+.font-weight-normal { font-weight: $font-weight-normal !important; }
+.font-weight-bold   { font-weight: $font-weight-bold !important; }
+.font-italic        { font-style: italic !important; }
+
+// Contextual colors
+
+.text-white { color: $white !important; }
+
+@each $color, $value in $theme-colors {
+  @include text-emphasis-variant(".text-#{$color}", $value);
+}
+
+.text-body { color: $body-color !important; }
+.text-muted { color: $text-muted !important; }
+
+.text-black-50 { color: rgba($black, .5) !important; }
+.text-white-50 { color: rgba($white, .5) !important; }
+
+// Misc
+
+.text-hide {
+  @include text-hide($ignore-warning: true);
+}
diff --git a/site/_sass/bootstrap/utilities/_visibility.scss b/site/_sass/bootstrap/utilities/_visibility.scss
new file mode 100644
index 0000000..823406d
--- /dev/null
+++ b/site/_sass/bootstrap/utilities/_visibility.scss
@@ -0,0 +1,11 @@
+//
+// Visibility utilities
+//
+
+.visible {
+  @include invisible(visible);
+}
+
+.invisible {
+  @include invisible(hidden);
+}
diff --git a/site/_sass/osqp.scss b/site/_sass/osqp.scss
new file mode 100644
index 0000000..4df67f7
--- /dev/null
+++ b/site/_sass/osqp.scss
@@ -0,0 +1,15 @@
+@charset "utf-8";
+
+// Bootstrap variables
+@import "osqp/variables";
+
+// Bootstrap itself
+@import "bootstrap";
+
+// Import partials.
+@import
+  "osqp/base",
+  "osqp/layout",
+  "osqp/highlight"
+;
+
diff --git a/site/_sass/osqp/_base.scss b/site/_sass/osqp/_base.scss
new file mode 100644
index 0000000..fc38f89
--- /dev/null
+++ b/site/_sass/osqp/_base.scss
@@ -0,0 +1,30 @@
+
+// // Fonts
+// h1, h2, h3, h4, h5, h6 {
+//     font-family: 'Roboto Condensed', sans-serif;
+// }
+// p, div {
+//     font-family: 'Roboto', sans-serif;
+// }
+
+html {
+  font-size: 1rem;
+}
+
+@include media-breakpoint-up(sm) {
+  html {
+    font-size: 1rem;
+  }
+}
+
+@include media-breakpoint-up(md) {
+  html {
+    font-size: 1rem;
+  }
+}
+
+@include media-breakpoint-up(lg) {
+  html {
+    font-size: 1.1rem;
+  }
+}
diff --git a/site/_sass/osqp/_highlight.scss b/site/_sass/osqp/_highlight.scss
new file mode 100644
index 0000000..58ae3b7
--- /dev/null
+++ b/site/_sass/osqp/_highlight.scss
@@ -0,0 +1,171 @@
+
+/*
+ * GitHub style for Pygments syntax highlighter, for use with Jekyll
+ * Courtesy of GitHub.com
+ *
+ * src: https://github.com/aahan/pygments-github-style
+ */
+
+.highlight pre, pre {
+  background-color: #f8f8f8;
+  border: 1px solid #ccc;
+  padding: 6px 10px;
+  border-radius: 3px;
+}
+
+.highlight {
+  .hll {
+    background-color: #f8f8f8;
+    border: 1px solid #ccc;
+    padding: 6px 10px;
+    border-radius: 3px;
+  }
+  .c {
+    color: #999988;
+    font-style: italic;
+  }
+  .err {
+    color: #a61717;
+    background-color: #e3d2d2;
+  }
+  .k, .o {
+    font-weight: bold;
+  }
+  .cm {
+    color: #999988;
+    font-style: italic;
+  }
+  .cp {
+    color: #999999;
+    font-weight: bold;
+  }
+  .c1 {
+    color: #999988;
+    font-style: italic;
+  }
+  .cs {
+    color: #999999;
+    font-weight: bold;
+    font-style: italic;
+  }
+  .gd {
+    color: #000000;
+    background-color: #ffdddd;
+    .x {
+      color: #000000;
+      background-color: #ffaaaa;
+    }
+  }
+  .ge {
+    font-style: italic;
+  }
+  .gr {
+    color: #aa0000;
+  }
+  .gh {
+    color: #999999;
+  }
+  .gi {
+    color: #000000;
+    background-color: #ddffdd;
+    .x {
+      color: #000000;
+      background-color: #aaffaa;
+    }
+  }
+  .go {
+    color: #888888;
+  }
+  .gp {
+    color: #555555;
+  }
+  .gs {
+    font-weight: bold;
+  }
+  .gu {
+    color: #800080;
+    font-weight: bold;
+  }
+  .gt {
+    color: #aa0000;
+  }
+  .kc, .kd, .kn, .kp, .kr {
+    font-weight: bold;
+  }
+  .kt {
+    color: #445588;
+    font-weight: bold;
+  }
+  .m {
+    color: #009999;
+  }
+  .s {
+    color: #dd1144;
+  }
+  .n {
+    color: #333333;
+  }
+  .na {
+    color: teal;
+  }
+  .nb {
+    color: #0086b3;
+  }
+  .nc {
+    color: #445588;
+    font-weight: bold;
+  }
+  .no {
+    color: teal;
+  }
+  .ni {
+    color: purple;
+  }
+  .ne, .nf {
+    color: #990000;
+    font-weight: bold;
+  }
+  .nn {
+    color: #555555;
+  }
+  .nt {
+    color: navy;
+  }
+  .nv {
+    color: teal;
+  }
+  .ow {
+    font-weight: bold;
+  }
+  .w {
+    color: #bbbbbb;
+  }
+  .mf, .mh, .mi, .mo {
+    color: #009999;
+  }
+  .sb, .sc, .sd, .s2, .se, .sh, .si, .sx {
+    color: #dd1144;
+  }
+  .sr {
+    color: #009926;
+  }
+  .s1 {
+    color: #dd1144;
+  }
+  .ss {
+    color: #990073;
+  }
+  .bp {
+    color: #999999;
+  }
+  .vc, .vg, .vi {
+    color: teal;
+  }
+  .il {
+    color: #009999;
+  }
+  .gc {
+    color: #999;
+    background-color: #EAF2F5;
+  }
+}
diff --git a/site/_sass/osqp/_layout.scss b/site/_sass/osqp/_layout.scss
new file mode 100644
index 0000000..3c12032
--- /dev/null
+++ b/site/_sass/osqp/_layout.scss
@@ -0,0 +1,128 @@
+// Intro jumbotron
+.head-intro {
+ // background: url(../images/home_header.jpg) center;
+ // background: black;
+ // background-size: cover;
+ // height: 500px;
+// width: 100%;
+height: 100vh;
+ color: white;
+ // text-shadow: 0px 2px 3px #555;
+  // -webkit-background-size: 100% 100%;           [> Safari 3.0 <]
+  //    -moz-background-size: 100% 100%;           [> Gecko 1.9.2 (Firefox 3.6) <]
+  //      -o-background-size: 100% 100%;           [> Opera 9.5 <]
+  //         background-size: 100% 100%;           [> Gecko 2.0 (Firefox 4.0) and other CSS3-compliant browsers <]
+
+}
+
+
+
+/* Sticky footer styles
+-------------------------------------------------- */
+body {
+  min-height: 100vh;
+}
+
+.flex-grow {
+   flex: 1;
+}
+
+
+.footer {
+	margin-top: 5rem;
+	a {
+		color: $secondary;
+	}
+}
+
+.navbar-brand .img-fluid {
+    height: 40px;
+}
+
+.nav-item .img-fluid {
+    height: 40px;
+}
+
+
+// Footer copyright
+.copyright{
+    font-size: .7rem;
+    text-align: right;
+    a, a:visited {
+	color: $secondary;
+	text-decoration: none;
+    }
+    a:hover{
+	color: $dark;
+	text-decoration: none;
+    }
+}
+
+// Features
+// The board
+.home-section{
+	padding-top: 5rem;
+	padding-top: 5rem;
+	text-align: center;
+	@include media-breakpoint-up(md) {
+	p {
+		font-size:.9rem;
+	}
+	}
+}
+
+/* Featurettes
+------------------------- */
+
+.featurette-divider {
+  margin: 5rem 0; /* Space out the Bootstrap <hr> more */
+}
+
+.featurette {
+text-align: left;
+}
+
+/* Thin out the marketing headings */
+.featurette-heading {
+  font-weight: 300;
+  line-height: 1;
+  letter-spacing: -.05rem;
+}
+
+/* Slider */
+// .slider_block {
+//         display: inline-block;
+//         height: 100%;
+//         vertical-align: middle;
+//         margin: auto;
+// }
+
+.slider img {
+	display:block;
+	margin: auto;
+	height: 80px;
+//         @include media-breakpoint-up(md) {
+//         height: 100px;
+// }
+	@include media-breakpoint-up(lg) {
+	height: 100px;
+}
+	//   background: #3A6F9A;
+	// vertical-align: middle;
+	// max-height: 100px;
+	// max-width: 180px;// margin:auto;
+    -webkit-filter: grayscale(100%);
+    -moz-filter:    grayscale(100%);
+    -ms-filter:     grayscale(100%);
+    -o-filter:      grayscale(100%);
+}
+
+#particles-js{
+	background: #224873;
+	z-index: -10;
+	position: absolute;
+	top: 0;
+	left: 0;
+	width:100%;
+	height: 100vh;
+}
diff --git a/site/_sass/osqp/_variables.scss b/site/_sass/osqp/_variables.scss
new file mode 100644
index 0000000..9f317c9
--- /dev/null
+++ b/site/_sass/osqp/_variables.scss
@@ -0,0 +1,12 @@
+// Change Bootstrap colors
+$theme-colors: (
+  "dark": rgb(0, 32, 72),
+  "primary": rgb(54, 133, 202),
+  // "light": rgb(240, 238, 231),
+  // "light": rgb(217, 214, 208),
+  );
+
+// Fonts
+$font-family-sans-serif: Open Sans, Helvetica, sans-serif !default;
+
+
diff --git a/site/assets/css/osqp.scss b/site/assets/css/osqp.scss
new file mode 100644
index 0000000..bcdf9d4
--- /dev/null
+++ b/site/assets/css/osqp.scss
@@ -0,0 +1,6 @@
+---
+# Only the main Sass file needs front matter (the dashes are enough)
+---
+
+@import "osqp";
+// @import "bootstrap";
diff --git a/site/assets/images/benchmarks/performance_profiles.jpg b/site/assets/images/benchmarks/performance_profiles.jpg
new file mode 100644
index 0000000..2b6af4f
--- /dev/null
+++ b/site/assets/images/benchmarks/performance_profiles.jpg
Binary files differ
diff --git a/site/assets/images/credits/alberto_bemporad.jpg b/site/assets/images/credits/alberto_bemporad.jpg
new file mode 100644
index 0000000..d086619
--- /dev/null
+++ b/site/assets/images/credits/alberto_bemporad.jpg
Binary files differ
diff --git a/site/assets/images/credits/bartolomeo_stellato.jpg b/site/assets/images/credits/bartolomeo_stellato.jpg
new file mode 100644
index 0000000..4b3ee4f
--- /dev/null
+++ b/site/assets/images/credits/bartolomeo_stellato.jpg
Binary files differ
diff --git a/site/assets/images/credits/goran_banjac.jpg b/site/assets/images/credits/goran_banjac.jpg
new file mode 100644
index 0000000..d51592f
--- /dev/null
+++ b/site/assets/images/credits/goran_banjac.jpg
Binary files differ
diff --git a/site/assets/images/credits/john_lygeros.jpg b/site/assets/images/credits/john_lygeros.jpg
new file mode 100644
index 0000000..59508a9
--- /dev/null
+++ b/site/assets/images/credits/john_lygeros.jpg
Binary files differ
diff --git a/site/assets/images/credits/michel_schubiger.jpg b/site/assets/images/credits/michel_schubiger.jpg
new file mode 100644
index 0000000..d898643
--- /dev/null
+++ b/site/assets/images/credits/michel_schubiger.jpg
Binary files differ
diff --git a/site/assets/images/credits/nicholas_moehle.jpg b/site/assets/images/credits/nicholas_moehle.jpg
new file mode 100644
index 0000000..3c0b282
--- /dev/null
+++ b/site/assets/images/credits/nicholas_moehle.jpg
Binary files differ
diff --git a/site/assets/images/credits/paul_goulart.jpg b/site/assets/images/credits/paul_goulart.jpg
new file mode 100644
index 0000000..35476ab
--- /dev/null
+++ b/site/assets/images/credits/paul_goulart.jpg
Binary files differ
diff --git a/site/assets/images/credits/stephen_boyd.jpg b/site/assets/images/credits/stephen_boyd.jpg
new file mode 100644
index 0000000..8fed486
--- /dev/null
+++ b/site/assets/images/credits/stephen_boyd.jpg
Binary files differ
diff --git a/site/assets/images/favicon.ico b/site/assets/images/favicon.ico
new file mode 100644
index 0000000..282b6d1
--- /dev/null
+++ b/site/assets/images/favicon.ico
Binary files differ
diff --git a/site/assets/images/home_header.jpg b/site/assets/images/home_header.jpg
new file mode 100644
index 0000000..665fdd6
--- /dev/null
+++ b/site/assets/images/home_header.jpg
Binary files differ
diff --git a/site/assets/images/logo.png b/site/assets/images/logo.png
new file mode 100644
index 0000000..edbca6e
--- /dev/null
+++ b/site/assets/images/logo.png
Binary files differ
diff --git a/site/assets/images/oxford_rectangle.png b/site/assets/images/oxford_rectangle.png
new file mode 100644
index 0000000..76602f2
--- /dev/null
+++ b/site/assets/images/oxford_rectangle.png
Binary files differ
diff --git a/site/assets/images/users/MIT.jpg b/site/assets/images/users/MIT.jpg
new file mode 100644
index 0000000..2209434
--- /dev/null
+++ b/site/assets/images/users/MIT.jpg
Binary files differ
diff --git a/site/assets/images/users/aptiv.png b/site/assets/images/users/aptiv.png
new file mode 100644
index 0000000..a8b8140
--- /dev/null
+++ b/site/assets/images/users/aptiv.png
Binary files differ
diff --git a/site/assets/images/users/berkeley.jpg b/site/assets/images/users/berkeley.jpg
new file mode 100644
index 0000000..d66102d
--- /dev/null
+++ b/site/assets/images/users/berkeley.jpg
Binary files differ
diff --git a/site/assets/images/users/blackrock.jpg b/site/assets/images/users/blackrock.jpg
new file mode 100644
index 0000000..dbf47ae
--- /dev/null
+++ b/site/assets/images/users/blackrock.jpg
Binary files differ
diff --git a/site/assets/images/users/eth.jpg b/site/assets/images/users/eth.jpg
new file mode 100644
index 0000000..488e285
--- /dev/null
+++ b/site/assets/images/users/eth.jpg
Binary files differ
diff --git a/site/assets/images/users/google.jpg b/site/assets/images/users/google.jpg
new file mode 100644
index 0000000..18f469e
--- /dev/null
+++ b/site/assets/images/users/google.jpg
Binary files differ
diff --git a/site/assets/images/users/iit.png b/site/assets/images/users/iit.png
new file mode 100644
index 0000000..fb1fff6
--- /dev/null
+++ b/site/assets/images/users/iit.png
Binary files differ
diff --git a/site/assets/images/users/kuleuven.png b/site/assets/images/users/kuleuven.png
new file mode 100644
index 0000000..50a8f42
--- /dev/null
+++ b/site/assets/images/users/kuleuven.png
Binary files differ
diff --git a/site/assets/images/users/linkedin.jpg b/site/assets/images/users/linkedin.jpg
new file mode 100644
index 0000000..d8362d0
--- /dev/null
+++ b/site/assets/images/users/linkedin.jpg
Binary files differ
diff --git a/site/assets/images/users/los_alamos.png b/site/assets/images/users/los_alamos.png
new file mode 100644
index 0000000..9f9bb59
--- /dev/null
+++ b/site/assets/images/users/los_alamos.png
Binary files differ
diff --git a/site/assets/images/users/lund.jpg b/site/assets/images/users/lund.jpg
new file mode 100644
index 0000000..ef74d58
--- /dev/null
+++ b/site/assets/images/users/lund.jpg
Binary files differ
diff --git a/site/assets/images/users/lyft.png b/site/assets/images/users/lyft.png
new file mode 100644
index 0000000..53cd88e
--- /dev/null
+++ b/site/assets/images/users/lyft.png
Binary files differ
diff --git a/site/assets/images/users/macquarie.jpg b/site/assets/images/users/macquarie.jpg
new file mode 100644
index 0000000..5f9c99b
--- /dev/null
+++ b/site/assets/images/users/macquarie.jpg
Binary files differ
diff --git a/site/assets/images/users/nyu.jpg b/site/assets/images/users/nyu.jpg
new file mode 100644
index 0000000..aea5447
--- /dev/null
+++ b/site/assets/images/users/nyu.jpg
Binary files differ
diff --git a/site/assets/images/users/seervision.png b/site/assets/images/users/seervision.png
new file mode 100644
index 0000000..b061d1c
--- /dev/null
+++ b/site/assets/images/users/seervision.png
Binary files differ
diff --git a/site/assets/images/users/siemens.png b/site/assets/images/users/siemens.png
new file mode 100644
index 0000000..3f2b35c
--- /dev/null
+++ b/site/assets/images/users/siemens.png
Binary files differ
diff --git a/site/assets/images/users/stanford.png b/site/assets/images/users/stanford.png
new file mode 100644
index 0000000..e53df74
--- /dev/null
+++ b/site/assets/images/users/stanford.png
Binary files differ
diff --git a/site/assets/images/users/stuttgart.png b/site/assets/images/users/stuttgart.png
new file mode 100644
index 0000000..ce06fe0
--- /dev/null
+++ b/site/assets/images/users/stuttgart.png
Binary files differ
diff --git a/site/assets/images/users/toyota.jpg b/site/assets/images/users/toyota.jpg
new file mode 100644
index 0000000..7e841be
--- /dev/null
+++ b/site/assets/images/users/toyota.jpg
Binary files differ
diff --git a/site/assets/images/users/ucdavis.png b/site/assets/images/users/ucdavis.png
new file mode 100644
index 0000000..f620aca
--- /dev/null
+++ b/site/assets/images/users/ucdavis.png
Binary files differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..02bf7f1
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Add the OSQP sources
+set(
+    osqp_src
+    "${CMAKE_CURRENT_SOURCE_DIR}/auxil.c"
+    "${CMAKE_CURRENT_SOURCE_DIR}/error.c"
+    "${CMAKE_CURRENT_SOURCE_DIR}/lin_alg.c"
+    "${CMAKE_CURRENT_SOURCE_DIR}/osqp.c"
+    "${CMAKE_CURRENT_SOURCE_DIR}/proj.c"
+    "${CMAKE_CURRENT_SOURCE_DIR}/scaling.c"
+    "${CMAKE_CURRENT_SOURCE_DIR}/util.c"
+)
+
+# Add the KKT update only in normal mode and matrix-updating embedded mode (not mode 1)
+if (NOT (EMBEDDED EQUAL 1))
+    list(
+      APPEND
+      osqp_src
+      "${CMAKE_CURRENT_SOURCE_DIR}/kkt.c"
+    )
+endif()
+
+# Add more files that should only be in non-embedded code
+if (NOT DEFINED EMBEDDED)
+    list(
+      APPEND
+      osqp_src
+      "${CMAKE_CURRENT_SOURCE_DIR}/cs.c"
+      "${CMAKE_CURRENT_SOURCE_DIR}/polish.c"
+      "${CMAKE_CURRENT_SOURCE_DIR}/lin_sys.c"
+    )
+endif()
+
+# Add the ctrl-c handler if enabled
+if (CTRLC)
+    list(
+      APPEND
+      osqp_src
+      "${CMAKE_CURRENT_SOURCE_DIR}/ctrlc.c"
+    )
+endif()
+
+# Pass the source list up to the main CMakeLists scope
+set(
+  osqp_src
+  "${osqp_src}"
+  PARENT_SCOPE
+)
diff --git a/src/auxil.c b/src/auxil.c
new file mode 100644
index 0000000..89c7de7
--- /dev/null
+++ b/src/auxil.c
@@ -0,0 +1,1067 @@
+#include "osqp.h" // For OSQP rho update
+#include "auxil.h"
+#include "proj.h"
+#include "lin_alg.h"
+#include "constants.h"
+#include "scaling.h"
+#include "util.h"
+
+/***********************************************************
+* Auxiliary functions needed to compute ADMM iterations * *
+***********************************************************/
+#if EMBEDDED != 1
+c_float compute_rho_estimate(OSQPWorkspace *work) {
+  c_int   n, m;                       // Dimensions
+  c_float pri_res, dua_res;           // Primal and dual residuals
+  c_float pri_res_norm, dua_res_norm; // Normalization for the residuals
+  c_float temp_res_norm;              // Temporary residual norm
+  c_float rho_estimate;               // Rho estimate value
+
+  // Get problem dimensions
+  n = work->data->n;
+  m = work->data->m;
+
+  // Get primal and dual residuals
+  pri_res = vec_norm_inf(work->z_prev, m);
+  dua_res = vec_norm_inf(work->x_prev, n);
+
+  // Normalize primal residual
+  pri_res_norm  = vec_norm_inf(work->z, m);           // ||z||
+  temp_res_norm = vec_norm_inf(work->Ax, m);          // ||Ax||
+  pri_res_norm  = c_max(pri_res_norm, temp_res_norm); // max (||z||,||Ax||)
+  pri_res      /= (pri_res_norm + OSQP_DIVISION_TOL); // Normalize primal
+                                                      // residual (prevent 0
+                                                      // division)
+
+  // Normalize dual residual
+  dua_res_norm  = vec_norm_inf(work->data->q, n);     // ||q||
+  temp_res_norm = vec_norm_inf(work->Aty, n);         // ||A' y||
+  dua_res_norm  = c_max(dua_res_norm, temp_res_norm);
+  temp_res_norm = vec_norm_inf(work->Px, n);          // ||P x||
+  dua_res_norm  = c_max(dua_res_norm, temp_res_norm); // max(||q||,||A' y||,||P
+                                                      // x||)
+  dua_res      /= (dua_res_norm + OSQP_DIVISION_TOL); // Normalize dual residual
+                                                      // (prevent 0 division)
+
+
+  // Return rho estimate
+  rho_estimate = work->settings->rho * c_sqrt(pri_res / dua_res);
+  rho_estimate = c_min(c_max(rho_estimate, RHO_MIN), RHO_MAX);     // Constrain
+                                                                   // rho values
+  return rho_estimate;
+}
+
+c_int adapt_rho(OSQPWorkspace *work) {
+  c_int   exitflag; // Exitflag
+  c_float rho_new;  // New rho value
+
+  exitflag = 0;     // Initialize exitflag to 0
+
+  // Compute new rho
+  rho_new = compute_rho_estimate(work);
+
+  // Set rho estimate in info
+  work->info->rho_estimate = rho_new;
+
+  // Check if the new rho is large or small enough and update it in case
+  if ((rho_new > work->settings->rho * work->settings->adaptive_rho_tolerance) ||
+      (rho_new < work->settings->rho /  work->settings->adaptive_rho_tolerance)) {
+    exitflag                 = osqp_update_rho(work, rho_new);
+    work->info->rho_updates += 1;
+  }
+
+  return exitflag;
+}
+
+void set_rho_vec(OSQPWorkspace *work) {
+  c_int i;
+
+  work->settings->rho = c_min(c_max(work->settings->rho, RHO_MIN), RHO_MAX);
+
+  for (i = 0; i < work->data->m; i++) {
+    if ((work->data->l[i] < -OSQP_INFTY * MIN_SCALING) &&
+        (work->data->u[i] > OSQP_INFTY * MIN_SCALING)) {
+      // Loose bounds
+      work->constr_type[i] = -1;
+      work->rho_vec[i]     = RHO_MIN;
+    } else if (work->data->u[i] - work->data->l[i] < RHO_TOL) {
+      // Equality constraints
+      work->constr_type[i] = 1;
+      work->rho_vec[i]     = RHO_EQ_OVER_RHO_INEQ * work->settings->rho;
+    } else {
+      // Inequality constraints
+      work->constr_type[i] = 0;
+      work->rho_vec[i]     = work->settings->rho;
+    }
+    work->rho_inv_vec[i] = 1. / work->rho_vec[i];
+  }
+}
+
+c_int update_rho_vec(OSQPWorkspace *work) {
+  c_int i, exitflag, constr_type_changed;
+
+  exitflag            = 0;
+  constr_type_changed = 0;
+
+  for (i = 0; i < work->data->m; i++) {
+    if ((work->data->l[i] < -OSQP_INFTY * MIN_SCALING) &&
+        (work->data->u[i] > OSQP_INFTY * MIN_SCALING)) {
+      // Loose bounds
+      if (work->constr_type[i] != -1) {
+        work->constr_type[i] = -1;
+        work->rho_vec[i]     = RHO_MIN;
+        work->rho_inv_vec[i] = 1. / RHO_MIN;
+        constr_type_changed  = 1;
+      }
+    } else if (work->data->u[i] - work->data->l[i] < RHO_TOL) {
+      // Equality constraints
+      if (work->constr_type[i] != 1) {
+        work->constr_type[i] = 1;
+        work->rho_vec[i]     = RHO_EQ_OVER_RHO_INEQ * work->settings->rho;
+        work->rho_inv_vec[i] = 1. / work->rho_vec[i];
+        constr_type_changed  = 1;
+      }
+    } else {
+      // Inequality constraints
+      if (work->constr_type[i] != 0) {
+        work->constr_type[i] = 0;
+        work->rho_vec[i]     = work->settings->rho;
+        work->rho_inv_vec[i] = 1. / work->settings->rho;
+        constr_type_changed  = 1;
+      }
+    }
+  }
+
+  // Update rho_vec in KKT matrix if constraints type has changed
+  if (constr_type_changed == 1) {
+    exitflag = work->linsys_solver->update_rho_vec(work->linsys_solver,
+                                                   work->rho_vec);
+  }
+
+  return exitflag;
+}
+
+#endif // EMBEDDED != 1
+
+
+void swap_vectors(c_float **a, c_float **b) {
+  c_float *temp;
+
+  temp = *b;
+  *b   = *a;
+  *a   = temp;
+}
+
+void cold_start(OSQPWorkspace *work) {
+  vec_set_scalar(work->x, 0., work->data->n);
+  vec_set_scalar(work->z, 0., work->data->m);
+  vec_set_scalar(work->y, 0., work->data->m);
+}
+
+static void compute_rhs(OSQPWorkspace *work) {
+  c_int i; // Index
+
+  for (i = 0; i < work->data->n; i++) {
+    // Cycle over part related to x variables
+    work->xz_tilde[i] = work->settings->sigma * work->x_prev[i] -
+                        work->data->q[i];
+  }
+
+  for (i = 0; i < work->data->m; i++) {
+    // Cycle over dual variable in the first step (nu)
+    work->xz_tilde[i + work->data->n] = work->z_prev[i] - work->rho_inv_vec[i] *
+                                        work->y[i];
+  }
+}
+
+void update_xz_tilde(OSQPWorkspace *work) {
+  // Compute right-hand side
+  compute_rhs(work);
+
+  // Solve linear system
+  work->linsys_solver->solve(work->linsys_solver, work->xz_tilde);
+}
+
+void update_x(OSQPWorkspace *work) {
+  c_int i;
+
+  // update x
+  for (i = 0; i < work->data->n; i++) {
+    work->x[i] = work->settings->alpha * work->xz_tilde[i] +
+                 ((c_float)1.0 - work->settings->alpha) * work->x_prev[i];
+  }
+
+  // update delta_x
+  for (i = 0; i < work->data->n; i++) {
+    work->delta_x[i] = work->x[i] - work->x_prev[i];
+  }
+}
+
+void update_z(OSQPWorkspace *work) {
+  c_int i;
+
+  // update z
+  for (i = 0; i < work->data->m; i++) {
+    work->z[i] = work->settings->alpha * work->xz_tilde[i + work->data->n] +
+                 ((c_float)1.0 - work->settings->alpha) * work->z_prev[i] +
+                 work->rho_inv_vec[i] * work->y[i];
+  }
+
+  // project z
+  project(work, work->z);
+}
+
+void update_y(OSQPWorkspace *work) {
+  c_int i; // Index
+
+  for (i = 0; i < work->data->m; i++) {
+    work->delta_y[i] = work->rho_vec[i] *
+                       (work->settings->alpha *
+                        work->xz_tilde[i + work->data->n] +
+                        ((c_float)1.0 - work->settings->alpha) * work->z_prev[i] -
+                        work->z[i]);
+    work->y[i] += work->delta_y[i];
+  }
+}
+
+c_float compute_obj_val(OSQPWorkspace *work, c_float *x) {
+  c_float obj_val;
+
+  obj_val = quad_form(work->data->P, x) +
+            vec_prod(work->data->q, x, work->data->n);
+
+  if (work->settings->scaling) {
+    obj_val *= work->scaling->cinv;
+  }
+
+  return obj_val;
+}
+
+c_float compute_pri_res(OSQPWorkspace *work, c_float *x, c_float *z) {
+  // NB: Use z_prev as working vector
+  // pr = Ax - z
+
+  mat_vec(work->data->A, x, work->Ax, 0); // Ax
+  vec_add_scaled(work->z_prev, work->Ax, z, work->data->m, -1);
+
+  // If scaling active -> rescale residual
+  if (work->settings->scaling && !work->settings->scaled_termination) {
+    return vec_scaled_norm_inf(work->scaling->Einv, work->z_prev, work->data->m);
+  }
+
+  // Return norm of the residual
+  return vec_norm_inf(work->z_prev, work->data->m);
+}
+
+c_float compute_pri_tol(OSQPWorkspace *work, c_float eps_abs, c_float eps_rel) {
+  c_float max_rel_eps, temp_rel_eps;
+
+  // max_rel_eps = max(||z||, ||A x||)
+  if (work->settings->scaling && !work->settings->scaled_termination) {
+    // ||Einv * z||
+    max_rel_eps =
+      vec_scaled_norm_inf(work->scaling->Einv, work->z, work->data->m);
+
+    // ||Einv * A * x||
+    temp_rel_eps = vec_scaled_norm_inf(work->scaling->Einv,
+                                       work->Ax,
+                                       work->data->m);
+
+    // Choose maximum
+    max_rel_eps = c_max(max_rel_eps, temp_rel_eps);
+  } else { // No unscaling required
+    // ||z||
+    max_rel_eps = vec_norm_inf(work->z, work->data->m);
+
+    // ||A * x||
+    temp_rel_eps = vec_norm_inf(work->Ax, work->data->m);
+
+    // Choose maximum
+    max_rel_eps = c_max(max_rel_eps, temp_rel_eps);
+  }
+
+  // eps_prim
+  return eps_abs + eps_rel * max_rel_eps;
+}
+
+c_float compute_dua_res(OSQPWorkspace *work, c_float *x, c_float *y) {
+  // NB: Use x_prev as temporary vector
+  // NB: Only upper triangular part of P is stored.
+  // dr = q + A'*y + P*x
+
+  // dr = q
+  prea_vec_copy(work->data->q, work->x_prev, work->data->n);
+
+  // P * x (upper triangular part)
+  mat_vec(work->data->P, x, work->Px, 0);
+
+  // P' * x (lower triangular part with no diagonal)
+  mat_tpose_vec(work->data->P, x, work->Px, 1, 1);
+
+  // dr += P * x (full P matrix)
+  vec_add_scaled(work->x_prev, work->x_prev, work->Px, work->data->n, 1);
+
+  // dr += A' * y
+  if (work->data->m > 0) {
+    mat_tpose_vec(work->data->A, y, work->Aty, 0, 0);
+    vec_add_scaled(work->x_prev, work->x_prev, work->Aty, work->data->n, 1);
+  }
+
+  // If scaling active -> rescale residual
+  if (work->settings->scaling && !work->settings->scaled_termination) {
+    return work->scaling->cinv * vec_scaled_norm_inf(work->scaling->Dinv,
+                                                     work->x_prev,
+                                                     work->data->n);
+  }
+
+  return vec_norm_inf(work->x_prev, work->data->n);
+}
+
+c_float compute_dua_tol(OSQPWorkspace *work, c_float eps_abs, c_float eps_rel) {
+  c_float max_rel_eps, temp_rel_eps;
+
+  // max_rel_eps = max(||q||, ||A' y|, ||P x||)
+  if (work->settings->scaling && !work->settings->scaled_termination) {
+    // || Dinv q||
+    max_rel_eps = vec_scaled_norm_inf(work->scaling->Dinv,
+                                      work->data->q,
+                                      work->data->n);
+
+    // || Dinv A' y ||
+    temp_rel_eps = vec_scaled_norm_inf(work->scaling->Dinv,
+                                       work->Aty,
+                                       work->data->n);
+    max_rel_eps = c_max(max_rel_eps, temp_rel_eps);
+
+    // || Dinv P x||
+    temp_rel_eps = vec_scaled_norm_inf(work->scaling->Dinv,
+                                       work->Px,
+                                       work->data->n);
+    max_rel_eps = c_max(max_rel_eps, temp_rel_eps);
+
+    // Multiply by cinv
+    max_rel_eps *= work->scaling->cinv;
+  } else { // No scaling required
+    // ||q||
+    max_rel_eps = vec_norm_inf(work->data->q, work->data->n);
+
+    // ||A'*y||
+    temp_rel_eps = vec_norm_inf(work->Aty, work->data->n);
+    max_rel_eps  = c_max(max_rel_eps, temp_rel_eps);
+
+    // ||P*x||
+    temp_rel_eps = vec_norm_inf(work->Px, work->data->n);
+    max_rel_eps  = c_max(max_rel_eps, temp_rel_eps);
+  }
+
+  // eps_dual
+  return eps_abs + eps_rel * max_rel_eps;
+}
+
+c_int is_primal_infeasible(OSQPWorkspace *work, c_float eps_prim_inf) {
+  // This function checks for the primal infeasibility termination criteria.
+  //
+  // 1) A' * delta_y < eps * ||delta_y||
+  //
+  // 2) u'*max(delta_y, 0) + l'*min(delta_y, 0) < eps * ||delta_y||
+  //
+
+  c_int i; // Index for loops
+  c_float norm_delta_y;
+  c_float ineq_lhs = 0.0;
+
+  // Project delta_y onto the polar of the recession cone of [l,u]
+  for (i = 0; i < work->data->m; i++) {
+    if (work->data->u[i] > OSQP_INFTY * MIN_SCALING) {          // Infinite upper bound
+      if (work->data->l[i] < -OSQP_INFTY * MIN_SCALING) {       // Infinite lower bound
+        // Both bounds infinite
+        work->delta_y[i] = 0.0;
+      } else {
+        // Only upper bound infinite
+        work->delta_y[i] = c_min(work->delta_y[i], 0.0);
+      }
+    } else if (work->data->l[i] < -OSQP_INFTY * MIN_SCALING) {  // Infinite lower bound
+      // Only lower bound infinite
+      work->delta_y[i] = c_max(work->delta_y[i], 0.0);
+    }
+  }
+
+  // Compute infinity norm of delta_y (unscale if necessary)
+  if (work->settings->scaling && !work->settings->scaled_termination) {
+    // Use work->Adelta_x as temporary vector
+    vec_ew_prod(work->scaling->E, work->delta_y, work->Adelta_x, work->data->m);
+    norm_delta_y = vec_norm_inf(work->Adelta_x, work->data->m);
+  } else {
+    norm_delta_y = vec_norm_inf(work->delta_y, work->data->m);
+  }
+
+  if (norm_delta_y > OSQP_DIVISION_TOL) {
+
+    for (i = 0; i < work->data->m; i++) {
+      ineq_lhs += work->data->u[i] * c_max(work->delta_y[i], 0) + \
+                  work->data->l[i] * c_min(work->delta_y[i], 0);
+    }
+
+    // Check if the condition is satisfied: ineq_lhs < -eps
+    if (ineq_lhs < eps_prim_inf * norm_delta_y) {
+      // Compute and return ||A'delta_y|| < eps_prim_inf
+      mat_tpose_vec(work->data->A, work->delta_y, work->Atdelta_y, 0, 0);
+
+      // Unscale if necessary
+      if (work->settings->scaling && !work->settings->scaled_termination) {
+        vec_ew_prod(work->scaling->Dinv,
+                    work->Atdelta_y,
+                    work->Atdelta_y,
+                    work->data->n);
+      }
+
+      return vec_norm_inf(work->Atdelta_y, work->data->n) < eps_prim_inf * norm_delta_y;
+    }
+  }
+
+  // Conditions not satisfied -> not primal infeasible
+  return 0;
+}
+
+c_int is_dual_infeasible(OSQPWorkspace *work, c_float eps_dual_inf) {
+  // This function checks for the scaled dual infeasibility termination
+  // criteria.
+  //
+  // 1) q * delta_x < eps * || delta_x ||
+  //
+  // 2) ||P * delta_x || < eps * || delta_x ||
+  //
+  // 3) -> (A * delta_x)_i > -eps * || delta_x ||,    l_i != -inf
+  //    -> (A * delta_x)_i <  eps * || delta_x ||,    u_i != inf
+  //
+
+
+  c_int   i; // Index for loops
+  c_float norm_delta_x;
+  c_float cost_scaling;
+
+  // Compute norm of delta_x
+  if (work->settings->scaling && !work->settings->scaled_termination) { // Unscale
+                                                                        // if
+                                                                        // necessary
+    norm_delta_x = vec_scaled_norm_inf(work->scaling->D,
+                                       work->delta_x,
+                                       work->data->n);
+    cost_scaling = work->scaling->c;
+  } else {
+    norm_delta_x = vec_norm_inf(work->delta_x, work->data->n);
+    cost_scaling = 1.0;
+  }
+
+  // Prevent 0 division || delta_x || > 0
+  if (norm_delta_x > OSQP_DIVISION_TOL) {
+    // Normalize delta_x by its norm
+
+    /* vec_mult_scalar(work->delta_x, 1./norm_delta_x, work->data->n); */
+
+    // Check first if q'*delta_x < 0
+    if (vec_prod(work->data->q, work->delta_x, work->data->n) <
+        cost_scaling * eps_dual_inf * norm_delta_x) {
+      // Compute product P * delta_x (NB: P is store in upper triangular form)
+      mat_vec(work->data->P, work->delta_x, work->Pdelta_x, 0);
+      mat_tpose_vec(work->data->P, work->delta_x, work->Pdelta_x, 1, 1);
+
+      // Scale if necessary
+      if (work->settings->scaling && !work->settings->scaled_termination) {
+        vec_ew_prod(work->scaling->Dinv,
+                    work->Pdelta_x,
+                    work->Pdelta_x,
+                    work->data->n);
+      }
+
+      // Check if || P * delta_x || = 0
+      if (vec_norm_inf(work->Pdelta_x, work->data->n) <
+          cost_scaling * eps_dual_inf * norm_delta_x) {
+        // Compute A * delta_x
+        mat_vec(work->data->A, work->delta_x, work->Adelta_x, 0);
+
+        // Scale if necessary
+        if (work->settings->scaling && !work->settings->scaled_termination) {
+          vec_ew_prod(work->scaling->Einv,
+                      work->Adelta_x,
+                      work->Adelta_x,
+                      work->data->m);
+        }
+
+        // De Morgan Law Applied to dual infeasibility conditions for A * x
+        // NB: Note that MIN_SCALING is used to adjust the infinity value
+        //     in case the problem is scaled.
+        for (i = 0; i < work->data->m; i++) {
+          if (((work->data->u[i] < OSQP_INFTY * MIN_SCALING) &&
+               (work->Adelta_x[i] >  eps_dual_inf * norm_delta_x)) ||
+              ((work->data->l[i] > -OSQP_INFTY * MIN_SCALING) &&
+               (work->Adelta_x[i] < -eps_dual_inf * norm_delta_x))) {
+            // At least one condition not satisfied -> not dual infeasible
+            return 0;
+          }
+        }
+
+        // All conditions passed -> dual infeasible
+        return 1;
+      }
+    }
+  }
+
+  // Conditions not satisfied -> not dual infeasible
+  return 0;
+}
+
+c_int has_solution(OSQPInfo * info){
+
+  return ((info->status_val != OSQP_PRIMAL_INFEASIBLE) &&
+      (info->status_val != OSQP_PRIMAL_INFEASIBLE_INACCURATE) &&
+      (info->status_val != OSQP_DUAL_INFEASIBLE) &&
+      (info->status_val != OSQP_DUAL_INFEASIBLE_INACCURATE) &&
+      (info->status_val != OSQP_NON_CVX));
+
+}
+
+void store_solution(OSQPWorkspace *work) {
+#ifndef EMBEDDED
+  c_float norm_vec;
+#endif /* ifndef EMBEDDED */
+
+  if (has_solution(work->info)) {
+    prea_vec_copy(work->x, work->solution->x, work->data->n); // primal
+    prea_vec_copy(work->y, work->solution->y, work->data->m); // dual
+
+    // Unscale solution if scaling has been performed
+    if (work->settings->scaling)
+      unscale_solution(work);
+  } else {
+    // No solution present. Solution is NaN
+    vec_set_scalar(work->solution->x, OSQP_NAN, work->data->n);
+    vec_set_scalar(work->solution->y, OSQP_NAN, work->data->m);
+
+#ifndef EMBEDDED
+
+    // Normalize infeasibility certificates if embedded is off
+    // NB: It requires a division
+    if ((work->info->status_val == OSQP_PRIMAL_INFEASIBLE) ||
+        ((work->info->status_val == OSQP_PRIMAL_INFEASIBLE_INACCURATE))) {
+      norm_vec = vec_norm_inf(work->delta_y, work->data->m);
+      vec_mult_scalar(work->delta_y, 1. / norm_vec, work->data->m);
+    }
+
+    if ((work->info->status_val == OSQP_DUAL_INFEASIBLE) ||
+        ((work->info->status_val == OSQP_DUAL_INFEASIBLE_INACCURATE))) {
+      norm_vec = vec_norm_inf(work->delta_x, work->data->n);
+      vec_mult_scalar(work->delta_x, 1. / norm_vec, work->data->n);
+    }
+
+#endif /* ifndef EMBEDDED */
+
+    // Cold start iterates to 0 for next runs (they cannot start from NaN)
+    cold_start(work);
+  }
+}
+
+void update_info(OSQPWorkspace *work,
+                 c_int          iter,
+                 c_int          compute_objective,
+                 c_int          polish) {
+  c_float *x, *z, *y;                   // Allocate pointers to variables
+  c_float *obj_val, *pri_res, *dua_res; // objective value, residuals
+
+#ifdef PROFILING
+  c_float *run_time;                    // Execution time
+#endif /* ifdef PROFILING */
+
+#ifndef EMBEDDED
+
+  if (polish) {
+    x       = work->pol->x;
+    y       = work->pol->y;
+    z       = work->pol->z;
+    obj_val = &work->pol->obj_val;
+    pri_res = &work->pol->pri_res;
+    dua_res = &work->pol->dua_res;
+# ifdef PROFILING
+    run_time = &work->info->polish_time;
+# endif /* ifdef PROFILING */
+  } else {
+#endif // EMBEDDED
+  x                = work->x;
+  y                = work->y;
+  z                = work->z;
+  obj_val          = &work->info->obj_val;
+  pri_res          = &work->info->pri_res;
+  dua_res          = &work->info->dua_res;
+  work->info->iter = iter; // Update iteration number
+#ifdef PROFILING
+  run_time = &work->info->solve_time;
+#endif /* ifdef PROFILING */
+#ifndef EMBEDDED
+}
+
+#endif /* ifndef EMBEDDED */
+
+
+  // Compute the objective if needed
+  if (compute_objective) {
+    *obj_val = compute_obj_val(work, x);
+  }
+
+  // Compute primal residual
+  if (work->data->m == 0) {
+    // No constraints -> Always primal feasible
+    *pri_res = 0.;
+  } else {
+    *pri_res = compute_pri_res(work, x, z);
+  }
+
+  // Compute dual residual
+  *dua_res = compute_dua_res(work, x, y);
+
+  // Update timing
+#ifdef PROFILING
+  *run_time = osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+#ifdef PRINTING
+  work->summary_printed = 0; // The just updated info have not been printed
+#endif /* ifdef PRINTING */
+}
+
+
+void reset_info(OSQPInfo *info) {
+#ifdef PROFILING
+
+  // Initialize info values.
+  info->solve_time = 0.0;  // Solve time to zero
+# ifndef EMBEDDED
+  info->polish_time = 0.0; // Polish time to zero
+# endif /* ifndef EMBEDDED */
+
+  // NB: We do not reset the setup_time because it is performed only once
+#endif /* ifdef PROFILING */
+
+  update_status(info, OSQP_UNSOLVED); // Problem is unsolved
+
+#if EMBEDDED != 1
+  info->rho_updates = 0;              // Rho updates are now 0
+#endif /* if EMBEDDED != 1 */
+}
+
+void update_status(OSQPInfo *info, c_int status_val) {
+  // Update status value
+  info->status_val = status_val;
+
+  // Update status string depending on status val
+  if (status_val == OSQP_SOLVED) c_strcpy(info->status, "solved");
+
+  if (status_val == OSQP_SOLVED_INACCURATE) c_strcpy(info->status,
+                                                     "solved inaccurate");
+  else if (status_val == OSQP_PRIMAL_INFEASIBLE) c_strcpy(info->status,
+                                                          "primal infeasible");
+  else if (status_val == OSQP_PRIMAL_INFEASIBLE_INACCURATE) c_strcpy(info->status,
+                                                                     "primal infeasible inaccurate");
+  else if (status_val == OSQP_UNSOLVED) c_strcpy(info->status, "unsolved");
+  else if (status_val == OSQP_DUAL_INFEASIBLE) c_strcpy(info->status,
+                                                        "dual infeasible");
+  else if (status_val == OSQP_DUAL_INFEASIBLE_INACCURATE) c_strcpy(info->status,
+                                                                   "dual infeasible inaccurate");
+  else if (status_val == OSQP_MAX_ITER_REACHED) c_strcpy(info->status,
+                                                         "maximum iterations reached");
+#ifdef PROFILING
+  else if (status_val == OSQP_TIME_LIMIT_REACHED) c_strcpy(info->status,
+                                                           "run time limit reached");
+#endif /* ifdef PROFILING */
+  else if (status_val == OSQP_SIGINT) c_strcpy(info->status, "interrupted");
+
+  else if (status_val == OSQP_NON_CVX) c_strcpy(info->status, "problem non convex");
+
+}
+
+c_int check_termination(OSQPWorkspace *work, c_int approximate) {
+  c_float eps_prim, eps_dual, eps_prim_inf, eps_dual_inf;
+  c_int   exitflag;
+  c_int   prim_res_check, dual_res_check, prim_inf_check, dual_inf_check;
+  c_float eps_abs, eps_rel;
+
+  // Initialize variables to 0
+  exitflag       = 0;
+  prim_res_check = 0; dual_res_check = 0;
+  prim_inf_check = 0; dual_inf_check = 0;
+
+  // Initialize tolerances
+  eps_abs      = work->settings->eps_abs;
+  eps_rel      = work->settings->eps_rel;
+  eps_prim_inf = work->settings->eps_prim_inf;
+  eps_dual_inf = work->settings->eps_dual_inf;
+
+  // If residuals are too large, the problem is probably non convex
+  if ((work->info->pri_res > OSQP_INFTY) ||
+      (work->info->dua_res > OSQP_INFTY)){
+    // Looks like residuals are diverging. Probably the problem is non convex!
+    // Terminate and report it
+    update_status(work->info, OSQP_NON_CVX);
+    work->info->obj_val = OSQP_NAN;
+    return 1;
+  }
+
+  // If approximate solution required, increase tolerances by 10
+  if (approximate) {
+    eps_abs      *= 10;
+    eps_rel      *= 10;
+    eps_prim_inf *= 10;
+    eps_dual_inf *= 10;
+  }
+
+  // Check residuals
+  if (work->data->m == 0) {
+    prim_res_check = 1; // No constraints -> Primal feasibility always satisfied
+  }
+  else {
+    // Compute primal tolerance
+    eps_prim = compute_pri_tol(work, eps_abs, eps_rel);
+
+    // Primal feasibility check
+    if (work->info->pri_res < eps_prim) {
+      prim_res_check = 1;
+    } else {
+      // Primal infeasibility check
+      prim_inf_check = is_primal_infeasible(work, eps_prim_inf);
+    }
+  } // End check if m == 0
+
+  // Compute dual tolerance
+  eps_dual = compute_dua_tol(work, eps_abs, eps_rel);
+
+  // Dual feasibility check
+  if (work->info->dua_res < eps_dual) {
+    dual_res_check = 1;
+  } else {
+    // Check dual infeasibility
+    dual_inf_check = is_dual_infeasible(work, eps_dual_inf);
+  }
+
+  // Compare checks to determine solver status
+  if (prim_res_check && dual_res_check) {
+    // Update final information
+    if (approximate) {
+      update_status(work->info, OSQP_SOLVED_INACCURATE);
+    } else {
+      update_status(work->info, OSQP_SOLVED);
+    }
+    exitflag = 1;
+  }
+  else if (prim_inf_check) {
+    // Update final information
+    if (approximate) {
+      update_status(work->info, OSQP_PRIMAL_INFEASIBLE_INACCURATE);
+    } else {
+      update_status(work->info, OSQP_PRIMAL_INFEASIBLE);
+    }
+
+    if (work->settings->scaling && !work->settings->scaled_termination) {
+      // Update infeasibility certificate
+      vec_ew_prod(work->scaling->E, work->delta_y, work->delta_y, work->data->m);
+    }
+    work->info->obj_val = OSQP_INFTY;
+    exitflag            = 1;
+  }
+  else if (dual_inf_check) {
+    // Update final information
+    if (approximate) {
+      update_status(work->info, OSQP_DUAL_INFEASIBLE_INACCURATE);
+    } else {
+      update_status(work->info, OSQP_DUAL_INFEASIBLE);
+    }
+
+    if (work->settings->scaling && !work->settings->scaled_termination) {
+      // Update infeasibility certificate
+      vec_ew_prod(work->scaling->D, work->delta_x, work->delta_x, work->data->n);
+    }
+    work->info->obj_val = -OSQP_INFTY;
+    exitflag            = 1;
+  }
+
+  return exitflag;
+}
+
+
+#ifndef EMBEDDED
+
+c_int validate_data(const OSQPData *data) {
+  c_int j, ptr;
+
+  if (!data) {
+# ifdef PRINTING
+    c_eprint("Missing data");
+# endif
+    return 1;
+  }
+
+  if (!(data->P)) {
+# ifdef PRINTING
+    c_eprint("Missing matrix P");
+# endif
+    return 1;
+  }
+
+  if (!(data->A)) {
+# ifdef PRINTING
+    c_eprint("Missing matrix A");
+# endif
+    return 1;
+  }
+
+  if (!(data->q)) {
+# ifdef PRINTING
+    c_eprint("Missing vector q");
+# endif
+    return 1;
+  }
+
+  // General dimensions Tests
+  if ((data->n <= 0) || (data->m < 0)) {
+# ifdef PRINTING
+    c_eprint("n must be positive and m nonnegative; n = %i, m = %i",
+             (int)data->n, (int)data->m);
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Matrix P
+  if (data->P->m != data->n) {
+# ifdef PRINTING
+    c_eprint("P does not have dimension n x n with n = %i", (int)data->n);
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (data->P->m != data->P->n) {
+# ifdef PRINTING
+    c_eprint("P is not square");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  for (j = 0; j < data->n; j++) { // COLUMN
+    for (ptr = data->P->p[j]; ptr < data->P->p[j + 1]; ptr++) {
+      if (data->P->i[ptr] > j) {  // if ROW > COLUMN
+# ifdef PRINTING
+        c_eprint("P is not upper triangular");
+# endif /* ifdef PRINTING */
+        return 1;
+      }
+    }
+  }
+
+  // Matrix A
+  if ((data->A->m != data->m) || (data->A->n != data->n)) {
+# ifdef PRINTING
+    c_eprint("A does not have dimension %i x %i", (int)data->m, (int)data->n);
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Lower and upper bounds
+  for (j = 0; j < data->m; j++) {
+    if (data->l[j] > data->u[j]) {
+# ifdef PRINTING
+      c_eprint("Lower bound at index %d is greater than upper bound: %.4e > %.4e",
+               (int)j, data->l[j], data->u[j]);
+# endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  // TODO: Complete with other checks
+
+  return 0;
+}
+
+c_int validate_linsys_solver(c_int linsys_solver) {
+  if ((linsys_solver != QDLDL_SOLVER) &&
+      (linsys_solver != MKL_PARDISO_SOLVER)) {
+    return 1;
+  }
+
+  // TODO: Add more solvers in case
+
+  // Valid solver
+  return 0;
+}
+
+c_int validate_settings(const OSQPSettings *settings) {
+  if (!settings) {
+# ifdef PRINTING
+    c_eprint("Missing settings!");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->scaling < 0) {
+# ifdef PRINTING
+    c_eprint("scaling must be nonnegative");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if ((settings->adaptive_rho != 0) && (settings->adaptive_rho != 1)) {
+# ifdef PRINTING
+    c_eprint("adaptive_rho must be either 0 or 1");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->adaptive_rho_interval < 0) {
+# ifdef PRINTING
+    c_eprint("adaptive_rho_interval must be nonnegative");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+# ifdef PROFILING
+
+  if (settings->adaptive_rho_fraction <= 0) {
+#  ifdef PRINTING
+    c_eprint("adaptive_rho_fraction must be positive");
+#  endif /* ifdef PRINTING */
+    return 1;
+  }
+# endif /* ifdef PROFILING */
+
+  if (settings->adaptive_rho_tolerance < 1.0) {
+# ifdef PRINTING
+    c_eprint("adaptive_rho_tolerance must be >= 1");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->polish_refine_iter < 0) {
+# ifdef PRINTING
+    c_eprint("polish_refine_iter must be nonnegative");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->rho <= 0.0) {
+# ifdef PRINTING
+    c_eprint("rho must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->sigma <= 0.0) {
+# ifdef PRINTING
+    c_eprint("sigma must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->delta <= 0.0) {
+# ifdef PRINTING
+    c_eprint("delta must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->max_iter <= 0) {
+# ifdef PRINTING
+    c_eprint("max_iter must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->eps_abs < 0.0) {
+# ifdef PRINTING
+    c_eprint("eps_abs must be nonnegative");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->eps_rel < 0.0) {
+# ifdef PRINTING
+    c_eprint("eps_rel must be nonnegative");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if ((settings->eps_rel == 0.0) &&
+      (settings->eps_abs == 0.0)) {
+# ifdef PRINTING
+    c_eprint("at least one of eps_abs and eps_rel must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->eps_prim_inf <= 0.0) {
+# ifdef PRINTING
+    c_eprint("eps_prim_inf must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->eps_dual_inf <= 0.0) {
+# ifdef PRINTING
+    c_eprint("eps_dual_inf must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if ((settings->alpha <= 0.0) ||
+      (settings->alpha >= 2.0)) {
+# ifdef PRINTING
+    c_eprint("alpha must be strictly between 0 and 2");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (validate_linsys_solver(settings->linsys_solver)) {
+# ifdef PRINTING
+    c_eprint("linsys_solver not recognized");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if ((settings->verbose != 0) &&
+      (settings->verbose != 1)) {
+# ifdef PRINTING
+    c_eprint("verbose must be either 0 or 1");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if ((settings->scaled_termination != 0) &&
+      (settings->scaled_termination != 1)) {
+# ifdef PRINTING
+    c_eprint("scaled_termination must be either 0 or 1");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if (settings->check_termination < 0) {
+# ifdef PRINTING
+    c_eprint("check_termination must be nonnegative");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  if ((settings->warm_start != 0) &&
+      (settings->warm_start != 1)) {
+# ifdef PRINTING
+    c_eprint("warm_start must be either 0 or 1");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+# ifdef PROFILING
+
+  if (settings->time_limit < 0.0) {
+#  ifdef PRINTING
+    c_eprint("time_limit must be nonnegative\n");
+#  endif /* ifdef PRINTING */
+    return 1;
+  }
+# endif /* ifdef PROFILING */
+
+  return 0;
+}
+
+#endif // #ifndef EMBEDDED
diff --git a/src/cs.c b/src/cs.c
new file mode 100644
index 0000000..a678ceb
--- /dev/null
+++ b/src/cs.c
@@ -0,0 +1,318 @@
+#include "cs.h"
+
+
+static void* csc_malloc(c_int n, c_int size) {
+  return c_malloc(n * size);
+}
+
+static void* csc_calloc(c_int n, c_int size) {
+  return c_calloc(n, size);
+}
+
+csc* csc_matrix(c_int m, c_int n, c_int nzmax, c_float *x, c_int *i, c_int *p)
+{
+  csc *M = (csc *)c_malloc(sizeof(csc));
+
+  if (!M) return OSQP_NULL;
+
+  M->m     = m;
+  M->n     = n;
+  M->nz    = -1;
+  M->nzmax = nzmax;
+  M->x     = x;
+  M->i     = i;
+  M->p     = p;
+  return M;
+}
+
+csc* csc_spalloc(c_int m, c_int n, c_int nzmax, c_int values, c_int triplet) {
+  csc *A = csc_calloc(1, sizeof(csc)); /* allocate the csc struct */
+
+  if (!A) return OSQP_NULL;            /* out of memory */
+
+  A->m     = m;                        /* define dimensions and nzmax */
+  A->n     = n;
+  A->nzmax = nzmax = c_max(nzmax, 1);
+  A->nz    = triplet ? 0 : -1;         /* allocate triplet or comp.col */
+  A->p     = csc_malloc(triplet ? nzmax : n + 1, sizeof(c_int));
+  A->i     = csc_malloc(nzmax,  sizeof(c_int));
+  A->x     = values ? csc_malloc(nzmax,  sizeof(c_float)) : OSQP_NULL;
+  if (!A->p || !A->i || (values && !A->x)){
+    csc_spfree(A);
+    return OSQP_NULL;
+  } else return A;
+}
+
+void csc_spfree(csc *A) {
+  if (A){
+    if (A->p) c_free(A->p);
+    if (A->i) c_free(A->i);
+    if (A->x) c_free(A->x);
+    c_free(A);
+  }
+}
+
+csc* triplet_to_csc(const csc *T, c_int *TtoC) {
+  c_int m, n, nz, p, k, *Cp, *Ci, *w, *Ti, *Tj;
+  c_float *Cx, *Tx;
+  csc     *C;
+
+  m  = T->m;
+  n  = T->n;
+  Ti = T->i;
+  Tj = T->p;
+  Tx = T->x;
+  nz = T->nz;
+  C  = csc_spalloc(m, n, nz, Tx != OSQP_NULL, 0);     /* allocate result */
+  w  = csc_calloc(n, sizeof(c_int));                  /* get workspace */
+
+  if (!C || !w) return csc_done(C, w, OSQP_NULL, 0);  /* out of memory */
+
+  Cp = C->p;
+  Ci = C->i;
+  Cx = C->x;
+
+  for (k = 0; k < nz; k++) w[Tj[k]]++;  /* column counts */
+  csc_cumsum(Cp, w, n);                 /* column pointers */
+
+  for (k = 0; k < nz; k++) {
+    Ci[p = w[Tj[k]]++] = Ti[k];         /* A(i,j) is the pth entry in C */
+
+    if (Cx) {
+      Cx[p] = Tx[k];
+
+      if (TtoC != OSQP_NULL) TtoC[k] = p;  // Assign vector of indices
+    }
+  }
+  return csc_done(C, w, OSQP_NULL, 1);     /* success; free w and return C */
+}
+
+csc* triplet_to_csr(const csc *T, c_int *TtoC) {
+  c_int m, n, nz, p, k, *Cp, *Cj, *w, *Ti, *Tj;
+  c_float *Cx, *Tx;
+  csc     *C;
+
+  m  = T->m;
+  n  = T->n;
+  Ti = T->i;
+  Tj = T->p;
+  Tx = T->x;
+  nz = T->nz;
+  C  = csc_spalloc(m, n, nz, Tx != OSQP_NULL, 0);     /* allocate result */
+  w  = csc_calloc(m, sizeof(c_int));                  /* get workspace */
+
+  if (!C || !w) return csc_done(C, w, OSQP_NULL, 0);  /* out of memory */
+
+  Cp = C->p;
+  Cj = C->i;
+  Cx = C->x;
+
+  for (k = 0; k < nz; k++) w[Ti[k]]++;  /* row counts */
+  csc_cumsum(Cp, w, m);                 /* row pointers */
+
+  for (k = 0; k < nz; k++) {
+    Cj[p = w[Ti[k]]++] = Tj[k];         /* A(i,j) is the pth entry in C */
+
+    if (Cx) {
+      Cx[p] = Tx[k];
+
+      if (TtoC != OSQP_NULL) TtoC[k] = p;  // Assign vector of indices
+    }
+  }
+  return csc_done(C, w, OSQP_NULL, 1);     /* success; free w and return C */
+}
+
+c_int csc_cumsum(c_int *p, c_int *c, c_int n) {
+  c_int i, nz = 0;
+
+  if (!p || !c) return -1;  /* check inputs */
+
+  for (i = 0; i < n; i++)
+  {
+    p[i] = nz;
+    nz  += c[i];
+    c[i] = p[i];
+  }
+  p[n] = nz;
+  return nz; /* return sum (c [0..n-1]) */
+}
+
+c_int* csc_pinv(c_int const *p, c_int n) {
+  c_int k, *pinv;
+
+  if (!p) return OSQP_NULL;                /* p = OSQP_NULL denotes identity */
+
+  pinv = csc_malloc(n, sizeof(c_int));     /* allocate result */
+
+  if (!pinv) return OSQP_NULL;             /* out of memory */
+
+  for (k = 0; k < n; k++) pinv[p[k]] = k;  /* invert the permutation */
+  return pinv;                             /* return result */
+}
+
+csc* csc_symperm(const csc *A, const c_int *pinv, c_int *AtoC, c_int values) {
+  c_int i, j, p, q, i2, j2, n, *Ap, *Ai, *Cp, *Ci, *w;
+  c_float *Cx, *Ax;
+  csc     *C;
+
+  n  = A->n;
+  Ap = A->p;
+  Ai = A->i;
+  Ax = A->x;
+  C  = csc_spalloc(n, n, Ap[n], values && (Ax != OSQP_NULL),
+                   0);                                /* alloc result*/
+  w = csc_calloc(n, sizeof(c_int));                   /* get workspace */
+
+  if (!C || !w) return csc_done(C, w, OSQP_NULL, 0);  /* out of memory */
+
+  Cp = C->p;
+  Ci = C->i;
+  Cx = C->x;
+
+  for (j = 0; j < n; j++)    /* count entries in each column of C */
+  {
+    j2 = pinv ? pinv[j] : j; /* column j of A is column j2 of C */
+
+    for (p = Ap[j]; p < Ap[j + 1]; p++) {
+      i = Ai[p];
+
+      if (i > j) continue;     /* skip lower triangular part of A */
+      i2 = pinv ? pinv[i] : i; /* row i of A is row i2 of C */
+      w[c_max(i2, j2)]++;      /* column count of C */
+    }
+  }
+  csc_cumsum(Cp, w, n);        /* compute column pointers of C */
+
+  for (j = 0; j < n; j++) {
+    j2 = pinv ? pinv[j] : j;   /* column j of A is column j2 of C */
+
+    for (p = Ap[j]; p < Ap[j + 1]; p++) {
+      i = Ai[p];
+
+      if (i > j) continue;                             /* skip lower triangular
+                                                          part of A*/
+      i2                         = pinv ? pinv[i] : i; /* row i of A is row i2
+                                                          of C */
+      Ci[q = w[c_max(i2, j2)]++] = c_min(i2, j2);
+
+      if (Cx) Cx[q] = Ax[p];
+
+      if (AtoC) { // If vector AtoC passed, store values of the mappings
+        AtoC[p] = q;
+      }
+    }
+  }
+  return csc_done(C, w, OSQP_NULL, 1); /* success; free workspace, return C */
+}
+
+csc* copy_csc_mat(const csc *A) {
+  csc *B = csc_spalloc(A->m, A->n, A->p[A->n], 1, 0);
+
+  if (!B) return OSQP_NULL;
+
+  prea_int_vec_copy(A->p, B->p, A->n + 1);
+  prea_int_vec_copy(A->i, B->i, A->p[A->n]);
+  prea_vec_copy(A->x, B->x, A->p[A->n]);
+
+  return B;
+}
+
+void prea_copy_csc_mat(const csc *A, csc *B) {
+  prea_int_vec_copy(A->p, B->p, A->n + 1);
+  prea_int_vec_copy(A->i, B->i, A->p[A->n]);
+  prea_vec_copy(A->x, B->x, A->p[A->n]);
+
+  B->nzmax = A->nzmax;
+}
+
+csc* csc_done(csc *C, void *w, void *x, c_int ok) {
+  c_free(w);                   /* free workspace */
+  c_free(x);
+  if (ok) return C;
+  else {
+    csc_spfree(C);
+    return OSQP_NULL;
+  }
+}
+
+csc* csc_to_triu(csc *M) {
+  csc  *M_trip;    // Matrix in triplet format
+  csc  *M_triu;    // Resulting upper triangular matrix
+  c_int nnzorigM;  // Number of nonzeros from original matrix M
+  c_int nnzmaxM;   // Estimated maximum number of elements of upper triangular M
+  c_int n;         // Dimension of M
+  c_int ptr, i, j; // Counters for (i,j) and index in M
+  c_int z_M = 0;   // Counter for elements in M_trip
+
+
+  // Check if matrix is square
+  if (M->m != M->n) {
+#ifdef PRINTING
+    c_eprint("Matrix M not square");
+#endif /* ifdef PRINTING */
+    return OSQP_NULL;
+  }
+  n = M->n;
+
+  // Get number of nonzeros full M
+  nnzorigM = M->p[n];
+
+  // Estimate nnzmaxM
+  // Number of nonzero elements in original M + diagonal part.
+  // -> Full matrix M as input: estimate is half the number of total elements +
+  // diagonal = .5 * (nnzorigM + n)
+  // -> Upper triangular matrix M as input: estimate is the number of total
+  // elements + diagonal = nnzorigM + n
+  // The maximum between the two is nnzorigM + n
+  nnzmaxM = nnzorigM + n;
+
+  // OLD
+  // nnzmaxM = n*(n+1)/2;  // Full upper triangular matrix (This version
+  // allocates too much memory!)
+  // nnzmaxM = .5 * (nnzorigM + n);  // half of the total elements + diagonal
+
+  // Allocate M_trip
+  M_trip = csc_spalloc(n, n, nnzmaxM, 1, 1); // Triplet format
+
+  if (!M_trip) {
+#ifdef PRINTING
+    c_eprint("Upper triangular matrix extraction failed (out of memory)");
+#endif /* ifdef PRINTING */
+    return OSQP_NULL;
+  }
+
+  // Fill M_trip with only elements in M which are in the upper triangular
+  for (j = 0; j < n; j++) { // Cycle over columns
+    for (ptr = M->p[j]; ptr < M->p[j + 1]; ptr++) {
+      // Get row index
+      i = M->i[ptr];
+
+      // Assign element only if in the upper triangular
+      if (i <= j) {
+        // c_print("\nM(%i, %i) = %.4f", M->i[ptr], j, M->x[ptr]);
+
+        M_trip->i[z_M] = i;
+        M_trip->p[z_M] = j;
+        M_trip->x[z_M] = M->x[ptr];
+
+        // Increase counter for the number of elements
+        z_M++;
+      }
+    }
+  }
+
+  // Set number of nonzeros
+  M_trip->nz = z_M;
+
+  // Convert triplet matrix to csc format
+  M_triu = triplet_to_csc(M_trip, OSQP_NULL);
+
+  // Assign number of nonzeros of full matrix to triu M
+  M_triu->nzmax = nnzmaxM;
+
+  // Cleanup and return result
+  csc_spfree(M_trip);
+
+  // Return matrix in triplet form
+  return M_triu;
+}
diff --git a/src/ctrlc.c b/src/ctrlc.c
new file mode 100644
index 0000000..a1fe2ea
--- /dev/null
+++ b/src/ctrlc.c
@@ -0,0 +1,80 @@
+/*
+ * Implements signal handling (ctrl-c) for OSQP.
+ *
+ * Under Windows, we use SetConsoleCtrlHandler.
+ * Under Unix systems, we use sigaction.
+ * For Mex files, we use utSetInterruptEnabled/utIsInterruptPending.
+ *
+ */
+
+#include "ctrlc.h"
+
+#if defined MATLAB
+
+static int istate;
+
+void osqp_start_interrupt_listener(void) {
+  istate = utSetInterruptEnabled(1);
+}
+
+void osqp_end_interrupt_listener(void) {
+  utSetInterruptEnabled(istate);
+}
+
+int osqp_is_interrupted(void) {
+  return utIsInterruptPending();
+}
+
+#elif defined IS_WINDOWS
+
+static int int_detected;
+static BOOL WINAPI handle_ctrlc(DWORD dwCtrlType) {
+  if (dwCtrlType != CTRL_C_EVENT) return FALSE;
+
+  int_detected = 1;
+  return TRUE;
+}
+
+void osqp_start_interrupt_listener(void) {
+  int_detected = 0;
+  SetConsoleCtrlHandler(handle_ctrlc, TRUE);
+}
+
+void osqp_end_interrupt_listener(void) {
+  SetConsoleCtrlHandler(handle_ctrlc, FALSE);
+}
+
+int osqp_is_interrupted(void) {
+  return int_detected;
+}
+
+#else /* Unix */
+
+# include <signal.h>
+static int int_detected;
+struct sigaction oact;
+static void handle_ctrlc(int dummy) {
+  int_detected = dummy ? dummy : -1;
+}
+
+void osqp_start_interrupt_listener(void) {
+  struct sigaction act;
+
+  int_detected = 0;
+  act.sa_flags = 0;
+  sigemptyset(&act.sa_mask);
+  act.sa_handler = handle_ctrlc;
+  sigaction(SIGINT, &act, &oact);
+}
+
+void osqp_end_interrupt_listener(void) {
+  struct sigaction act;
+
+  sigaction(SIGINT, &oact, &act);
+}
+
+int osqp_is_interrupted(void) {
+  return int_detected;
+}
+
+#endif /* END IF IS_MATLAB / WINDOWS */
diff --git a/src/error.c b/src/error.c
new file mode 100644
index 0000000..e0c069e
--- /dev/null
+++ b/src/error.c
@@ -0,0 +1,21 @@
+#include "error.h"
+
+const char *OSQP_ERROR_MESSAGE[] = {
+  "Problem data validation.",
+  "Solver settings validation.",
+  "Linear system solver not available.\nTried to obtain it from shared library.",
+  "Linear system solver initialization.",
+  "KKT matrix factorization.\nThe problem seems to be non-convex.",
+  "Memory allocation.",
+  "Solver workspace not initialized.",
+};
+
+
+c_int _osqp_error(enum osqp_error_type error_code,
+		 const char * function_name) {
+# ifdef PRINTING
+  c_print("ERROR in %s: %s\n", function_name, OSQP_ERROR_MESSAGE[error_code-1]);
+# endif
+  return (c_int)error_code;
+}
+
diff --git a/src/kkt.c b/src/kkt.c
new file mode 100644
index 0000000..f89aea0
--- /dev/null
+++ b/src/kkt.c
@@ -0,0 +1,224 @@
+#include "kkt.h"
+
+#ifndef EMBEDDED
+
+
+csc* form_KKT(const csc  *P,
+              const  csc *A,
+              c_int       format,
+              c_float     param1,
+              c_float    *param2,
+              c_int      *PtoKKT,
+              c_int      *AtoKKT,
+              c_int     **Pdiag_idx,
+              c_int      *Pdiag_n,
+              c_int      *param2toKKT) {
+  c_int  nKKT, nnzKKTmax; // Size, number of nonzeros and max number of nonzeros
+                          // in KKT matrix
+  csc   *KKT_trip, *KKT;  // KKT matrix in triplet format and CSC format
+  c_int  ptr, i, j;       // Counters for elements (i,j) and index pointer
+  c_int  zKKT = 0;        // Counter for total number of elements in P and in
+                          // KKT
+  c_int *KKT_TtoC;        // Pointer to vector mapping from KKT in triplet form
+                          // to CSC
+
+  // Get matrix dimensions
+  nKKT = P->m + A->m;
+
+  // Get maximum number of nonzero elements (only upper triangular part)
+  nnzKKTmax = P->p[P->n] + // Number of elements in P
+              P->m +       // Number of elements in param1 * I
+              A->p[A->n] + // Number of nonzeros in A
+              A->m;        // Number of elements in - diag(param2)
+
+  // Preallocate KKT matrix in triplet format
+  KKT_trip = csc_spalloc(nKKT, nKKT, nnzKKTmax, 1, 1);
+
+  if (!KKT_trip) return OSQP_NULL;  // Failed to preallocate matrix
+
+  // Allocate vector of indices on the diagonal. Worst case it has m elements
+  if (Pdiag_idx != OSQP_NULL) {
+    (*Pdiag_idx) = c_malloc(P->m * sizeof(c_int));
+    *Pdiag_n     = 0; // Set 0 diagonal elements to start
+  }
+
+  // Allocate Triplet matrices
+  // P + param1 I
+  for (j = 0; j < P->n; j++) { // cycle over columns
+    // No elements in column j => add diagonal element param1
+    if (P->p[j] == P->p[j + 1]) {
+      KKT_trip->i[zKKT] = j;
+      KKT_trip->p[zKKT] = j;
+      KKT_trip->x[zKKT] = param1;
+      zKKT++;
+    }
+
+    for (ptr = P->p[j]; ptr < P->p[j + 1]; ptr++) { // cycle over rows
+      // Get current row
+      i = P->i[ptr];
+
+      // Add element of P
+      KKT_trip->i[zKKT] = i;
+      KKT_trip->p[zKKT] = j;
+      KKT_trip->x[zKKT] = P->x[ptr];
+
+      if (PtoKKT != OSQP_NULL) PtoKKT[ptr] = zKKT;  // Update index from P to
+                                                    // KKTtrip
+
+      if (i == j) {                                 // P has a diagonal element,
+                                                    // add param1
+        KKT_trip->x[zKKT] += param1;
+
+        // If index vector pointer supplied -> Store the index
+        if (Pdiag_idx != OSQP_NULL) {
+          (*Pdiag_idx)[*Pdiag_n] = ptr;
+          (*Pdiag_n)++;
+        }
+      }
+      zKKT++;
+
+      // Add diagonal param1 in case
+      if ((i < j) &&                  // Diagonal element not reached
+          (ptr + 1 == P->p[j + 1])) { // last element of column j
+        // Add diagonal element param1
+        KKT_trip->i[zKKT] = j;
+        KKT_trip->p[zKKT] = j;
+        KKT_trip->x[zKKT] = param1;
+        zKKT++;
+      }
+    }
+  }
+
+  if (Pdiag_idx != OSQP_NULL) {
+    // Realloc Pdiag_idx so that it contains exactly *Pdiag_n diagonal elements
+    (*Pdiag_idx) = c_realloc((*Pdiag_idx), (*Pdiag_n) * sizeof(c_int));
+  }
+
+
+  // A' at top right
+  for (j = 0; j < A->n; j++) {                      // Cycle over columns of A
+    for (ptr = A->p[j]; ptr < A->p[j + 1]; ptr++) {
+      KKT_trip->p[zKKT] = P->m + A->i[ptr];         // Assign column index from
+                                                    // row index of A
+      KKT_trip->i[zKKT] = j;                        // Assign row index from
+                                                    // column index of A
+      KKT_trip->x[zKKT] = A->x[ptr];                // Assign A value element
+
+      if (AtoKKT != OSQP_NULL) AtoKKT[ptr] = zKKT;  // Update index from A to
+                                                    // KKTtrip
+      zKKT++;
+    }
+  }
+
+  // - diag(param2) at bottom right
+  for (j = 0; j < A->m; j++) {
+    KKT_trip->i[zKKT] = j + P->n;
+    KKT_trip->p[zKKT] = j + P->n;
+    KKT_trip->x[zKKT] = -param2[j];
+
+    if (param2toKKT != OSQP_NULL) param2toKKT[j] = zKKT;  // Update index from
+                                                          // param2 to KKTtrip
+    zKKT++;
+  }
+
+  // Allocate number of nonzeros
+  KKT_trip->nz = zKKT;
+
+  // Convert triplet matrix to csc format
+  if (!PtoKKT && !AtoKKT && !param2toKKT) {
+    // If no index vectors passed, do not store KKT mapping from Trip to CSC/CSR
+    if (format == 0) KKT = triplet_to_csc(KKT_trip, OSQP_NULL);
+    else KKT = triplet_to_csr(KKT_trip, OSQP_NULL);
+  }
+  else {
+    // Allocate vector of indices from triplet to csc
+    KKT_TtoC = c_malloc((zKKT) * sizeof(c_int));
+
+    if (!KKT_TtoC) {
+      // Error in allocating KKT_TtoC vector
+      csc_spfree(KKT_trip);
+      c_free(*Pdiag_idx);
+      return OSQP_NULL;
+    }
+
+    // Store KKT mapping from Trip to CSC/CSR
+    if (format == 0)
+      KKT = triplet_to_csc(KKT_trip, KKT_TtoC);
+    else
+      KKT = triplet_to_csr(KKT_trip, KKT_TtoC);
+
+    // Update vectors of indices from P, A, param2 to KKT (now in CSC format)
+    if (PtoKKT != OSQP_NULL) {
+      for (i = 0; i < P->p[P->n]; i++) {
+        PtoKKT[i] = KKT_TtoC[PtoKKT[i]];
+      }
+    }
+
+    if (AtoKKT != OSQP_NULL) {
+      for (i = 0; i < A->p[A->n]; i++) {
+        AtoKKT[i] = KKT_TtoC[AtoKKT[i]];
+      }
+    }
+
+    if (param2toKKT != OSQP_NULL) {
+      for (i = 0; i < A->m; i++) {
+        param2toKKT[i] = KKT_TtoC[param2toKKT[i]];
+      }
+    }
+
+    // Free mapping
+    c_free(KKT_TtoC);
+  }
+
+  // Clean matrix in triplet format and return result
+  csc_spfree(KKT_trip);
+
+  return KKT;
+}
+
+#endif /* ifndef EMBEDDED */
+
+
+#if EMBEDDED != 1
+
+void update_KKT_P(csc          *KKT,
+                  const csc    *P,
+                  const c_int  *PtoKKT,
+                  const c_float param1,
+                  const c_int  *Pdiag_idx,
+                  const c_int   Pdiag_n) {
+  c_int i, j; // Iterations
+
+  // Update elements of KKT using P
+  for (i = 0; i < P->p[P->n]; i++) {
+    KKT->x[PtoKKT[i]] = P->x[i];
+  }
+
+  // Update diagonal elements of KKT by adding sigma
+  for (i = 0; i < Pdiag_n; i++) {
+    j                  = Pdiag_idx[i]; // Extract index of the element on the
+                                       // diagonal
+    KKT->x[PtoKKT[j]] += param1;
+  }
+}
+
+void update_KKT_A(csc *KKT, const csc *A, const c_int *AtoKKT) {
+  c_int i; // Iterations
+
+  // Update elements of KKT using A
+  for (i = 0; i < A->p[A->n]; i++) {
+    KKT->x[AtoKKT[i]] = A->x[i];
+  }
+}
+
+void update_KKT_param2(csc *KKT, const c_float *param2,
+                       const c_int *param2toKKT, const c_int m) {
+  c_int i; // Iterations
+
+  // Update elements of KKT using param2
+  for (i = 0; i < m; i++) {
+    KKT->x[param2toKKT[i]] = -param2[i];
+  }
+}
+
+#endif // EMBEDDED != 1
diff --git a/src/lin_alg.c b/src/lin_alg.c
new file mode 100644
index 0000000..ba896f5
--- /dev/null
+++ b/src/lin_alg.c
@@ -0,0 +1,413 @@
+#include "lin_alg.h"
+
+
+/* VECTOR FUNCTIONS ----------------------------------------------------------*/
+
+
+void vec_add_scaled(c_float       *c,
+                    const c_float *a,
+                    const c_float *b,
+                    c_int          n,
+                    c_float        sc) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    c[i] =  a[i] + sc * b[i];
+  }
+}
+
+c_float vec_scaled_norm_inf(const c_float *S, const c_float *v, c_int l) {
+  c_int   i;
+  c_float abs_Sv_i;
+  c_float max = 0.0;
+
+  for (i = 0; i < l; i++) {
+    abs_Sv_i = c_absval(S[i] * v[i]);
+
+    if (abs_Sv_i > max) max = abs_Sv_i;
+  }
+  return max;
+}
+
+c_float vec_norm_inf(const c_float *v, c_int l) {
+  c_int   i;
+  c_float abs_v_i;
+  c_float max = 0.0;
+
+  for (i = 0; i < l; i++) {
+    abs_v_i = c_absval(v[i]);
+
+    if (abs_v_i > max) max = abs_v_i;
+  }
+  return max;
+}
+
+c_float vec_norm_inf_diff(const c_float *a, const c_float *b, c_int l) {
+  c_float nmDiff = 0.0, tmp;
+  c_int   i;
+
+  for (i = 0; i < l; i++) {
+    tmp = c_absval(a[i] - b[i]);
+
+    if (tmp > nmDiff) nmDiff = tmp;
+  }
+  return nmDiff;
+}
+
+c_float vec_mean(const c_float *a, c_int n) {
+  c_float mean = 0.0;
+  c_int   i;
+
+  for (i = 0; i < n; i++) {
+    mean += a[i];
+  }
+  mean /= (c_float)n;
+
+  return mean;
+}
+
+void int_vec_set_scalar(c_int *a, c_int sc, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    a[i] = sc;
+  }
+}
+
+void vec_set_scalar(c_float *a, c_float sc, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    a[i] = sc;
+  }
+}
+
+void vec_add_scalar(c_float *a, c_float sc, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    a[i] += sc;
+  }
+}
+
+void vec_mult_scalar(c_float *a, c_float sc, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    a[i] *= sc;
+  }
+}
+
+#ifndef EMBEDDED
+c_float* vec_copy(c_float *a, c_int n) {
+  c_float *b;
+  c_int    i;
+
+  b = c_malloc(n * sizeof(c_float));
+  if (!b) return OSQP_NULL;
+
+  for (i = 0; i < n; i++) {
+    b[i] = a[i];
+  }
+
+  return b;
+}
+
+#endif // end EMBEDDED
+
+
+void prea_int_vec_copy(const c_int *a, c_int *b, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    b[i] = a[i];
+  }
+}
+
+void prea_vec_copy(const c_float *a, c_float *b, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    b[i] = a[i];
+  }
+}
+
+void vec_ew_recipr(const c_float *a, c_float *b, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    b[i] = (c_float)1.0 / a[i];
+  }
+}
+
+c_float vec_prod(const c_float *a, const c_float *b, c_int n) {
+  c_float prod = 0.0;
+  c_int   i; // Index
+
+  for (i = 0; i < n; i++) {
+    prod += a[i] * b[i];
+  }
+
+  return prod;
+}
+
+void vec_ew_prod(const c_float *a, const c_float *b, c_float *c, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    c[i] = b[i] * a[i];
+  }
+}
+
+#if EMBEDDED != 1
+void vec_ew_sqrt(c_float *a, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    a[i] = c_sqrt(a[i]);
+  }
+}
+
+void vec_ew_max(c_float *a, c_int n, c_float max_val) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    a[i] = c_max(a[i], max_val);
+  }
+}
+
+void vec_ew_min(c_float *a, c_int n, c_float min_val) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    a[i] = c_min(a[i], min_val);
+  }
+}
+
+void vec_ew_max_vec(const c_float *a, const c_float *b, c_float *c, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    c[i] = c_max(a[i], b[i]);
+  }
+}
+
+void vec_ew_min_vec(const c_float *a, const c_float *b, c_float *c, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    c[i] = c_min(a[i], b[i]);
+  }
+}
+
+#endif // EMBEDDED != 1
+
+
+/* MATRIX FUNCTIONS ----------------------------------------------------------*/
+
+/* multiply scalar to matrix */
+void mat_mult_scalar(csc *A, c_float sc) {
+  c_int i, nnzA;
+
+  nnzA = A->p[A->n];
+
+  for (i = 0; i < nnzA; i++) {
+    A->x[i] *= sc;
+  }
+}
+
+void mat_premult_diag(csc *A, const c_float *d) {
+  c_int j, i;
+
+  for (j = 0; j < A->n; j++) {                // Cycle over columns
+    for (i = A->p[j]; i < A->p[j + 1]; i++) { // Cycle every row in the column
+      A->x[i] *= d[A->i[i]];                  // Scale by corresponding element
+                                              // of d for row i
+    }
+  }
+}
+
+void mat_postmult_diag(csc *A, const c_float *d) {
+  c_int j, i;
+
+  for (j = 0; j < A->n; j++) {                // Cycle over columns j
+    for (i = A->p[j]; i < A->p[j + 1]; i++) { // Cycle every row i in column j
+      A->x[i] *= d[j];                        // Scale by corresponding element
+                                              // of d for column j
+    }
+  }
+}
+
+void mat_vec(const csc *A, const c_float *x, c_float *y, c_int plus_eq) {
+  c_int i, j;
+
+  if (!plus_eq) {
+    // y = 0
+    for (i = 0; i < A->m; i++) {
+      y[i] = 0;
+    }
+  }
+
+  // if A is empty
+  if (A->p[A->n] == 0) {
+    return;
+  }
+
+  if (plus_eq == -1) {
+    // y -=  A*x
+    for (j = 0; j < A->n; j++) {
+      for (i = A->p[j]; i < A->p[j + 1]; i++) {
+        y[A->i[i]] -= A->x[i] * x[j];
+      }
+    }
+  } else {
+    // y +=  A*x
+    for (j = 0; j < A->n; j++) {
+      for (i = A->p[j]; i < A->p[j + 1]; i++) {
+        y[A->i[i]] += A->x[i] * x[j];
+      }
+    }
+  }
+}
+
+void mat_tpose_vec(const csc *A, const c_float *x, c_float *y,
+                   c_int plus_eq, c_int skip_diag) {
+  c_int i, j, k;
+
+  if (!plus_eq) {
+    // y = 0
+    for (i = 0; i < A->n; i++) {
+      y[i] = 0;
+    }
+  }
+
+  // if A is empty
+  if (A->p[A->n] == 0) {
+    return;
+  }
+
+  if (plus_eq == -1) {
+    // y -=  A*x
+    if (skip_diag) {
+      for (j = 0; j < A->n; j++) {
+        for (k = A->p[j]; k < A->p[j + 1]; k++) {
+          i     = A->i[k];
+          y[j] -= i == j ? 0 : A->x[k] * x[i];
+        }
+      }
+    } else {
+      for (j = 0; j < A->n; j++) {
+        for (k = A->p[j]; k < A->p[j + 1]; k++) {
+          y[j] -= A->x[k] * x[A->i[k]];
+        }
+      }
+    }
+  } else {
+    // y +=  A*x
+    if (skip_diag) {
+      for (j = 0; j < A->n; j++) {
+        for (k = A->p[j]; k < A->p[j + 1]; k++) {
+          i     = A->i[k];
+          y[j] += i == j ? 0 : A->x[k] * x[i];
+        }
+      }
+    } else {
+      for (j = 0; j < A->n; j++) {
+        for (k = A->p[j]; k < A->p[j + 1]; k++) {
+          y[j] += A->x[k] * x[A->i[k]];
+        }
+      }
+    }
+  }
+}
+
+#if EMBEDDED != 1
+void mat_inf_norm_cols(const csc *M, c_float *E) {
+  c_int j, ptr;
+
+  // Initialize zero max elements
+  for (j = 0; j < M->n; j++) {
+    E[j] = 0.;
+  }
+
+  // Compute maximum across columns
+  for (j = 0; j < M->n; j++) {
+    for (ptr = M->p[j]; ptr < M->p[j + 1]; ptr++) {
+      E[j] = c_max(c_absval(M->x[ptr]), E[j]);
+    }
+  }
+}
+
+void mat_inf_norm_rows(const csc *M, c_float *E) {
+  c_int i, j, ptr;
+
+  // Initialize zero max elements
+  for (j = 0; j < M->m; j++) {
+    E[j] = 0.;
+  }
+
+  // Compute maximum across rows
+  for (j = 0; j < M->n; j++) {
+    for (ptr = M->p[j]; ptr < M->p[j + 1]; ptr++) {
+      i    = M->i[ptr];
+      E[i] = c_max(c_absval(M->x[ptr]), E[i]);
+    }
+  }
+}
+
+void mat_inf_norm_cols_sym_triu(const csc *M, c_float *E) {
+  c_int   i, j, ptr;
+  c_float abs_x;
+
+  // Initialize zero max elements
+  for (j = 0; j < M->n; j++) {
+    E[j] = 0.;
+  }
+
+  // Compute maximum across columns
+  // Note that element (i, j) contributes to
+  // -> Column j (as expected in any matrices)
+  // -> Column i (which is equal to row i for symmetric matrices)
+  for (j = 0; j < M->n; j++) {
+    for (ptr = M->p[j]; ptr < M->p[j + 1]; ptr++) {
+      i     = M->i[ptr];
+      abs_x = c_absval(M->x[ptr]);
+      E[j]  = c_max(abs_x, E[j]);
+
+      if (i != j) {
+        E[i] = c_max(abs_x, E[i]);
+      }
+    }
+  }
+}
+
+#endif /* if EMBEDDED != 1 */
+
+
+c_float quad_form(const csc *P, const c_float *x) {
+  c_float quad_form = 0.;
+  c_int   i, j, ptr;                                // Pointers to iterate over
+                                                    // matrix: (i,j) a element
+                                                    // pointer
+
+  for (j = 0; j < P->n; j++) {                      // Iterate over columns
+    for (ptr = P->p[j]; ptr < P->p[j + 1]; ptr++) { // Iterate over rows
+      i = P->i[ptr];                                // Row index
+
+      if (i == j) {                                 // Diagonal element
+        quad_form += (c_float).5 * P->x[ptr] * x[i] * x[i];
+      }
+      else if (i < j) {                             // Off-diagonal element
+        quad_form += P->x[ptr] * x[i] * x[j];
+      }
+      else {                                        // Element in lower diagonal
+                                                    // part
+#ifdef PRINTING
+        c_eprint("quad_form matrix is not upper triangular");
+#endif /* ifdef PRINTING */
+        return OSQP_NULL;
+      }
+    }
+  }
+  return quad_form;
+}
diff --git a/src/lin_sys.c b/src/lin_sys.c
new file mode 100644
index 0000000..fe617d3
--- /dev/null
+++ b/src/lin_sys.c
@@ -0,0 +1,75 @@
+#include "lin_sys.h"
+
+#include "qdldl_interface.h" // Include only this solver in the same directory
+
+const char *LINSYS_SOLVER_NAME[] = {
+  "qdldl", "mkl pardiso"
+};
+
+#ifdef ENABLE_MKL_PARDISO
+# include "pardiso_interface.h"
+# include "pardiso_loader.h"
+#endif /* ifdef ENABLE_MKL_PARDISO */
+
+// Load linear system solver shared library
+c_int load_linsys_solver(enum linsys_solver_type linsys_solver) {
+  switch (linsys_solver) {
+  case QDLDL_SOLVER:
+
+    // We do not load  QDLDL solver. We have the source.
+    return 0;
+
+# ifdef ENABLE_MKL_PARDISO
+  case MKL_PARDISO_SOLVER:
+
+    // Load Pardiso library
+    return lh_load_pardiso(OSQP_NULL);
+
+# endif /* ifdef ENABLE_MKL_PARDISO */
+  default: // QDLDL
+    return 0;
+  }
+}
+
+// Unload linear system solver shared library
+c_int unload_linsys_solver(enum linsys_solver_type linsys_solver) {
+  switch (linsys_solver) {
+  case QDLDL_SOLVER:
+
+    // We do not load QDLDL solver. We have the source.
+    return 0;
+
+# ifdef ENABLE_MKL_PARDISO
+  case MKL_PARDISO_SOLVER:
+
+    // Unload Pardiso library
+    return lh_unload_pardiso();
+
+# endif /* ifdef ENABLE_MKL_PARDISO */
+  default: //  QDLDL
+    return 0;
+  }
+}
+
+// Initialize linear system solver structure
+// NB: Only the upper triangular part of P is stuffed!
+c_int init_linsys_solver(LinSysSolver          **s,
+                         const csc              *P,
+                         const csc              *A,
+                         c_float                 sigma,
+                         const c_float          *rho_vec,
+                         enum linsys_solver_type linsys_solver,
+                         c_int                   polish) {
+  switch (linsys_solver) {
+  case QDLDL_SOLVER:
+    return init_linsys_solver_qdldl((qdldl_solver **)s, P, A, sigma, rho_vec, polish);
+
+# ifdef ENABLE_MKL_PARDISO
+  case MKL_PARDISO_SOLVER:
+    return init_linsys_solver_pardiso((pardiso_solver **)s, P, A, sigma, rho_vec, polish);
+
+# endif /* ifdef ENABLE_MKL_PARDISO */
+  default: // QDLDL
+    return init_linsys_solver_qdldl((qdldl_solver **)s, P, A, sigma, rho_vec, polish);
+  }
+}
diff --git a/src/osqp.c b/src/osqp.c
new file mode 100644
index 0000000..20cb1af
--- /dev/null
+++ b/src/osqp.c
@@ -0,0 +1,1617 @@
+#include "osqp.h"
+#include "auxil.h"
+#include "util.h"
+#include "scaling.h"
+#include "glob_opts.h"
+#include "error.h"
+
+
+#ifndef EMBEDDED
+# include "polish.h"
+#endif /* ifndef EMBEDDED */
+
+#ifdef CTRLC
+# include "ctrlc.h"
+#endif /* ifdef CTRLC */
+
+#ifndef EMBEDDED
+# include "lin_sys.h"
+#endif /* ifndef EMBEDDED */
+
+/**********************
+* Main API Functions *
+**********************/
+void osqp_set_default_settings(OSQPSettings *settings) {
+
+  settings->rho           = (c_float)RHO;            /* ADMM step */
+  settings->sigma         = (c_float)SIGMA;          /* ADMM step */
+  settings->scaling = SCALING;                       /* heuristic problem scaling */
+#if EMBEDDED != 1
+  settings->adaptive_rho           = ADAPTIVE_RHO;
+  settings->adaptive_rho_interval  = ADAPTIVE_RHO_INTERVAL;
+  settings->adaptive_rho_tolerance = (c_float)ADAPTIVE_RHO_TOLERANCE;
+
+# ifdef PROFILING
+  settings->adaptive_rho_fraction = (c_float)ADAPTIVE_RHO_FRACTION;
+# endif /* ifdef PROFILING */
+#endif  /* if EMBEDDED != 1 */
+
+  settings->max_iter      = MAX_ITER;                /* maximum iterations to
+                                                        take */
+  settings->eps_abs       = (c_float)EPS_ABS;        /* absolute convergence
+                                                        tolerance */
+  settings->eps_rel       = (c_float)EPS_REL;        /* relative convergence
+                                                        tolerance */
+  settings->eps_prim_inf  = (c_float)EPS_PRIM_INF;   /* primal infeasibility
+                                                        tolerance */
+  settings->eps_dual_inf  = (c_float)EPS_DUAL_INF;   /* dual infeasibility
+                                                        tolerance */
+  settings->alpha         = (c_float)ALPHA;          /* relaxation parameter */
+  settings->linsys_solver = LINSYS_SOLVER;           /* relaxation parameter */
+
+#ifndef EMBEDDED
+  settings->delta              = DELTA;              /* regularization parameter
+                                                        for polish */
+  settings->polish             = POLISH;             /* ADMM solution polish: 1
+                                                      */
+  settings->polish_refine_iter = POLISH_REFINE_ITER; /* iterative refinement
+                                                        steps in polish */
+  settings->verbose            = VERBOSE;            /* print output */
+#endif /* ifndef EMBEDDED */
+
+  settings->scaled_termination = SCALED_TERMINATION; /* Evaluate scaled
+                                                        termination criteria*/
+  settings->check_termination  = CHECK_TERMINATION;  /* Interval for evaluating
+                                                        termination criteria */
+  settings->warm_start         = WARM_START;         /* warm starting */
+
+#ifdef PROFILING
+  settings->time_limit = TIME_LIMIT;
+#endif /* ifdef PROFILING */
+}
+
+#ifndef EMBEDDED
+
+
+c_int osqp_setup(OSQPWorkspace** workp, const OSQPData *data, const OSQPSettings *settings) {
+  c_int exitflag;
+
+  OSQPWorkspace * work;
+
+  // Validate data
+  if (validate_data(data)) return osqp_error(OSQP_DATA_VALIDATION_ERROR);
+
+  // Validate settings
+  if (validate_settings(settings)) return osqp_error(OSQP_SETTINGS_VALIDATION_ERROR);
+
+  // Allocate empty workspace
+  work = c_calloc(1, sizeof(OSQPWorkspace));
+  if (!(work)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  *workp = work;
+
+  // Start and allocate directly timer
+# ifdef PROFILING
+  work->timer = c_malloc(sizeof(OSQPTimer));
+  if (!(work->timer)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  osqp_tic(work->timer);
+# endif /* ifdef PROFILING */
+
+  // Copy problem data into workspace
+  work->data = c_malloc(sizeof(OSQPData));
+  if (!(work->data)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  work->data->n = data->n;
+  work->data->m = data->m;
+
+  // Cost function
+  work->data->P = copy_csc_mat(data->P);
+  work->data->q = vec_copy(data->q, data->n);
+  if (!(work->data->P) || !(work->data->q)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Constraints
+  work->data->A = copy_csc_mat(data->A);
+  if (!(work->data->A)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  work->data->l = vec_copy(data->l, data->m);
+  work->data->u = vec_copy(data->u, data->m);
+  if ( data->m && (!(work->data->l) || !(work->data->u)) )
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Vectorized rho parameter
+  work->rho_vec     = c_malloc(data->m * sizeof(c_float));
+  work->rho_inv_vec = c_malloc(data->m * sizeof(c_float));
+  if ( data->m && (!(work->rho_vec) || !(work->rho_inv_vec)) )
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Type of constraints
+  work->constr_type = c_calloc(data->m, sizeof(c_int));
+  if (data->m && !(work->constr_type)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Allocate internal solver variables (ADMM steps)
+  work->x        = c_calloc(data->n, sizeof(c_float));
+  work->z        = c_calloc(data->m, sizeof(c_float));
+  work->xz_tilde = c_calloc(data->n + data->m, sizeof(c_float));
+  work->x_prev   = c_calloc(data->n, sizeof(c_float));
+  work->z_prev   = c_calloc(data->m, sizeof(c_float));
+  work->y        = c_calloc(data->m, sizeof(c_float));
+  if (!(work->x) || !(work->xz_tilde) || !(work->x_prev))
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  if ( data->m && (!(work->z) || !(work->z_prev) || !(work->y)) )
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Initialize variables x, y, z to 0
+  cold_start(work);
+
+  // Primal and dual residuals variables
+  work->Ax  = c_calloc(data->m, sizeof(c_float));
+  work->Px  = c_calloc(data->n, sizeof(c_float));
+  work->Aty = c_calloc(data->n, sizeof(c_float));
+
+  // Primal infeasibility variables
+  work->delta_y   = c_calloc(data->m, sizeof(c_float));
+  work->Atdelta_y = c_calloc(data->n, sizeof(c_float));
+
+  // Dual infeasibility variables
+  work->delta_x  = c_calloc(data->n, sizeof(c_float));
+  work->Pdelta_x = c_calloc(data->n, sizeof(c_float));
+  work->Adelta_x = c_calloc(data->m, sizeof(c_float));
+
+  if (!(work->Px) || !(work->Aty) || !(work->Atdelta_y) ||
+      !(work->delta_x) || !(work->Pdelta_x))
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  if ( data->m && (!(work->Ax) || !(work->delta_y) || !(work->Adelta_x)) )
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Copy settings
+  work->settings = copy_settings(settings);
+  if (!(work->settings)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Perform scaling
+  if (settings->scaling) {
+    // Allocate scaling structure
+    work->scaling = c_malloc(sizeof(OSQPScaling));
+    if (!(work->scaling)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+    work->scaling->D    = c_malloc(data->n * sizeof(c_float));
+    work->scaling->Dinv = c_malloc(data->n * sizeof(c_float));
+    work->scaling->E    = c_malloc(data->m * sizeof(c_float));
+    work->scaling->Einv = c_malloc(data->m * sizeof(c_float));
+    if (!(work->scaling->D) || !(work->scaling->Dinv))
+      return osqp_error(OSQP_MEM_ALLOC_ERROR);
+    if ( data->m && (!(work->scaling->E) || !(work->scaling->Einv)) )
+      return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+
+    // Allocate workspace variables used in scaling
+    work->D_temp   = c_malloc(data->n * sizeof(c_float));
+    work->D_temp_A = c_malloc(data->n * sizeof(c_float));
+    work->E_temp   = c_malloc(data->m * sizeof(c_float));
+    // if (!(work->D_temp) || !(work->D_temp_A) || !(work->E_temp))
+    //   return osqp_error(OSQP_MEM_ALLOC_ERROR);
+    if (!(work->D_temp) || !(work->D_temp_A)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+    if (data->m && !(work->E_temp))           return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+    // Scale data
+    scale_data(work);
+  } else {
+    work->scaling  = OSQP_NULL;
+    work->D_temp   = OSQP_NULL;
+    work->D_temp_A = OSQP_NULL;
+    work->E_temp   = OSQP_NULL;
+  }
+
+  // Set type of constraints
+  set_rho_vec(work);
+
+  // Load linear system solver
+  if (load_linsys_solver(work->settings->linsys_solver)) return osqp_error(OSQP_LINSYS_SOLVER_LOAD_ERROR);
+
+  // Initialize linear system solver structure
+  exitflag = init_linsys_solver(&(work->linsys_solver), work->data->P, work->data->A,
+                                work->settings->sigma, work->rho_vec,
+                                work->settings->linsys_solver, 0);
+
+  if (exitflag) {
+    return osqp_error(exitflag);
+  }
+
+  // Initialize active constraints structure
+  work->pol = c_malloc(sizeof(OSQPPolish));
+  if (!(work->pol)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  work->pol->Alow_to_A = c_malloc(data->m * sizeof(c_int));
+  work->pol->Aupp_to_A = c_malloc(data->m * sizeof(c_int));
+  work->pol->A_to_Alow = c_malloc(data->m * sizeof(c_int));
+  work->pol->A_to_Aupp = c_malloc(data->m * sizeof(c_int));
+  work->pol->x         = c_malloc(data->n * sizeof(c_float));
+  work->pol->z         = c_malloc(data->m * sizeof(c_float));
+  work->pol->y         = c_malloc(data->m * sizeof(c_float));
+  if (!(work->pol->x)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  if ( data->m && (!(work->pol->Alow_to_A) || !(work->pol->Aupp_to_A) ||
+      !(work->pol->A_to_Alow) || !(work->pol->A_to_Aupp) ||
+      !(work->pol->z) || !(work->pol->y)) )
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Allocate solution
+  work->solution = c_calloc(1, sizeof(OSQPSolution));
+  if (!(work->solution)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  work->solution->x = c_calloc(1, data->n * sizeof(c_float));
+  work->solution->y = c_calloc(1, data->m * sizeof(c_float));
+  if (!(work->solution->x))            return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  if (data->m && !(work->solution->y)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Allocate and initialize information
+  work->info = c_calloc(1, sizeof(OSQPInfo));
+  if (!(work->info)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  work->info->status_polish = 0;              // Polishing not performed
+  update_status(work->info, OSQP_UNSOLVED);
+# ifdef PROFILING
+  work->info->solve_time  = 0.0;                   // Solve time to zero
+  work->info->update_time = 0.0;                   // Update time to zero
+  work->info->polish_time = 0.0;                   // Polish time to zero
+  work->info->run_time    = 0.0;                   // Total run time to zero
+  work->info->setup_time  = osqp_toc(work->timer); // Update timer information
+
+  work->first_run         = 1;
+  work->clear_update_time = 0;
+  work->rho_update_from_solve = 0;
+# endif /* ifdef PROFILING */
+  work->info->rho_updates  = 0;                    // Rho updates set to 0
+  work->info->rho_estimate = work->settings->rho;  // Best rho estimate
+
+  // Print header
+# ifdef PRINTING
+  if (work->settings->verbose) print_setup_header(work);
+  work->summary_printed = 0; // Initialize last summary  to not printed
+# endif /* ifdef PRINTING */
+
+
+  // If adaptive rho and automatic interval, but profiling disabled, we need to
+  // set the interval to a default value
+# ifndef PROFILING
+  if (work->settings->adaptive_rho && !work->settings->adaptive_rho_interval) {
+    if (work->settings->check_termination) {
+      // If check_termination is enabled, we set it to a multiple of the check
+      // termination interval
+      work->settings->adaptive_rho_interval = ADAPTIVE_RHO_MULTIPLE_TERMINATION *
+                                              work->settings->check_termination;
+    } else {
+      // If check_termination is disabled we set it to a predefined fix number
+      work->settings->adaptive_rho_interval = ADAPTIVE_RHO_FIXED;
+    }
+  }
+# endif /* ifndef PROFILING */
+
+  // Return exit flag
+  return 0;
+}
+
+#endif // #ifndef EMBEDDED
+
+
+c_int osqp_solve(OSQPWorkspace *work) {
+
+  c_int exitflag;
+  c_int iter;
+  c_int compute_cost_function; // Boolean: compute the cost function in the loop or not
+  c_int can_check_termination; // Boolean: check termination or not
+
+#ifdef PROFILING
+  c_float temp_run_time;       // Temporary variable to store current run time
+#endif /* ifdef PROFILING */
+
+#ifdef PRINTING
+  c_int can_print;             // Boolean whether you can print
+#endif /* ifdef PRINTING */
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1)
+    work->info->update_time = 0.0;
+  work->rho_update_from_solve = 1;
+#endif /* ifdef PROFILING */
+
+  // Initialize variables
+  exitflag              = 0;
+  can_check_termination = 0;
+#ifdef PRINTING
+  can_print = work->settings->verbose;
+#endif /* ifdef PRINTING */
+#ifdef PRINTING
+  compute_cost_function = work->settings->verbose; // Compute cost function only
+                                                   // if verbose is on
+#else /* ifdef PRINTING */
+  compute_cost_function = 0;                       // Never compute cost
+                                                   // function during the
+                                                   // iterations if no printing
+                                                   // enabled
+#endif /* ifdef PRINTING */
+
+
+
+#ifdef PROFILING
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+
+#ifdef PRINTING
+
+  if (work->settings->verbose) {
+    // Print Header for every column
+    print_header();
+  }
+#endif /* ifdef PRINTING */
+
+#ifdef CTRLC
+
+  // initialize Ctrl-C support
+  osqp_start_interrupt_listener();
+#endif /* ifdef CTRLC */
+
+  // Initialize variables (cold start or warm start depending on settings)
+  if (!work->settings->warm_start) cold_start(work);  // If not warm start ->
+                                                      // set x, z, y to zero
+
+  // Main ADMM algorithm
+  for (iter = 1; iter <= work->settings->max_iter; iter++) {
+    // Update x_prev, z_prev (preallocated, no malloc)
+    swap_vectors(&(work->x), &(work->x_prev));
+    swap_vectors(&(work->z), &(work->z_prev));
+
+    /* ADMM STEPS */
+    /* Compute \tilde{x}^{k+1}, \tilde{z}^{k+1} */
+    update_xz_tilde(work);
+
+    /* Compute x^{k+1} */
+    update_x(work);
+
+    /* Compute z^{k+1} */
+    update_z(work);
+
+    /* Compute y^{k+1} */
+    update_y(work);
+
+    /* End of ADMM Steps */
+
+#ifdef CTRLC
+
+    // Check the interrupt signal
+    if (osqp_is_interrupted()) {
+      update_status(work->info, OSQP_SIGINT);
+# ifdef PRINTING
+      c_print("Solver interrupted\n");
+# endif /* ifdef PRINTING */
+      exitflag = 1;
+      goto exit;
+    }
+#endif /* ifdef CTRLC */
+
+#ifdef PROFILING
+
+    // Check if solver time_limit is enabled. In case, check if the current
+    // run time is more than the time_limit option.
+    if (work->first_run) {
+      temp_run_time = work->info->setup_time + osqp_toc(work->timer);
+    }
+    else {
+      temp_run_time = work->info->update_time + osqp_toc(work->timer);
+    }
+
+    if (work->settings->time_limit &&
+        (temp_run_time >= work->settings->time_limit)) {
+      update_status(work->info, OSQP_TIME_LIMIT_REACHED);
+# ifdef PRINTING
+      if (work->settings->verbose) c_print("run time limit reached\n");
+      can_print = 0;  // Not printing at this iteration
+# endif /* ifdef PRINTING */
+      break;
+    }
+#endif /* ifdef PROFILING */
+
+
+    // Can we check for termination ?
+    can_check_termination = work->settings->check_termination &&
+                            (iter % work->settings->check_termination == 0);
+
+#ifdef PRINTING
+
+    // Can we print ?
+    can_print = work->settings->verbose &&
+                ((iter % PRINT_INTERVAL == 0) || (iter == 1));
+
+    if (can_check_termination || can_print) { // Update status in either of
+                                              // these cases
+      // Update information
+      update_info(work, iter, compute_cost_function, 0);
+
+      if (can_print) {
+        // Print summary
+        print_summary(work);
+      }
+
+      if (can_check_termination) {
+        // Check algorithm termination
+        if (check_termination(work, 0)) {
+          // Terminate algorithm
+          break;
+        }
+      }
+    }
+#else /* ifdef PRINTING */
+
+    if (can_check_termination) {
+      // Update information and compute also objective value
+      update_info(work, iter, compute_cost_function, 0);
+
+      // Check algorithm termination
+      if (check_termination(work, 0)) {
+        // Terminate algorithm
+        break;
+      }
+    }
+#endif /* ifdef PRINTING */
+
+
+#if EMBEDDED != 1
+# ifdef PROFILING
+
+    // If adaptive rho with automatic interval, check if the solve time is a
+    // certain fraction
+    // of the setup time.
+    if (work->settings->adaptive_rho && !work->settings->adaptive_rho_interval) {
+      // Check time
+      if (osqp_toc(work->timer) >
+          work->settings->adaptive_rho_fraction * work->info->setup_time) {
+        // Enough time has passed. We now get the number of iterations between
+        // the updates.
+        if (work->settings->check_termination) {
+          // If check_termination is enabled, we round the number of iterations
+          // between
+          // rho updates to the closest multiple of check_termination
+          work->settings->adaptive_rho_interval = (c_int)c_roundmultiple(iter,
+                                                                         work->settings->check_termination);
+        } else {
+          // If check_termination is disabled, we round the number of iterations
+          // between
+          // updates to the closest multiple of the default check_termination
+          // interval.
+          work->settings->adaptive_rho_interval = (c_int)c_roundmultiple(iter,
+                                                                         CHECK_TERMINATION);
+        }
+
+        // Make sure the interval is not 0 and at least check_termination times
+        work->settings->adaptive_rho_interval = c_max(
+          work->settings->adaptive_rho_interval,
+          work->settings->check_termination);
+      } // If time condition is met
+    }   // If adaptive rho enabled and interval set to auto
+# else // PROFILING
+    if (work->settings->adaptive_rho && !work->settings->adaptive_rho_interval) {
+      // Set adaptive_rho_interval to constant value
+      if (work->settings->check_termination) {
+        // If check_termination is enabled, we set it to a multiple of the check
+        // termination interval
+        work->settings->adaptive_rho_interval = ADAPTIVE_RHO_MULTIPLE_TERMINATION *
+                                                work->settings->check_termination;
+      } else {
+        // If check_termination is disabled we set it to a predefined fix number
+        work->settings->adaptive_rho_interval = ADAPTIVE_RHO_FIXED;
+      }
+    }
+# endif /* ifdef PROFILING */
+
+    // Adapt rho
+    if (work->settings->adaptive_rho &&
+        work->settings->adaptive_rho_interval &&
+        (iter % work->settings->adaptive_rho_interval == 0)) {
+      // Update info with the residuals if it hasn't been done before
+# ifdef PRINTING
+
+      if (!can_check_termination && !can_print) {
+        // Information has not been computed neither for termination or printing
+        // reasons
+        update_info(work, iter, compute_cost_function, 0);
+      }
+# else /* ifdef PRINTING */
+
+      if (!can_check_termination) {
+        // Information has not been computed before for termination check
+        update_info(work, iter, compute_cost_function, 0);
+      }
+# endif /* ifdef PRINTING */
+
+      // Actually update rho
+      if (adapt_rho(work)) {
+# ifdef PRINTING
+        c_eprint("Failed rho update");
+# endif // PRINTING
+        exitflag = 1;
+        goto exit;
+      }
+    }
+#endif // EMBEDDED != 1
+
+  }        // End of ADMM for loop
+
+
+  // Update information and check termination condition if it hasn't been done
+  // during last iteration (max_iter reached or check_termination disabled)
+  if (!can_check_termination) {
+    /* Update information */
+#ifdef PRINTING
+
+    if (!can_print) {
+      // Update info only if it hasn't been updated before for printing
+      // reasons
+      update_info(work, iter - 1, compute_cost_function, 0);
+    }
+#else /* ifdef PRINTING */
+
+    // If no printing is enabled, update info directly
+    update_info(work, iter - 1, compute_cost_function, 0);
+#endif /* ifdef PRINTING */
+
+#ifdef PRINTING
+
+    /* Print summary */
+    if (work->settings->verbose && !work->summary_printed) print_summary(work);
+#endif /* ifdef PRINTING */
+
+    /* Check whether a termination criterion is triggered */
+    check_termination(work, 0);
+  }
+
+  // Compute objective value in case it was not
+  // computed during the iterations
+  if (!compute_cost_function && has_solution(work->info)){
+    work->info->obj_val = compute_obj_val(work, work->x);
+  }
+
+
+#ifdef PRINTING
+  /* Print summary for last iteration */
+  if (work->settings->verbose && !work->summary_printed) {
+    print_summary(work);
+  }
+#endif /* ifdef PRINTING */
+
+  /* if max iterations reached, change status accordingly */
+  if (work->info->status_val == OSQP_UNSOLVED) {
+    if (!check_termination(work, 1)) { // Try to check for approximate
+      update_status(work->info, OSQP_MAX_ITER_REACHED);
+    }
+  }
+
+#ifdef PROFILING
+  /* if time-limit reached check termination and update status accordingly */
+ if (work->info->status_val == OSQP_TIME_LIMIT_REACHED) {
+    if (!check_termination(work, 1)) { // Try for approximate solutions
+      update_status(work->info, OSQP_TIME_LIMIT_REACHED); /* Change update status back to OSQP_TIME_LIMIT_REACHED */
+    }
+  }
+#endif /* ifdef PROFILING */
+
+
+#if EMBEDDED != 1
+  /* Update rho estimate */
+  work->info->rho_estimate = compute_rho_estimate(work);
+#endif /* if EMBEDDED != 1 */
+
+  /* Update solve time */
+#ifdef PROFILING
+  work->info->solve_time = osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+
+#ifndef EMBEDDED
+  // Polish the obtained solution
+  if (work->settings->polish && (work->info->status_val == OSQP_SOLVED))
+    polish(work);
+#endif /* ifndef EMBEDDED */
+
+#ifdef PROFILING
+  /* Update total time */
+  if (work->first_run) {
+    // total time: setup + solve + polish
+    work->info->run_time = work->info->setup_time +
+                           work->info->solve_time +
+                           work->info->polish_time;
+  } else {
+    // total time: update + solve + polish
+    work->info->run_time = work->info->update_time +
+                           work->info->solve_time +
+                           work->info->polish_time;
+  }
+
+  // Indicate that the solve function has already been executed
+  if (work->first_run) work->first_run = 0;
+
+  // Indicate that the update_time should be set to zero
+  work->clear_update_time = 1;
+
+  // Indicate that osqp_update_rho is not called from osqp_solve
+  work->rho_update_from_solve = 0;
+#endif /* ifdef PROFILING */
+
+#ifdef PRINTING
+  /* Print final footer */
+  if (work->settings->verbose) print_footer(work->info, work->settings->polish);
+#endif /* ifdef PRINTING */
+
+  // Store solution
+  store_solution(work);
+
+
+// Define exit flag for quitting function
+#if defined(PROFILING) || defined(CTRLC) || EMBEDDED != 1
+exit:
+#endif /* if defined(PROFILING) || defined(CTRLC) || EMBEDDED != 1 */
+
+#ifdef CTRLC
+  // Restore previous signal handler
+  osqp_end_interrupt_listener();
+#endif /* ifdef CTRLC */
+
+  return exitflag;
+}
+
+
+#ifndef EMBEDDED
+
+c_int osqp_cleanup(OSQPWorkspace *work) {
+  c_int exitflag = 0;
+
+  if (work) { // If workspace has been allocated
+    // Free Data
+    if (work->data) {
+      if (work->data->P) csc_spfree(work->data->P);
+      if (work->data->A) csc_spfree(work->data->A);
+      if (work->data->q) c_free(work->data->q);
+      if (work->data->l) c_free(work->data->l);
+      if (work->data->u) c_free(work->data->u);
+      c_free(work->data);
+    }
+
+    // Free scaling variables
+    if (work->scaling){
+      if (work->scaling->D)    c_free(work->scaling->D);
+      if (work->scaling->Dinv) c_free(work->scaling->Dinv);
+      if (work->scaling->E)    c_free(work->scaling->E);
+      if (work->scaling->Einv) c_free(work->scaling->Einv);
+      c_free(work->scaling);
+    }
+
+    // Free temp workspace variables for scaling
+    if (work->D_temp)   c_free(work->D_temp);
+    if (work->D_temp_A) c_free(work->D_temp_A);
+    if (work->E_temp)   c_free(work->E_temp);
+
+    // Free linear system solver structure
+    if (work->linsys_solver) {
+      if (work->linsys_solver->free) {
+        work->linsys_solver->free(work->linsys_solver);
+      }
+    }
+
+    // Unload linear system solver after free
+    if (work->settings) {
+      exitflag = unload_linsys_solver(work->settings->linsys_solver);
+    }
+
+#ifndef EMBEDDED
+    // Free active constraints structure
+    if (work->pol) {
+      if (work->pol->Alow_to_A) c_free(work->pol->Alow_to_A);
+      if (work->pol->Aupp_to_A) c_free(work->pol->Aupp_to_A);
+      if (work->pol->A_to_Alow) c_free(work->pol->A_to_Alow);
+      if (work->pol->A_to_Aupp) c_free(work->pol->A_to_Aupp);
+      if (work->pol->x)         c_free(work->pol->x);
+      if (work->pol->z)         c_free(work->pol->z);
+      if (work->pol->y)         c_free(work->pol->y);
+      c_free(work->pol);
+    }
+#endif /* ifndef EMBEDDED */
+
+    // Free other Variables
+    if (work->rho_vec)     c_free(work->rho_vec);
+    if (work->rho_inv_vec) c_free(work->rho_inv_vec);
+#if EMBEDDED != 1
+    if (work->constr_type) c_free(work->constr_type);
+#endif
+    if (work->x)           c_free(work->x);
+    if (work->z)           c_free(work->z);
+    if (work->xz_tilde)    c_free(work->xz_tilde);
+    if (work->x_prev)      c_free(work->x_prev);
+    if (work->z_prev)      c_free(work->z_prev);
+    if (work->y)           c_free(work->y);
+    if (work->Ax)          c_free(work->Ax);
+    if (work->Px)          c_free(work->Px);
+    if (work->Aty)         c_free(work->Aty);
+    if (work->delta_y)     c_free(work->delta_y);
+    if (work->Atdelta_y)   c_free(work->Atdelta_y);
+    if (work->delta_x)     c_free(work->delta_x);
+    if (work->Pdelta_x)    c_free(work->Pdelta_x);
+    if (work->Adelta_x)    c_free(work->Adelta_x);
+
+    // Free Settings
+    if (work->settings) c_free(work->settings);
+
+    // Free solution
+    if (work->solution) {
+      if (work->solution->x) c_free(work->solution->x);
+      if (work->solution->y) c_free(work->solution->y);
+      c_free(work->solution);
+    }
+
+    // Free information
+    if (work->info) c_free(work->info);
+
+# ifdef PROFILING
+    // Free timer
+    if (work->timer) c_free(work->timer);
+# endif /* ifdef PROFILING */
+
+    // Free work
+    c_free(work);
+  }
+
+  return exitflag;
+}
+
+#endif // #ifndef EMBEDDED
+
+
+/************************
+* Update problem data  *
+************************/
+c_int osqp_update_lin_cost(OSQPWorkspace *work, const c_float *q_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  // Replace q by the new vector
+  prea_vec_copy(q_new, work->data->q, work->data->n);
+
+  // Scaling
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->D, work->data->q, work->data->q, work->data->n);
+    vec_mult_scalar(work->data->q, work->scaling->c, work->data->n);
+  }
+
+  // Reset solver information
+  reset_info(work->info);
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return 0;
+}
+
+c_int osqp_update_bounds(OSQPWorkspace *work,
+                         const c_float *l_new,
+                         const c_float *u_new) {
+  c_int i, exitflag = 0;
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  // Check if lower bound is smaller than upper bound
+  for (i = 0; i < work->data->m; i++) {
+    if (l_new[i] > u_new[i]) {
+#ifdef PRINTING
+      c_eprint("lower bound must be lower than or equal to upper bound");
+#endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  // Replace l and u by the new vectors
+  prea_vec_copy(l_new, work->data->l, work->data->m);
+  prea_vec_copy(u_new, work->data->u, work->data->m);
+
+  // Scaling
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->E, work->data->l, work->data->l, work->data->m);
+    vec_ew_prod(work->scaling->E, work->data->u, work->data->u, work->data->m);
+  }
+
+  // Reset solver information
+  reset_info(work->info);
+
+#if EMBEDDED != 1
+  // Update rho_vec and refactor if constraints type changes
+  exitflag = update_rho_vec(work);
+#endif // EMBEDDED != 1
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+c_int osqp_update_lower_bound(OSQPWorkspace *work, const c_float *l_new) {
+  c_int i, exitflag = 0;
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  // Replace l by the new vector
+  prea_vec_copy(l_new, work->data->l, work->data->m);
+
+  // Scaling
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->E, work->data->l, work->data->l, work->data->m);
+  }
+
+  // Check if lower bound is smaller than upper bound
+  for (i = 0; i < work->data->m; i++) {
+    if (work->data->l[i] > work->data->u[i]) {
+#ifdef PRINTING
+      c_eprint("upper bound must be greater than or equal to lower bound");
+#endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  // Reset solver information
+  reset_info(work->info);
+
+#if EMBEDDED != 1
+  // Update rho_vec and refactor if constraints type changes
+  exitflag = update_rho_vec(work);
+#endif // EMBEDDED ! =1
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+c_int osqp_update_upper_bound(OSQPWorkspace *work, const c_float *u_new) {
+  c_int i, exitflag = 0;
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  // Replace u by the new vector
+  prea_vec_copy(u_new, work->data->u, work->data->m);
+
+  // Scaling
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->E, work->data->u, work->data->u, work->data->m);
+  }
+
+  // Check if upper bound is greater than lower bound
+  for (i = 0; i < work->data->m; i++) {
+    if (work->data->u[i] < work->data->l[i]) {
+#ifdef PRINTING
+      c_eprint("lower bound must be lower than or equal to upper bound");
+#endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  // Reset solver information
+  reset_info(work->info);
+
+#if EMBEDDED != 1
+  // Update rho_vec and refactor if constraints type changes
+  exitflag = update_rho_vec(work);
+#endif // EMBEDDED != 1
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+c_int osqp_warm_start(OSQPWorkspace *work, const c_float *x, const c_float *y) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Update warm_start setting to true
+  if (!work->settings->warm_start) work->settings->warm_start = 1;
+
+  // Copy primal and dual variables into the iterates
+  prea_vec_copy(x, work->x, work->data->n);
+  prea_vec_copy(y, work->y, work->data->m);
+
+  // Scale iterates
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->Dinv, work->x, work->x, work->data->n);
+    vec_ew_prod(work->scaling->Einv, work->y, work->y, work->data->m);
+    vec_mult_scalar(work->y, work->scaling->c, work->data->m);
+  }
+
+  // Compute Ax = z and store it in z
+  mat_vec(work->data->A, work->x, work->z, 0);
+
+  return 0;
+}
+
+c_int osqp_warm_start_x(OSQPWorkspace *work, const c_float *x) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Update warm_start setting to true
+  if (!work->settings->warm_start) work->settings->warm_start = 1;
+
+  // Copy primal variable into the iterate x
+  prea_vec_copy(x, work->x, work->data->n);
+
+  // Scale iterate
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->Dinv, work->x, work->x, work->data->n);
+  }
+
+  // Compute Ax = z and store it in z
+  mat_vec(work->data->A, work->x, work->z, 0);
+
+  return 0;
+}
+
+c_int osqp_warm_start_y(OSQPWorkspace *work, const c_float *y) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Update warm_start setting to true
+  if (!work->settings->warm_start) work->settings->warm_start = 1;
+
+  // Copy dual variable into the iterate y
+  prea_vec_copy(y, work->y, work->data->m);
+
+  // Scale iterate
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->Einv, work->y, work->y, work->data->m);
+    vec_mult_scalar(work->y, work->scaling->c, work->data->m);
+  }
+
+  return 0;
+}
+
+
+#if EMBEDDED != 1
+
+c_int osqp_update_P(OSQPWorkspace *work,
+                    const c_float *Px_new,
+                    const c_int   *Px_new_idx,
+                    c_int          P_new_n) {
+  c_int i;        // For indexing
+  c_int exitflag; // Exit flag
+  c_int nnzP;     // Number of nonzeros in P
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  nnzP = work->data->P->p[work->data->P->n];
+
+  if (Px_new_idx) { // Passing the index of elements changed
+    // Check if number of elements is less or equal than the total number of
+    // nonzeros in P
+    if (P_new_n > nnzP) {
+# ifdef PRINTING
+      c_eprint("new number of elements (%i) greater than elements in P (%i)",
+               (int)P_new_n,
+               (int)nnzP);
+# endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Unscale data
+    unscale_data(work);
+  }
+
+  // Update P elements
+  if (Px_new_idx) { // Change only Px_new_idx
+    for (i = 0; i < P_new_n; i++) {
+      work->data->P->x[Px_new_idx[i]] = Px_new[i];
+    }
+  }
+  else // Change whole P
+  {
+    for (i = 0; i < nnzP; i++) {
+      work->data->P->x[i] = Px_new[i];
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Scale data
+    scale_data(work);
+  }
+
+  // Update linear system structure with new data
+  exitflag = work->linsys_solver->update_matrices(work->linsys_solver,
+                                                  work->data->P,
+                                                  work->data->A);
+
+  // Reset solver information
+  reset_info(work->info);
+
+# ifdef PRINTING
+
+  if (exitflag < 0) {
+    c_eprint("new KKT matrix is not quasidefinite");
+  }
+# endif /* ifdef PRINTING */
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+
+c_int osqp_update_A(OSQPWorkspace *work,
+                    const c_float *Ax_new,
+                    const c_int   *Ax_new_idx,
+                    c_int          A_new_n) {
+  c_int i;        // For indexing
+  c_int exitflag; // Exit flag
+  c_int nnzA;     // Number of nonzeros in A
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  nnzA = work->data->A->p[work->data->A->n];
+
+  if (Ax_new_idx) { // Passing the index of elements changed
+    // Check if number of elements is less or equal than the total number of
+    // nonzeros in A
+    if (A_new_n > nnzA) {
+# ifdef PRINTING
+      c_eprint("new number of elements (%i) greater than elements in A (%i)",
+               (int)A_new_n,
+               (int)nnzA);
+# endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Unscale data
+    unscale_data(work);
+  }
+
+  // Update A elements
+  if (Ax_new_idx) { // Change only Ax_new_idx
+    for (i = 0; i < A_new_n; i++) {
+      work->data->A->x[Ax_new_idx[i]] = Ax_new[i];
+    }
+  }
+  else { // Change whole A
+    for (i = 0; i < nnzA; i++) {
+      work->data->A->x[i] = Ax_new[i];
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Scale data
+    scale_data(work);
+  }
+
+  // Update linear system structure with new data
+  exitflag = work->linsys_solver->update_matrices(work->linsys_solver,
+                                                  work->data->P,
+                                                  work->data->A);
+
+  // Reset solver information
+  reset_info(work->info);
+
+# ifdef PRINTING
+
+  if (exitflag < 0) {
+    c_eprint("new KKT matrix is not quasidefinite");
+  }
+# endif /* ifdef PRINTING */
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+
+c_int osqp_update_P_A(OSQPWorkspace *work,
+                      const c_float *Px_new,
+                      const c_int   *Px_new_idx,
+                      c_int          P_new_n,
+                      const c_float *Ax_new,
+                      const c_int   *Ax_new_idx,
+                      c_int          A_new_n) {
+  c_int i;          // For indexing
+  c_int exitflag;   // Exit flag
+  c_int nnzP, nnzA; // Number of nonzeros in P and A
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  nnzP = work->data->P->p[work->data->P->n];
+  nnzA = work->data->A->p[work->data->A->n];
+
+
+  if (Px_new_idx) { // Passing the index of elements changed
+    // Check if number of elements is less or equal than the total number of
+    // nonzeros in P
+    if (P_new_n > nnzP) {
+# ifdef PRINTING
+      c_eprint("new number of elements (%i) greater than elements in P (%i)",
+               (int)P_new_n,
+               (int)nnzP);
+# endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+
+  if (Ax_new_idx) { // Passing the index of elements changed
+    // Check if number of elements is less or equal than the total number of
+    // nonzeros in A
+    if (A_new_n > nnzA) {
+# ifdef PRINTING
+      c_eprint("new number of elements (%i) greater than elements in A (%i)",
+               (int)A_new_n,
+               (int)nnzA);
+# endif /* ifdef PRINTING */
+      return 2;
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Unscale data
+    unscale_data(work);
+  }
+
+  // Update P elements
+  if (Px_new_idx) { // Change only Px_new_idx
+    for (i = 0; i < P_new_n; i++) {
+      work->data->P->x[Px_new_idx[i]] = Px_new[i];
+    }
+  }
+  else // Change whole P
+  {
+    for (i = 0; i < nnzP; i++) {
+      work->data->P->x[i] = Px_new[i];
+    }
+  }
+
+  // Update A elements
+  if (Ax_new_idx) { // Change only Ax_new_idx
+    for (i = 0; i < A_new_n; i++) {
+      work->data->A->x[Ax_new_idx[i]] = Ax_new[i];
+    }
+  }
+  else { // Change whole A
+    for (i = 0; i < nnzA; i++) {
+      work->data->A->x[i] = Ax_new[i];
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Scale data
+    scale_data(work);
+  }
+
+  // Update linear system structure with new data
+  exitflag = work->linsys_solver->update_matrices(work->linsys_solver,
+                                                  work->data->P,
+                                                  work->data->A);
+
+  // Reset solver information
+  reset_info(work->info);
+
+# ifdef PRINTING
+
+  if (exitflag < 0) {
+    c_eprint("new KKT matrix is not quasidefinite");
+  }
+# endif /* ifdef PRINTING */
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+c_int osqp_update_rho(OSQPWorkspace *work, c_float rho_new) {
+  c_int exitflag, i;
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check value of rho
+  if (rho_new <= 0) {
+# ifdef PRINTING
+    c_eprint("rho must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+#ifdef PROFILING
+  if (work->rho_update_from_solve == 0) {
+    if (work->clear_update_time == 1) {
+      work->clear_update_time = 0;
+      work->info->update_time = 0.0;
+    }
+    osqp_tic(work->timer); // Start timer
+  }
+#endif /* ifdef PROFILING */
+
+  // Update rho in settings
+  work->settings->rho = c_min(c_max(rho_new, RHO_MIN), RHO_MAX);
+
+  // Update rho_vec and rho_inv_vec
+  for (i = 0; i < work->data->m; i++) {
+    if (work->constr_type[i] == 0) {
+      // Inequalities
+      work->rho_vec[i]     = work->settings->rho;
+      work->rho_inv_vec[i] = 1. / work->settings->rho;
+    }
+    else if (work->constr_type[i] == 1) {
+      // Equalities
+      work->rho_vec[i]     = RHO_EQ_OVER_RHO_INEQ * work->settings->rho;
+      work->rho_inv_vec[i] = 1. / work->rho_vec[i];
+    }
+  }
+
+  // Update rho_vec in KKT matrix
+  exitflag = work->linsys_solver->update_rho_vec(work->linsys_solver,
+                                                 work->rho_vec);
+
+#ifdef PROFILING
+  if (work->rho_update_from_solve == 0)
+    work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+#endif // EMBEDDED != 1
+
+/****************************
+* Update problem settings  *
+****************************/
+c_int osqp_update_max_iter(OSQPWorkspace *work, c_int max_iter_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that max_iter is positive
+  if (max_iter_new <= 0) {
+#ifdef PRINTING
+    c_eprint("max_iter must be positive");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update max_iter
+  work->settings->max_iter = max_iter_new;
+
+  return 0;
+}
+
+c_int osqp_update_eps_abs(OSQPWorkspace *work, c_float eps_abs_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that eps_abs is positive
+  if (eps_abs_new < 0.) {
+#ifdef PRINTING
+    c_eprint("eps_abs must be nonnegative");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update eps_abs
+  work->settings->eps_abs = eps_abs_new;
+
+  return 0;
+}
+
+c_int osqp_update_eps_rel(OSQPWorkspace *work, c_float eps_rel_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that eps_rel is positive
+  if (eps_rel_new < 0.) {
+#ifdef PRINTING
+    c_eprint("eps_rel must be nonnegative");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update eps_rel
+  work->settings->eps_rel = eps_rel_new;
+
+  return 0;
+}
+
+c_int osqp_update_eps_prim_inf(OSQPWorkspace *work, c_float eps_prim_inf_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that eps_prim_inf is positive
+  if (eps_prim_inf_new < 0.) {
+#ifdef PRINTING
+    c_eprint("eps_prim_inf must be nonnegative");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update eps_prim_inf
+  work->settings->eps_prim_inf = eps_prim_inf_new;
+
+  return 0;
+}
+
+c_int osqp_update_eps_dual_inf(OSQPWorkspace *work, c_float eps_dual_inf_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that eps_dual_inf is positive
+  if (eps_dual_inf_new < 0.) {
+#ifdef PRINTING
+    c_eprint("eps_dual_inf must be nonnegative");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update eps_dual_inf
+  work->settings->eps_dual_inf = eps_dual_inf_new;
+
+
+  return 0;
+}
+
+c_int osqp_update_alpha(OSQPWorkspace *work, c_float alpha_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that alpha is between 0 and 2
+  if ((alpha_new <= 0.) || (alpha_new >= 2.)) {
+#ifdef PRINTING
+    c_eprint("alpha must be between 0 and 2");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update alpha
+  work->settings->alpha = alpha_new;
+
+  return 0;
+}
+
+c_int osqp_update_warm_start(OSQPWorkspace *work, c_int warm_start_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that warm_start is either 0 or 1
+  if ((warm_start_new != 0) && (warm_start_new != 1)) {
+#ifdef PRINTING
+    c_eprint("warm_start should be either 0 or 1");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update warm_start
+  work->settings->warm_start = warm_start_new;
+
+  return 0;
+}
+
+c_int osqp_update_scaled_termination(OSQPWorkspace *work, c_int scaled_termination_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that scaled_termination is either 0 or 1
+  if ((scaled_termination_new != 0) && (scaled_termination_new != 1)) {
+#ifdef PRINTING
+    c_eprint("scaled_termination should be either 0 or 1");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update scaled_termination
+  work->settings->scaled_termination = scaled_termination_new;
+
+  return 0;
+}
+
+c_int osqp_update_check_termination(OSQPWorkspace *work, c_int check_termination_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that check_termination is nonnegative
+  if (check_termination_new < 0) {
+#ifdef PRINTING
+    c_eprint("check_termination should be nonnegative");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update check_termination
+  work->settings->check_termination = check_termination_new;
+
+  return 0;
+}
+
+#ifndef EMBEDDED
+
+c_int osqp_update_delta(OSQPWorkspace *work, c_float delta_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that delta is positive
+  if (delta_new <= 0.) {
+# ifdef PRINTING
+    c_eprint("delta must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update delta
+  work->settings->delta = delta_new;
+
+  return 0;
+}
+
+c_int osqp_update_polish(OSQPWorkspace *work, c_int polish_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that polish is either 0 or 1
+  if ((polish_new != 0) && (polish_new != 1)) {
+# ifdef PRINTING
+    c_eprint("polish should be either 0 or 1");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update polish
+  work->settings->polish = polish_new;
+
+# ifdef PROFILING
+
+  // Reset polish time to zero
+  work->info->polish_time = 0.0;
+# endif /* ifdef PROFILING */
+
+  return 0;
+}
+
+c_int osqp_update_polish_refine_iter(OSQPWorkspace *work, c_int polish_refine_iter_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that polish_refine_iter is nonnegative
+  if (polish_refine_iter_new < 0) {
+# ifdef PRINTING
+    c_eprint("polish_refine_iter must be nonnegative");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update polish_refine_iter
+  work->settings->polish_refine_iter = polish_refine_iter_new;
+
+  return 0;
+}
+
+c_int osqp_update_verbose(OSQPWorkspace *work, c_int verbose_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that verbose is either 0 or 1
+  if ((verbose_new != 0) && (verbose_new != 1)) {
+# ifdef PRINTING
+    c_eprint("verbose should be either 0 or 1");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update verbose
+  work->settings->verbose = verbose_new;
+
+  return 0;
+}
+
+#endif // EMBEDDED
+
+#ifdef PROFILING
+
+c_int osqp_update_time_limit(OSQPWorkspace *work, c_float time_limit_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that time_limit is nonnegative
+  if (time_limit_new < 0.) {
+# ifdef PRINTING
+    c_print("time_limit must be nonnegative\n");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update time_limit
+  work->settings->time_limit = time_limit_new;
+
+  return 0;
+}
+#endif /* ifdef PROFILING */
diff --git a/src/polish.c b/src/polish.c
new file mode 100644
index 0000000..55df72d
--- /dev/null
+++ b/src/polish.c
@@ -0,0 +1,350 @@
+#include "polish.h"
+#include "lin_alg.h"
+#include "util.h"
+#include "auxil.h"
+#include "lin_sys.h"
+#include "kkt.h"
+#include "proj.h"
+#include "error.h"
+
+/**
+ * Form reduced matrix A that contains only rows that are active at the
+ * solution.
+ * Ared = vstack[Alow, Aupp]
+ * Active constraints are guessed from the primal and dual solution returned by
+ * the ADMM.
+ * @param  work Workspace
+ * @return      Number of rows in Ared, negative if error
+ */
+static c_int form_Ared(OSQPWorkspace *work) {
+  c_int j, ptr;
+  c_int Ared_nnz = 0;
+
+  // Initialize counters for active constraints
+  work->pol->n_low = 0;
+  work->pol->n_upp = 0;
+
+  /* Guess which linear constraints are lower-active, upper-active and free
+   *    A_to_Alow[j] = -1    (if j-th row of A is not inserted in Alow)
+   *    A_to_Alow[j] =  i    (if j-th row of A is inserted at i-th row of Alow)
+   * Aupp is formed in the equivalent way.
+   * Ared is formed by stacking vertically Alow and Aupp.
+   */
+  for (j = 0; j < work->data->m; j++) {
+    if (work->z[j] - work->data->l[j] < -work->y[j]) { // lower-active
+      work->pol->Alow_to_A[work->pol->n_low] = j;
+      work->pol->A_to_Alow[j]                = work->pol->n_low++;
+    } else {
+      work->pol->A_to_Alow[j] = -1;
+    }
+  }
+
+  for (j = 0; j < work->data->m; j++) {
+    if (work->data->u[j] - work->z[j] < work->y[j]) { // upper-active
+      work->pol->Aupp_to_A[work->pol->n_upp] = j;
+      work->pol->A_to_Aupp[j]                = work->pol->n_upp++;
+    } else {
+      work->pol->A_to_Aupp[j] = -1;
+    }
+  }
+
+  // Check if there are no active constraints
+  if (work->pol->n_low + work->pol->n_upp == 0) {
+    // Form empty Ared
+    work->pol->Ared = csc_spalloc(0, work->data->n, 0, 1, 0);
+    if (!(work->pol->Ared)) return -1;
+    int_vec_set_scalar(work->pol->Ared->p, 0, work->data->n + 1);
+    return 0; // mred = 0
+  }
+
+  // Count number of elements in Ared
+  for (j = 0; j < work->data->A->p[work->data->A->n]; j++) {
+    if ((work->pol->A_to_Alow[work->data->A->i[j]] != -1) ||
+        (work->pol->A_to_Aupp[work->data->A->i[j]] != -1)) Ared_nnz++;
+  }
+
+  // Form Ared
+  // Ared = vstack[Alow, Aupp]
+  work->pol->Ared = csc_spalloc(work->pol->n_low + work->pol->n_upp,
+                                work->data->n, Ared_nnz, 1, 0);
+  if (!(work->pol->Ared)) return -1;
+  Ared_nnz = 0; // counter
+
+  for (j = 0; j < work->data->n; j++) { // Cycle over columns of A
+    work->pol->Ared->p[j] = Ared_nnz;
+
+    for (ptr = work->data->A->p[j]; ptr < work->data->A->p[j + 1]; ptr++) {
+      // Cycle over elements in j-th column
+      if (work->pol->A_to_Alow[work->data->A->i[ptr]] != -1) {
+        // Lower-active rows of A
+        work->pol->Ared->i[Ared_nnz] =
+          work->pol->A_to_Alow[work->data->A->i[ptr]];
+        work->pol->Ared->x[Ared_nnz++] = work->data->A->x[ptr];
+      } else if (work->pol->A_to_Aupp[work->data->A->i[ptr]] != -1) {
+        // Upper-active rows of A
+        work->pol->Ared->i[Ared_nnz] = work->pol->A_to_Aupp[work->data->A->i[ptr]] \
+                                       + work->pol->n_low;
+        work->pol->Ared->x[Ared_nnz++] = work->data->A->x[ptr];
+      }
+    }
+  }
+
+  // Update the last element in Ared->p
+  work->pol->Ared->p[work->data->n] = Ared_nnz;
+
+  // Return number of rows in Ared
+  return work->pol->n_low + work->pol->n_upp;
+}
+
+/**
+ * Form reduced right-hand side rhs_red = vstack[-q, l_low, u_upp]
+ * @param  work Workspace
+ * @param  rhs  right-hand-side
+ * @return      reduced rhs
+ */
+static void form_rhs_red(OSQPWorkspace *work, c_float *rhs) {
+  c_int j;
+
+  // Form the rhs of the reduced KKT linear system
+  for (j = 0; j < work->data->n; j++) { // -q
+    rhs[j] = -work->data->q[j];
+  }
+
+  for (j = 0; j < work->pol->n_low; j++) { // l_low
+    rhs[work->data->n + j] = work->data->l[work->pol->Alow_to_A[j]];
+  }
+
+  for (j = 0; j < work->pol->n_upp; j++) { // u_upp
+    rhs[work->data->n + work->pol->n_low + j] =
+      work->data->u[work->pol->Aupp_to_A[j]];
+  }
+}
+
+/**
+ * Perform iterative refinement on the polished solution:
+ *    (repeat)
+ *    1. (K + dK) * dz = b - K*z
+ *    2. z <- z + dz
+ * @param  work Solver workspace
+ * @param  p    Private variable for solving linear system
+ * @param  z    Initial z value
+ * @param  b    RHS of the linear system
+ * @return      Exitflag
+ */
+static c_int iterative_refinement(OSQPWorkspace *work,
+                                  LinSysSolver  *p,
+                                  c_float       *z,
+                                  c_float       *b) {
+  c_int i, j, n;
+  c_float *rhs;
+
+  if (work->settings->polish_refine_iter > 0) {
+
+    // Assign dimension n
+    n = work->data->n + work->pol->Ared->m;
+
+    // Allocate rhs vector
+    rhs = (c_float *)c_malloc(sizeof(c_float) * n);
+
+    if (!rhs) {
+      return osqp_error(OSQP_MEM_ALLOC_ERROR);
+    } else {
+      for (i = 0; i < work->settings->polish_refine_iter; i++) {
+        // Form the RHS for the iterative refinement:  b - K*z
+        prea_vec_copy(b, rhs, n);
+
+        // Upper Part: R^{n}
+        // -= Px (upper triang)
+        mat_vec(work->data->P, z, rhs, -1);
+
+        // -= Px (lower triang)
+        mat_tpose_vec(work->data->P, z, rhs, -1, 1);
+
+        // -= Ared'*y_red
+        mat_tpose_vec(work->pol->Ared, z + work->data->n, rhs, -1, 0);
+
+        // Lower Part: R^{m}
+        mat_vec(work->pol->Ared, z, rhs + work->data->n, -1);
+
+        // Solve linear system. Store solution in rhs
+        p->solve(p, rhs);
+
+        // Update solution
+        for (j = 0; j < n; j++) {
+          z[j] += rhs[j];
+        }
+      }
+    }
+    if (rhs) c_free(rhs);
+  }
+  return 0;
+}
+
+/**
+ * Compute dual variable y from yred
+ * @param work Workspace
+ * @param yred Dual variables associated to active constraints
+ */
+static void get_ypol_from_yred(OSQPWorkspace *work, c_float *yred) {
+  c_int j;
+
+  // If there are no active constraints
+  if (work->pol->n_low + work->pol->n_upp == 0) {
+    vec_set_scalar(work->pol->y, 0., work->data->m);
+    return;
+  }
+
+  // NB: yred = vstack[ylow, yupp]
+  for (j = 0; j < work->data->m; j++) {
+    if (work->pol->A_to_Alow[j] != -1) {
+      // lower-active
+      work->pol->y[j] = yred[work->pol->A_to_Alow[j]];
+    } else if (work->pol->A_to_Aupp[j] != -1) {
+      // upper-active
+      work->pol->y[j] = yred[work->pol->A_to_Aupp[j] + work->pol->n_low];
+    } else {
+      // inactive
+      work->pol->y[j] = 0.0;
+    }
+  }
+}
+
+c_int polish(OSQPWorkspace *work) {
+  c_int mred, polish_successful, exitflag;
+  c_float *rhs_red;
+  LinSysSolver *plsh;
+  c_float *pol_sol; // Polished solution
+
+#ifdef PROFILING
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  // Form Ared by assuming the active constraints and store in work->pol->Ared
+  mred = form_Ared(work);
+  if (mred < 0) { // work->pol->red = OSQP_NULL
+    // Polishing failed
+    work->info->status_polish = -1;
+
+    return -1;
+  }
+
+  // Form and factorize reduced KKT
+  exitflag = init_linsys_solver(&plsh, work->data->P, work->pol->Ared,
+                                work->settings->delta, OSQP_NULL,
+                                work->settings->linsys_solver, 1);
+
+  if (exitflag) {
+    // Polishing failed
+    work->info->status_polish = -1;
+
+    // Memory clean-up
+    if (work->pol->Ared) csc_spfree(work->pol->Ared);
+
+    return 1;
+  }
+
+  // Form reduced right-hand side rhs_red
+  rhs_red = c_malloc(sizeof(c_float) * (work->data->n + mred));
+  if (!rhs_red) {
+    // Polishing failed
+    work->info->status_polish = -1;
+
+    // Memory clean-up
+    csc_spfree(work->pol->Ared);
+
+    return -1;
+  }
+  form_rhs_red(work, rhs_red);
+
+  pol_sol = vec_copy(rhs_red, work->data->n + mred);
+  if (!pol_sol) {
+    // Polishing failed
+    work->info->status_polish = -1;
+
+    // Memory clean-up
+    csc_spfree(work->pol->Ared);
+    c_free(rhs_red);
+  
+    return -1;
+  }
+
+  // Solve the reduced KKT system
+  plsh->solve(plsh, pol_sol);
+
+  // Perform iterative refinement to compensate for the regularization error
+  exitflag = iterative_refinement(work, plsh, pol_sol, rhs_red);
+
+  if (exitflag) {
+    // Polishing failed
+    work->info->status_polish = -1;
+
+    // Memory clean-up
+    csc_spfree(work->pol->Ared);
+    c_free(rhs_red);
+    c_free(pol_sol);
+  
+    return -1;
+  }
+
+  // Store the polished solution (x,z,y)
+  prea_vec_copy(pol_sol, work->pol->x, work->data->n);   // pol->x
+  mat_vec(work->data->A, work->pol->x, work->pol->z, 0); // pol->z
+  get_ypol_from_yred(work, pol_sol + work->data->n);     // pol->y
+
+  // Ensure (z,y) satisfies normal cone constraint
+  project_normalcone(work, work->pol->z, work->pol->y);
+
+  // Compute primal and dual residuals at the polished solution
+  update_info(work, 0, 1, 1);
+
+  // Check if polish was successful
+  polish_successful = (work->pol->pri_res < work->info->pri_res &&
+                       work->pol->dua_res < work->info->dua_res) || // Residuals
+                                                                    // are
+                                                                    // reduced
+                      (work->pol->pri_res < work->info->pri_res &&
+                       work->info->dua_res < 1e-10) ||              // Dual
+                                                                    // residual
+                                                                    // already
+                                                                    // tiny
+                      (work->pol->dua_res < work->info->dua_res &&
+                       work->info->pri_res < 1e-10);                // Primal
+                                                                    // residual
+                                                                    // already
+                                                                    // tiny
+
+  if (polish_successful) {
+    // Update solver information
+    work->info->obj_val       = work->pol->obj_val;
+    work->info->pri_res       = work->pol->pri_res;
+    work->info->dua_res       = work->pol->dua_res;
+    work->info->status_polish = 1;
+
+    // Update (x, z, y) in ADMM iterations
+    // NB: z needed for warm starting
+    prea_vec_copy(work->pol->x, work->x, work->data->n);
+    prea_vec_copy(work->pol->z, work->z, work->data->m);
+    prea_vec_copy(work->pol->y, work->y, work->data->m);
+
+    // Print summary
+#ifdef PRINTING
+
+    if (work->settings->verbose) print_polish(work);
+#endif /* ifdef PRINTING */
+  } else { // Polishing failed
+    work->info->status_polish = -1;
+
+    // TODO: Try to find a better solution on the line connecting ADMM
+    //       and polished solution
+  }
+
+  // Memory clean-up
+  plsh->free(plsh);
+
+  // Checks that they are not NULL are already performed earlier
+  csc_spfree(work->pol->Ared);
+  c_free(rhs_red);
+  c_free(pol_sol);
+
+  return 0;
+}
diff --git a/src/proj.c b/src/proj.c
new file mode 100644
index 0000000..c7fdb45
--- /dev/null
+++ b/src/proj.c
@@ -0,0 +1,29 @@
+#include "proj.h"
+
+
+void project(OSQPWorkspace *work, c_float *z) {
+  c_int i, m;
+
+  m = work->data->m;
+
+  for (i = 0; i < m; i++) {
+    z[i] = c_min(c_max(z[i],
+                       work->data->l[i]), // Between lower
+                 work->data->u[i]);       // and upper bounds
+  }
+}
+
+void project_normalcone(OSQPWorkspace *work, c_float *z, c_float *y) {
+  c_int i, m;
+
+  // NB: Use z_prev as temporary vector
+
+  m = work->data->m;
+
+  for (i = 0; i < m; i++) {
+    work->z_prev[i] = z[i] + y[i];
+    z[i]            = c_min(c_max(work->z_prev[i], work->data->l[i]),
+                            work->data->u[i]);
+    y[i] = work->z_prev[i] - z[i];
+  }
+}
diff --git a/src/scaling.c b/src/scaling.c
new file mode 100644
index 0000000..74616a7
--- /dev/null
+++ b/src/scaling.c
@@ -0,0 +1,192 @@
+#include "scaling.h"
+
+#if EMBEDDED != 1
+
+
+// Set values lower than threshold SCALING_REG to 1
+void limit_scaling(c_float *D, c_int n) {
+  c_int i;
+
+  for (i = 0; i < n; i++) {
+    D[i] = D[i] < MIN_SCALING ? 1.0 : D[i];
+    D[i] = D[i] > MAX_SCALING ? MAX_SCALING : D[i];
+  }
+}
+
+/**
+ * Compute infinite norm of the columns of the KKT matrix without forming it
+ *
+ * The norm is stored in the vector v = (D, E)
+ *
+ * @param P        Cost matrix
+ * @param A        Constraints matrix
+ * @param D        Norm of columns related to variables
+ * @param D_temp_A Temporary vector for norm of columns of A
+ * @param E        Norm of columns related to constraints
+ * @param n        Dimension of KKT matrix
+ */
+void compute_inf_norm_cols_KKT(const csc *P, const csc *A,
+                               c_float *D, c_float *D_temp_A,
+                               c_float *E, c_int n) {
+  // First half
+  //  [ P ]
+  //  [ A ]
+  mat_inf_norm_cols_sym_triu(P, D);
+  mat_inf_norm_cols(A, D_temp_A);
+  vec_ew_max_vec(D, D_temp_A, D, n);
+
+  // Second half
+  //  [ A']
+  //  [ 0 ]
+  mat_inf_norm_rows(A, E);
+}
+
+c_int scale_data(OSQPWorkspace *work) {
+  // Scale KKT matrix
+  //
+  //    [ P   A']
+  //    [ A   0 ]
+  //
+  // with diagonal matrix
+  //
+  //  S = [ D    ]
+  //      [    E ]
+  //
+
+  c_int   i;          // Iterations index
+  c_int   n, m;       // Number of constraints and variables
+  c_float c_temp;     // Cost function scaling
+  c_float inf_norm_q; // Infinity norm of q
+
+  n = work->data->n;
+  m = work->data->m;
+
+  // Initialize scaling to 1
+  work->scaling->c = 1.0;
+  vec_set_scalar(work->scaling->D,    1., work->data->n);
+  vec_set_scalar(work->scaling->Dinv, 1., work->data->n);
+  vec_set_scalar(work->scaling->E,    1., work->data->m);
+  vec_set_scalar(work->scaling->Einv, 1., work->data->m);
+
+
+  for (i = 0; i < work->settings->scaling; i++) {
+    //
+    // First Ruiz step
+    //
+
+    // Compute norm of KKT columns
+    compute_inf_norm_cols_KKT(work->data->P, work->data->A,
+                              work->D_temp, work->D_temp_A,
+                              work->E_temp, n);
+
+    // Set to 1 values with 0 norms (avoid crazy scaling)
+    limit_scaling(work->D_temp, n);
+    limit_scaling(work->E_temp, m);
+
+    // Take square root of norms
+    vec_ew_sqrt(work->D_temp, n);
+    vec_ew_sqrt(work->E_temp, m);
+
+    // Divide scalings D and E by themselves
+    vec_ew_recipr(work->D_temp, work->D_temp, n);
+    vec_ew_recipr(work->E_temp, work->E_temp, m);
+
+    // Equilibrate matrices P and A and vector q
+    // P <- DPD
+    mat_premult_diag(work->data->P, work->D_temp);
+    mat_postmult_diag(work->data->P, work->D_temp);
+
+    // A <- EAD
+    mat_premult_diag(work->data->A, work->E_temp);
+    mat_postmult_diag(work->data->A, work->D_temp);
+
+    // q <- Dq
+    vec_ew_prod(work->D_temp,     work->data->q, work->data->q,    n);
+
+    // Update equilibration matrices D and E
+    vec_ew_prod(work->scaling->D, work->D_temp,  work->scaling->D, n);
+    vec_ew_prod(work->scaling->E, work->E_temp,  work->scaling->E, m);
+
+    //
+    // Cost normalization step
+    //
+
+    // Compute avg norm of cols of P
+    mat_inf_norm_cols_sym_triu(work->data->P, work->D_temp);
+    c_temp = vec_mean(work->D_temp, n);
+
+    // Compute inf norm of q
+    inf_norm_q = vec_norm_inf(work->data->q, n);
+
+    // If norm_q == 0, set it to 1 (ignore it in the scaling)
+    // NB: Using the same function as with vectors here
+    limit_scaling(&inf_norm_q, 1);
+
+    // Compute max between avg norm of cols of P and inf norm of q
+    c_temp = c_max(c_temp, inf_norm_q);
+
+    // Limit scaling (use same function as with vectors)
+    limit_scaling(&c_temp, 1);
+
+    // Invert scaling c = 1 / cost_measure
+    c_temp = 1. / c_temp;
+
+    // Scale P
+    mat_mult_scalar(work->data->P, c_temp);
+
+    // Scale q
+    vec_mult_scalar(work->data->q, c_temp, n);
+
+    // Update cost scaling
+    work->scaling->c *= c_temp;
+  }
+
+
+  // Store cinv, Dinv, Einv
+  work->scaling->cinv = 1. / work->scaling->c;
+  vec_ew_recipr(work->scaling->D, work->scaling->Dinv, work->data->n);
+  vec_ew_recipr(work->scaling->E, work->scaling->Einv, work->data->m);
+
+
+  // Scale problem vectors l, u
+  vec_ew_prod(work->scaling->E, work->data->l, work->data->l, work->data->m);
+  vec_ew_prod(work->scaling->E, work->data->u, work->data->u, work->data->m);
+
+  return 0;
+}
+
+#endif // EMBEDDED
+
+c_int unscale_data(OSQPWorkspace *work) {
+  // Unscale cost
+  mat_mult_scalar(work->data->P, work->scaling->cinv);
+  mat_premult_diag(work->data->P, work->scaling->Dinv);
+  mat_postmult_diag(work->data->P, work->scaling->Dinv);
+  vec_mult_scalar(work->data->q, work->scaling->cinv, work->data->n);
+  vec_ew_prod(work->scaling->Dinv, work->data->q, work->data->q, work->data->n);
+
+  // Unscale constraints
+  mat_premult_diag(work->data->A, work->scaling->Einv);
+  mat_postmult_diag(work->data->A, work->scaling->Dinv);
+  vec_ew_prod(work->scaling->Einv, work->data->l, work->data->l, work->data->m);
+  vec_ew_prod(work->scaling->Einv, work->data->u, work->data->u, work->data->m);
+
+  return 0;
+}
+
+c_int unscale_solution(OSQPWorkspace *work) {
+  // primal
+  vec_ew_prod(work->scaling->D,
+              work->solution->x,
+              work->solution->x,
+              work->data->n);
+
+  // dual
+  vec_ew_prod(work->scaling->E,
+              work->solution->y,
+              work->solution->y,
+              work->data->m);
+  vec_mult_scalar(work->solution->y, work->scaling->cinv, work->data->m);
+
+  return 0;
+}
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..8e8d04a
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,487 @@
+#include "util.h"
+
+/***************
+* Versioning  *
+***************/
+const char* osqp_version(void) {
+  return OSQP_VERSION;
+}
+
+/************************************
+* Printing Constants to set Layout *
+************************************/
+#ifdef PRINTING
+# define HEADER_LINE_LEN 65
+#endif /* ifdef PRINTING */
+
+/**********************
+* Utility Functions  *
+**********************/
+void c_strcpy(char dest[], const char source[]) {
+  int i = 0;
+
+  while (1) {
+    dest[i] = source[i];
+
+    if (dest[i] == '\0') break;
+    i++;
+  }
+}
+
+#ifdef PRINTING
+
+static void print_line(void) {
+  char  the_line[HEADER_LINE_LEN + 1];
+  c_int i;
+
+  for (i = 0; i < HEADER_LINE_LEN; ++i) the_line[i] = '-';
+  the_line[HEADER_LINE_LEN] = '\0';
+  c_print("%s\n", the_line);
+}
+
+void print_header(void) {
+  // Different indentation required for windows
+#if defined(IS_WINDOWS) && !defined(PYTHON)
+  c_print("iter  ");
+#else
+  c_print("iter   ");
+#endif
+
+  // Main information
+  c_print("objective    pri res    dua res    rho");
+# ifdef PROFILING
+  c_print("        time");
+# endif /* ifdef PROFILING */
+  c_print("\n");
+}
+
+void print_setup_header(const OSQPWorkspace *work) {
+  OSQPData *data;
+  OSQPSettings *settings;
+  c_int nnz; // Number of nonzeros in the problem
+
+  data     = work->data;
+  settings = work->settings;
+
+  // Number of nonzeros
+  nnz = data->P->p[data->P->n] + data->A->p[data->A->n];
+
+  print_line();
+  c_print("           OSQP v%s  -  Operator Splitting QP Solver\n"
+          "              (c) Bartolomeo Stellato,  Goran Banjac\n"
+          "        University of Oxford  -  Stanford University 2021\n",
+          OSQP_VERSION);
+  print_line();
+
+  // Print variables and constraints
+  c_print("problem:  ");
+  c_print("variables n = %i, constraints m = %i\n          ",
+                                    (int)data->n,
+          (int)data->m);
+  c_print("nnz(P) + nnz(A) = %i\n", (int)nnz);
+
+  // Print Settings
+  c_print("settings: ");
+  c_print("linear system solver = %s",
+          LINSYS_SOLVER_NAME[settings->linsys_solver]);
+
+  if (work->linsys_solver->nthreads != 1) {
+    c_print(" (%d threads)", (int)work->linsys_solver->nthreads);
+  }
+  c_print(",\n          ");
+
+  c_print("eps_abs = %.1e, eps_rel = %.1e,\n          ",
+          settings->eps_abs, settings->eps_rel);
+  c_print("eps_prim_inf = %.1e, eps_dual_inf = %.1e,\n          ",
+          settings->eps_prim_inf, settings->eps_dual_inf);
+  c_print("rho = %.2e ", settings->rho);
+
+  if (settings->adaptive_rho) {
+    c_print("(adaptive)");
+  }
+  c_print(",\n          ");
+  c_print("sigma = %.2e, alpha = %.2f, ",
+          settings->sigma, settings->alpha);
+  c_print("max_iter = %i\n", (int)settings->max_iter);
+
+  if (settings->check_termination) {
+    c_print("          check_termination: on (interval %i),\n",
+      (int)settings->check_termination);
+  } else {c_print("          check_termination: off,\n");}
+# ifdef PROFILING
+  if (settings->time_limit) {
+    c_print("          time_limit: %.2e sec,\n", settings->time_limit);
+  }
+# endif /* ifdef PROFILING */
+
+  if (settings->scaling) {
+    c_print("          scaling: on, ");
+  } else {
+    c_print("          scaling: off, ");
+  }
+
+  if (settings->scaled_termination) {
+    c_print("scaled_termination: on\n");
+  } else {
+    c_print("scaled_termination: off\n");
+  }
+
+  if (settings->warm_start) {
+    c_print("          warm start: on, ");
+  } else {
+    c_print("          warm start: off, ");
+  }
+
+  if (settings->polish) {
+    c_print("polish: on, ");
+  } else {
+    c_print("polish: off, ");
+  }
+
+# ifdef PROFILING
+  if (settings->time_limit) {
+    c_print("time_limit: %.2e sec\n", settings->time_limit);
+  } else {
+    c_print("time_limit: off\n");
+  }
+# endif
+
+  c_print("\n");
+}
+
+void print_summary(OSQPWorkspace *work) {
+  OSQPInfo *info;
+
+  info = work->info;
+
+  c_print("%4i",     (int)info->iter);
+  c_print(" %12.4e", info->obj_val);
+  c_print("  %9.2e", info->pri_res);
+  c_print("  %9.2e", info->dua_res);
+  c_print("  %9.2e", work->settings->rho);
+# ifdef PROFILING
+
+  if (work->first_run) {
+    // total time: setup + solve
+    c_print("  %9.2es", info->setup_time + info->solve_time);
+  } else {
+    // total time: update + solve
+    c_print("  %9.2es", info->update_time + info->solve_time);
+  }
+# endif /* ifdef PROFILING */
+  c_print("\n");
+
+  work->summary_printed = 1; // Summary has been printed
+}
+
+void print_polish(OSQPWorkspace *work) {
+  OSQPInfo *info;
+
+  info = work->info;
+
+  c_print("%4s",     "plsh");
+  c_print(" %12.4e", info->obj_val);
+  c_print("  %9.2e", info->pri_res);
+  c_print("  %9.2e", info->dua_res);
+
+  // Different characters for windows/unix
+#if defined(IS_WINDOWS) && !defined(PYTHON)
+  c_print("  ---------");
+#else
+  c_print("   --------");
+#endif
+
+# ifdef PROFILING
+  if (work->first_run) {
+    // total time: setup + solve
+    c_print("  %9.2es", info->setup_time + info->solve_time +
+            info->polish_time);
+  } else {
+    // total time: update + solve
+    c_print("  %9.2es", info->update_time + info->solve_time +
+            info->polish_time);
+  }
+# endif /* ifdef PROFILING */
+  c_print("\n");
+}
+
+void print_footer(OSQPInfo *info, c_int polish) {
+  c_print("\n"); // Add space after iterations
+
+  c_print("status:               %s\n", info->status);
+
+  if (polish && (info->status_val == OSQP_SOLVED)) {
+    if (info->status_polish == 1) {
+      c_print("solution polish:      successful\n");
+    } else if (info->status_polish < 0) {
+      c_print("solution polish:      unsuccessful\n");
+    }
+  }
+
+  c_print("number of iterations: %i\n", (int)info->iter);
+
+  if ((info->status_val == OSQP_SOLVED) ||
+      (info->status_val == OSQP_SOLVED_INACCURATE)) {
+    c_print("optimal objective:    %.4f\n", info->obj_val);
+  }
+
+# ifdef PROFILING
+  c_print("run time:             %.2es\n", info->run_time);
+# endif /* ifdef PROFILING */
+
+# if EMBEDDED != 1
+  c_print("optimal rho estimate: %.2e\n", info->rho_estimate);
+# endif /* if EMBEDDED != 1 */
+  c_print("\n");
+}
+
+#endif /* End #ifdef PRINTING */
+
+
+#ifndef EMBEDDED
+
+OSQPSettings* copy_settings(const OSQPSettings *settings) {
+  OSQPSettings *new = c_malloc(sizeof(OSQPSettings));
+
+  if (!new) return OSQP_NULL;
+
+  // Copy settings
+  // NB. Copying them explicitly because memcpy is not
+  // defined when PRINTING is disabled (appears in string.h)
+  new->rho = settings->rho;
+  new->sigma = settings->sigma;
+  new->scaling = settings->scaling;
+
+# if EMBEDDED != 1
+  new->adaptive_rho = settings->adaptive_rho;
+  new->adaptive_rho_interval = settings->adaptive_rho_interval;
+  new->adaptive_rho_tolerance = settings->adaptive_rho_tolerance;
+# ifdef PROFILING
+  new->adaptive_rho_fraction = settings->adaptive_rho_fraction;
+# endif
+# endif // EMBEDDED != 1
+  new->max_iter = settings->max_iter;
+  new->eps_abs = settings->eps_abs;
+  new->eps_rel = settings->eps_rel;
+  new->eps_prim_inf = settings->eps_prim_inf;
+  new->eps_dual_inf = settings->eps_dual_inf;
+  new->alpha = settings->alpha;
+  new->linsys_solver = settings->linsys_solver;
+  new->delta = settings->delta;
+  new->polish = settings->polish;
+  new->polish_refine_iter = settings->polish_refine_iter;
+  new->verbose = settings->verbose;
+  new->scaled_termination = settings->scaled_termination;
+  new->check_termination = settings->check_termination;
+  new->warm_start = settings->warm_start;
+# ifdef PROFILING
+  new->time_limit = settings->time_limit;
+# endif
+
+  return new;
+}
+
+#endif // #ifndef EMBEDDED
+
+
+/*******************
+* Timer Functions *
+*******************/
+
+#ifdef PROFILING
+
+// Windows
+# ifdef IS_WINDOWS
+
+void osqp_tic(OSQPTimer *t)
+{
+  QueryPerformanceFrequency(&t->freq);
+  QueryPerformanceCounter(&t->tic);
+}
+
+c_float osqp_toc(OSQPTimer *t)
+{
+  QueryPerformanceCounter(&t->toc);
+  return (t->toc.QuadPart - t->tic.QuadPart) / (c_float)t->freq.QuadPart;
+}
+
+// Mac
+# elif defined IS_MAC
+
+void osqp_tic(OSQPTimer *t)
+{
+  /* read current clock cycles */
+  t->tic = mach_absolute_time();
+}
+
+c_float osqp_toc(OSQPTimer *t)
+{
+  uint64_t duration; /* elapsed time in clock cycles*/
+
+  t->toc   = mach_absolute_time();
+  duration = t->toc - t->tic;
+
+  /*conversion from clock cycles to nanoseconds*/
+  mach_timebase_info(&(t->tinfo));
+  duration *= t->tinfo.numer;
+  duration /= t->tinfo.denom;
+
+  return (c_float)duration / 1e9;
+}
+
+// Linux
+# else  /* ifdef IS_WINDOWS */
+
+/* read current time */
+void osqp_tic(OSQPTimer *t)
+{
+  clock_gettime(CLOCK_MONOTONIC, &t->tic);
+}
+
+/* return time passed since last call to tic on this timer */
+c_float osqp_toc(OSQPTimer *t)
+{
+  struct timespec temp;
+
+  clock_gettime(CLOCK_MONOTONIC, &t->toc);
+
+  if ((t->toc.tv_nsec - t->tic.tv_nsec) < 0) {
+    temp.tv_sec  = t->toc.tv_sec - t->tic.tv_sec - 1;
+    temp.tv_nsec = 1e9 + t->toc.tv_nsec - t->tic.tv_nsec;
+  } else {
+    temp.tv_sec  = t->toc.tv_sec - t->tic.tv_sec;
+    temp.tv_nsec = t->toc.tv_nsec - t->tic.tv_nsec;
+  }
+  return (c_float)temp.tv_sec + (c_float)temp.tv_nsec / 1e9;
+}
+
+# endif /* ifdef IS_WINDOWS */
+
+#endif // If Profiling end
+
+
+/* ==================== DEBUG FUNCTIONS ======================= */
+
+
+
+// If debug mode enabled
+#ifdef DDEBUG
+
+#ifdef PRINTING
+
+void print_csc_matrix(csc *M, const char *name)
+{
+  c_int j, i, row_start, row_stop;
+  c_int k = 0;
+
+  // Print name
+  c_print("%s :\n", name);
+
+  for (j = 0; j < M->n; j++) {
+    row_start = M->p[j];
+    row_stop  = M->p[j + 1];
+
+    if (row_start == row_stop) continue;
+    else {
+      for (i = row_start; i < row_stop; i++) {
+        c_print("\t[%3u,%3u] = %.3g\n", (int)M->i[i], (int)j, M->x[k++]);
+      }
+    }
+  }
+}
+
+void dump_csc_matrix(csc *M, const char *file_name) {
+  c_int j, i, row_strt, row_stop;
+  c_int k = 0;
+  FILE *f = fopen(file_name, "w");
+
+  if (f != NULL) {
+    for (j = 0; j < M->n; j++) {
+      row_strt = M->p[j];
+      row_stop = M->p[j + 1];
+
+      if (row_strt == row_stop) continue;
+      else {
+        for (i = row_strt; i < row_stop; i++) {
+          fprintf(f, "%d\t%d\t%20.18e\n",
+                  (int)M->i[i] + 1, (int)j + 1, M->x[k++]);
+        }
+      }
+    }
+    fprintf(f, "%d\t%d\t%20.18e\n", (int)M->m, (int)M->n, 0.0);
+    fclose(f);
+    c_print("File %s successfully written.\n", file_name);
+  } else {
+    c_eprint("Error during writing file %s.\n", file_name);
+  }
+}
+
+void print_trip_matrix(csc *M, const char *name)
+{
+  c_int k = 0;
+
+  // Print name
+  c_print("%s :\n", name);
+
+  for (k = 0; k < M->nz; k++) {
+    c_print("\t[%3u, %3u] = %.3g\n", (int)M->i[k], (int)M->p[k], M->x[k]);
+  }
+}
+
+void print_dns_matrix(c_float *M, c_int m, c_int n, const char *name)
+{
+  c_int i, j;
+
+  c_print("%s : \n\t", name);
+
+  for (i = 0; i < m; i++) {   // Cycle over rows
+    for (j = 0; j < n; j++) { // Cycle over columns
+      if (j < n - 1)
+        // c_print("% 14.12e,  ", M[j*m+i]);
+        c_print("% .3g,  ", M[j * m + i]);
+
+      else
+        // c_print("% 14.12e;  ", M[j*m+i]);
+        c_print("% .3g;  ", M[j * m + i]);
+    }
+
+    if (i < m - 1) {
+      c_print("\n\t");
+    }
+  }
+  c_print("\n");
+}
+
+void print_vec(c_float *v, c_int n, const char *name) {
+  print_dns_matrix(v, 1, n, name);
+}
+
+void dump_vec(c_float *v, c_int len, const char *file_name) {
+  c_int i;
+  FILE *f = fopen(file_name, "w");
+
+  if (f != NULL) {
+    for (i = 0; i < len; i++) {
+      fprintf(f, "%20.18e\n", v[i]);
+    }
+    fclose(f);
+    c_print("File %s successfully written.\n", file_name);
+  } else {
+    c_print("Error during writing file %s.\n", file_name);
+  }
+}
+
+void print_vec_int(c_int *x, c_int n, const char *name) {
+  c_int i;
+
+  c_print("%s = [", name);
+
+  for (i = 0; i < n; i++) {
+    c_print(" %i ", (int)x[i]);
+  }
+  c_print("]\n");
+}
+
+#endif // PRINTING
+
+#endif // DEBUG MODE
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..6d0eb7d
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,3 @@
+# Data Files
+# -------------------------------------------------------------------
+**/*data.h
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..1a8aea8
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Add subdirectories of all the tests
+add_subdirectory(basic_qp)
+add_subdirectory(basic_qp2)
+add_subdirectory(lin_alg)
+add_subdirectory(non_cvx)
+add_subdirectory(primal_dual_infeasibility)
+add_subdirectory(primal_infeasibility)
+add_subdirectory(solve_linsys)
+add_subdirectory(unconstrained)
+add_subdirectory(update_matrices)
+
+set(test_headers ${headers} PARENT_SCOPE)
+set(codegen_test_headers ${codegen_headers} PARENT_SCOPE)
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..d245717
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,9 @@
+# C unittests
+
+Run
+
+```python
+python generate_tests_data.py
+```
+
+to generate the tests data for each unit test.
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/basic_qp/CMakeLists.txt b/tests/basic_qp/CMakeLists.txt
new file mode 100644
index 0000000..7adac5e
--- /dev/null
+++ b/tests/basic_qp/CMakeLists.txt
@@ -0,0 +1,13 @@
+get_directory_property(headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION headers)
+                        
+set(headers ${headers}
+    ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_qp.h PARENT_SCOPE)
+
+get_directory_property(codegen_headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION codegen_headers)
+
+set(codegen_headers ${codegen_headers}
+        ${CMAKE_CURRENT_SOURCE_DIR}/data.h PARENT_SCOPE)
diff --git a/tests/basic_qp/__init__.py b/tests/basic_qp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/basic_qp/__init__.py
diff --git a/tests/basic_qp/generate_problem.py b/tests/basic_qp/generate_problem.py
new file mode 100644
index 0000000..a8defb1
--- /dev/null
+++ b/tests/basic_qp/generate_problem.py
@@ -0,0 +1,30 @@
+import numpy as np
+from scipy import sparse
+import utils.codegen_utils as cu
+
+P = sparse.triu([[4., 1.], [1., 2.]], format='csc')
+q = np.ones(2)
+
+A = sparse.csc_matrix(np.array([[1., 1.], [1., 0.], [0., 1.], [0., 1.]]))
+l = np.array([1., 0., 0., -np.inf])
+u = np.array([1., 0.7, 0.7, np.inf])
+
+n = P.shape[0]
+m = A.shape[0]
+
+# New data
+q_new = np.array([2.5, 3.2])
+l_new = np.array([0.8, -3.4, -np.inf, 0.5])
+u_new = np.array([1.6, 1.0, np.inf, 0.5])
+
+# Generate problem solutions
+sols_data = {'x_test': np.array([0.3, 0.7]),
+             'y_test': np.array([-2.9, 0.0, 0.2, 0.0]),
+             'obj_value_test': 1.88,
+             'status_test': 'optimal',
+             'q_new': q_new,
+             'l_new': l_new,
+             'u_new': u_new}
+
+# Generate problem data
+cu.generate_problem_data(P, q, A, l, u, 'basic_qp', sols_data)
diff --git a/tests/basic_qp/test_basic_qp.h b/tests/basic_qp/test_basic_qp.h
new file mode 100644
index 0000000..3fb85f5
--- /dev/null
+++ b/tests/basic_qp/test_basic_qp.h
@@ -0,0 +1,904 @@
+#include "osqp.h"        // OSQP API
+#include "auxil.h"       // Needed for cold_start()
+#include "cs.h"          // CSC data structure
+#include "util.h"        // Utilities for testing
+#include "osqp_tester.h" // Basic testing script header
+
+#include "basic_qp/data.h"
+
+
+void test_basic_qp_solve()
+{
+  c_int exitflag, tmp_int;
+  c_float tmp_float;
+  csc *tmp_mat, *P_tmp;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  basic_qp_sols_data *sols_data;
+
+  // Populate data
+  data = generate_problem_basic_qp();
+  sols_data = generate_problem_basic_qp_sols_data();
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->max_iter   = 2000;
+  settings->alpha      = 1.6;
+  settings->polish     = 1;
+  settings->scaling    = 0;
+  settings->verbose    = 1;
+  settings->warm_start = 0;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Basic QP test solve: Setup error!", exitflag == 0);
+
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Basic QP test solve: Error in solver status!",
+	    work->info->status_val == sols_data->status_test);
+
+  // Compare primal solutions
+  mu_assert("Basic QP test solve: Error in primal solution!",
+	    vec_norm_inf_diff(work->solution->x, sols_data->x_test,
+			      data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Basic QP test solve: Error in dual solution!",
+	    vec_norm_inf_diff(work->solution->y, sols_data->y_test,
+			      data->m) < TESTS_TOL);
+
+
+  // Compare objective values
+  mu_assert("Basic QP test solve: Error in objective value!",
+	    c_absval(work->info->obj_val - sols_data->obj_value_test) <
+	    TESTS_TOL);
+
+  // Try to set wrong settings
+  mu_assert("Basic QP test solve: Wrong value of rho not caught!",
+	    osqp_update_rho(work, -0.1) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of max_iter not caught!",
+	    osqp_update_max_iter(work, -1) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of eps_abs not caught!",
+	    osqp_update_eps_abs(work, -1.) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of eps_rel not caught!",
+	    osqp_update_eps_rel(work, -1.) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of eps_prim_inf not caught!",
+	    osqp_update_eps_prim_inf(work, -0.1) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of eps_dual_inf not caught!",
+	    osqp_update_eps_dual_inf(work, -0.1) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of alpha not caught!",
+	    osqp_update_alpha(work, 2.0) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of warm_start not caught!",
+	    osqp_update_warm_start(work, -1) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of scaled_termination not caught!",
+	    osqp_update_scaled_termination(work, 2) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of check_termination not caught!",
+	    osqp_update_check_termination(work, -1) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of delta not caught!",
+	    osqp_update_delta(work, 0.) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of polish not caught!",
+	    osqp_update_polish(work, 2) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of polish_refine_iter not caught!",
+	    osqp_update_polish_refine_iter(work, -1) == 1);
+
+  mu_assert("Basic QP test solve: Wrong value of verbose not caught!",
+	    osqp_update_verbose(work, 2) == 1);
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+  /* =============================
+       SETUP WITH WRONG SETTINGS
+     ============================= */
+
+  // Setup workspace with empty settings
+  exitflag = osqp_setup(&work, data, OSQP_NULL);
+  mu_assert("Basic QP test solve: Setup should result in error due to empty settings",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+
+  // Setup workspace with a wrong number of scaling iterations
+  tmp_int = settings->scaling;
+  settings->scaling = -1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to a negative number of scaling iterations",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->scaling = tmp_int;
+
+  // Setup workspace with wrong settings->adaptive_rho
+  tmp_int = settings->adaptive_rho;
+  settings->adaptive_rho = 2;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-boolean settings->adaptive_rho",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->adaptive_rho = tmp_int;
+
+  // Setup workspace with wrong settings->adaptive_rho_interval
+  tmp_int = settings->adaptive_rho_interval;
+  settings->adaptive_rho_interval = -1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to negative settings->adaptive_rho_interval",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->adaptive_rho_interval = tmp_int;
+
+#ifdef PROFILING
+  // Setup workspace with wrong settings->adaptive_rho_fraction
+  tmp_float = settings->adaptive_rho_fraction;
+  settings->adaptive_rho_fraction = -1.5;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-positive settings->adaptive_rho_fraction",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->adaptive_rho_fraction = tmp_float;
+#endif
+
+  // Setup workspace with wrong settings->adaptive_rho_tolerance
+  tmp_float = settings->adaptive_rho_tolerance;
+  settings->adaptive_rho_tolerance = 0.5;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to wrong settings->adaptive_rho_tolerance",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->adaptive_rho_tolerance = tmp_float;
+
+  // Setup workspace with wrong settings->polish_refine_iter
+  tmp_int = settings->polish_refine_iter;
+  settings->polish_refine_iter = -3;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to negative settings->polish_refine_iter",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->polish_refine_iter = tmp_int;
+
+  // Setup workspace with wrong settings->rho
+  tmp_float = settings->rho;
+  settings->rho = 0.0;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-positive settings->rho",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->rho = tmp_float;
+
+  // Setup workspace with wrong settings->sigma
+  tmp_float = settings->sigma;
+  settings->sigma = -0.1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-positive settings->sigma",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->sigma = tmp_float;
+
+  // Setup workspace with wrong settings->delta
+  tmp_float = settings->delta;
+  settings->delta = -1.1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-positive settings->delta",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->delta = tmp_float;
+
+  // Setup workspace with wrong settings->max_iter
+  tmp_int = settings->max_iter;
+  settings->max_iter = 0;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-positive settings->max_iter",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->max_iter = tmp_int;
+
+  // Setup workspace with wrong settings->eps_abs
+  tmp_float = settings->eps_abs;
+  settings->eps_abs = -1.1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to negative settings->eps_abs",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->eps_abs = tmp_float;
+
+  // Setup workspace with wrong settings->eps_rel
+  tmp_float = settings->eps_rel;
+  settings->eps_rel = -0.1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to negative settings->eps_rel",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->eps_rel = tmp_float;
+
+  // Setup workspace with wrong settings->eps_prim_inf
+  tmp_float = settings->eps_prim_inf;
+  settings->eps_prim_inf = -0.1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-positive settings->eps_prim_inf",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->eps_prim_inf = tmp_float;
+
+  // Setup workspace with wrong settings->eps_dual_inf
+  tmp_float = settings->eps_dual_inf;
+  settings->eps_dual_inf = 0.0;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-positive settings->eps_dual_inf",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->eps_dual_inf = tmp_float;
+
+  // Setup workspace with wrong settings->alpha
+  tmp_float = settings->alpha;
+  settings->alpha = 2.0;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to wrong settings->alpha",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->alpha = tmp_float;
+
+  // Setup workspace with wrong settings->linsys_solver
+  enum linsys_solver_type tmp_solver_type= settings->linsys_solver;
+  settings->linsys_solver = UNKNOWN_SOLVER;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to wrong settings->linsys_solver",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->linsys_solver = tmp_solver_type;
+
+  // Setup workspace with wrong settings->verbose
+  tmp_int = settings->verbose;
+  settings->verbose = 2;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-boolean settings->verbose",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->verbose = tmp_int;
+
+  // Setup workspace with wrong settings->scaled_termination
+  tmp_int = settings->scaled_termination;
+  settings->scaled_termination = 2;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-boolean settings->scaled_termination",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->scaled_termination = tmp_int;
+
+  // Setup workspace with wrong settings->check_termination
+  tmp_int = settings->check_termination;
+  settings->check_termination = -1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-boolean settings->check_termination",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->check_termination = tmp_int;
+
+  // Setup workspace with wrong settings->warm_start
+  tmp_int = settings->warm_start;
+  settings->warm_start = 5;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-boolean settings->warm_start",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->warm_start = tmp_int;
+
+#ifdef PROFILING
+  // Setup workspace with wrong settings->time_limit
+  tmp_float = settings->time_limit;
+  settings->time_limit = -0.2;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to wrong settings->time_limit",
+            exitflag == OSQP_SETTINGS_VALIDATION_ERROR);
+  settings->time_limit = tmp_float;
+#endif
+
+
+  /* =========================
+       SETUP WITH WRONG DATA
+     ========================= */
+
+  // Setup workspace with empty data
+  exitflag = osqp_setup(&work, OSQP_NULL, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to empty data",
+            exitflag == OSQP_DATA_VALIDATION_ERROR);
+
+  // Setup workspace with wrong data->m
+  tmp_int = data->m;
+  data->m = data->m - 1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to wrong data->m",
+            exitflag == OSQP_DATA_VALIDATION_ERROR);
+  data->m = tmp_int;
+
+  // Setup workspace with wrong data->n
+  tmp_int = data->n;
+  data->n = data->n + 1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to wrong data->n",
+            exitflag == OSQP_DATA_VALIDATION_ERROR);
+
+  // Setup workspace with zero data->n
+  data->n = 0;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to zero data->n",
+            exitflag == OSQP_DATA_VALIDATION_ERROR);
+  data->n = tmp_int;
+
+  // Setup workspace with wrong P->m
+  tmp_int = data->P->m;
+  data->P->m = data->n + 1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to wrong P->m",
+            exitflag == OSQP_DATA_VALIDATION_ERROR);
+  data->P->m = tmp_int;
+
+  // Setup workspace with wrong P->n
+  tmp_int = data->P->n;
+  data->P->n = data->n + 1;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to wrong P->n",
+            exitflag == OSQP_DATA_VALIDATION_ERROR);
+  data->P->n = tmp_int;
+
+  // Setup workspace with non-upper-triangular P
+  tmp_mat = data->P;
+
+  // Construct non-upper-triangular P
+  P_tmp = (csc*) c_malloc(sizeof(csc));
+  P_tmp->m = 2;
+  P_tmp->n = 2;
+  P_tmp->nz = -1;
+  P_tmp->nzmax = 4;
+  P_tmp->x = (c_float*) c_malloc(4 * sizeof(c_float));
+  P_tmp->x[0] = 4.0;
+  P_tmp->x[1] = 1.0;
+  P_tmp->x[2] = 1.0;
+  P_tmp->x[3] = 2.0;
+  P_tmp->i = (c_int*) c_malloc(4 * sizeof(c_int));
+  P_tmp->i[0] = 0;
+  P_tmp->i[1] = 1;
+  P_tmp->i[2] = 0;
+  P_tmp->i[3] = 1;
+  P_tmp->p = (c_int*) c_malloc((2 + 1) * sizeof(c_int));
+  P_tmp->p[0] = 0;
+  P_tmp->p[1] = 2;
+  P_tmp->p[2] = 4;
+
+  data->P = P_tmp;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-triu structure of P",
+            exitflag == OSQP_DATA_VALIDATION_ERROR);
+  data->P = tmp_mat;
+
+  // Setup workspace with non-consistent bounds
+  data->l[0] = data->u[0] + 1.0;
+  exitflag = osqp_setup(&work, data, settings);
+  mu_assert("Basic QP test solve: Setup should result in error due to non-consistent bounds",
+            exitflag == OSQP_DATA_VALIDATION_ERROR);
+
+
+  // Cleanup data
+  clean_problem_basic_qp(data);
+  clean_problem_basic_qp_sols_data(sols_data);
+
+  // Cleanup
+  c_free(settings);
+  c_free(P_tmp->x);
+  c_free(P_tmp->i);
+  c_free(P_tmp->p);
+  c_free(P_tmp);
+}
+
+#ifdef ENABLE_MKL_PARDISO
+void test_basic_qp_solve_pardiso()
+{
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  basic_qp_sols_data *sols_data;
+
+  // Populate data
+  data = generate_problem_basic_qp();
+  sols_data = generate_problem_basic_qp_sols_data();
+
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->max_iter      = 2000;
+  settings->alpha         = 1.6;
+  settings->polish        = 1;
+  settings->scaling       = 0;
+  settings->verbose       = 1;
+  settings->warm_start    = 0;
+  settings->linsys_solver = MKL_PARDISO_SOLVER;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Basic QP test solve Pardiso: Setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Basic QP test solve Pardiso: Error in solver status!",
+            work->info->status_val == sols_data->status_test);
+
+  // Compare primal solutions
+  mu_assert("Basic QP test solve Pardiso: Error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, sols_data->x_test,
+                              data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Basic QP test solve Pardiso: Error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, sols_data->y_test,
+                              data->m) < TESTS_TOL);
+
+
+  // Compare objective values
+  mu_assert("Basic QP test solve Pardiso: Error in objective value!",
+            c_absval(work->info->obj_val - sols_data->obj_value_test) <
+            TESTS_TOL);
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+
+  // Cleanup data
+  clean_problem_basic_qp(data);
+  clean_problem_basic_qp_sols_data(sols_data);
+
+  // Cleanup
+  c_free(settings);
+}
+#endif
+
+void test_basic_qp_update()
+{
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  basic_qp_sols_data *sols_data;
+
+  // Populate data
+  data = generate_problem_basic_qp();
+  sols_data = generate_problem_basic_qp_sols_data();
+
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->max_iter   = 200;
+  settings->alpha      = 1.6;
+  settings->polish     = 1;
+  settings->scaling    = 0;
+  settings->verbose    = 1;
+  settings->warm_start = 0;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Basic QP test update: Setup error!", exitflag == 0);
+
+
+  // ====================================================================
+  //  Update data
+  // ====================================================================
+
+  // Update linear cost
+  osqp_update_lin_cost(work, sols_data->q_new);
+  mu_assert("Basic QP test update: Error in updating linear cost!",
+            vec_norm_inf_diff(work->data->q, sols_data->q_new,
+                              data->n) < TESTS_TOL);
+
+  // UPDATE BOUND
+  // Try to update with non-consistent values
+  mu_assert("Basic QP test update: Error in bounds update ordering not caught!",
+            osqp_update_bounds(work, sols_data->u_new, sols_data->l_new) == 1);
+
+  // Now update with correct values
+  mu_assert("Basic QP test update: Error in bounds update ordering!",
+            osqp_update_bounds(work, sols_data->l_new, sols_data->u_new) == 0);
+
+  mu_assert("Basic QP test update: Error in bounds update, lower bound!",
+            vec_norm_inf_diff(work->data->l, sols_data->l_new,
+                              data->m) < TESTS_TOL);
+
+  mu_assert("Basic QP test update: Error in bounds update, upper bound!",
+            vec_norm_inf_diff(work->data->u, sols_data->u_new,
+                              data->m) < TESTS_TOL);
+
+  // Return original values
+  osqp_update_bounds(work, data->l, data->u);
+
+
+  // UPDATE LOWER BOUND
+  // Try to update with non-consistent values
+  mu_assert(
+    "Basic QP test update: Error in lower bound update ordering not caught!",
+    osqp_update_lower_bound(work, sols_data->u_new) == 1);
+
+  // Now update with correct values
+  mu_assert("Basic QP test update: Error in lower bound update ordering!",
+            osqp_update_lower_bound(work, sols_data->l_new) == 0);
+
+  mu_assert("Basic QP test update: Error in updating lower bound!",
+            vec_norm_inf_diff(work->data->l, sols_data->l_new,
+                              data->m) < TESTS_TOL);
+
+  // Return original values
+  osqp_update_lower_bound(work, data->l);
+
+
+  // UPDATE UPPER BOUND
+  // Try to update with non-consistent values
+  mu_assert(
+    "Basic QP test update: Error in upper bound update: ordering not caught!",
+    osqp_update_upper_bound(work, sols_data->l_new) == 1);
+
+  // Now update with correct values
+  mu_assert("Basic QP test update: Error in upper bound update: ordering!",
+            osqp_update_upper_bound(work, sols_data->u_new) == 0);
+
+  mu_assert("Basic QP test update: Error in updating upper bound!",
+            vec_norm_inf_diff(work->data->u, sols_data->u_new,
+                              data->m) < TESTS_TOL);
+
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+
+  // Cleanup data
+  clean_problem_basic_qp(data);
+  clean_problem_basic_qp_sols_data(sols_data);
+
+  // Cleanup
+  c_free(settings);
+}
+
+void test_basic_qp_check_termination()
+{
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  basic_qp_sols_data *sols_data;
+
+  // Populate data
+  data = generate_problem_basic_qp();
+  sols_data = generate_problem_basic_qp_sols_data();
+
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->max_iter          = 200;
+  settings->alpha             = 1.6;
+  settings->polish            = 0;
+  settings->scaling           = 0;
+  settings->verbose           = 1;
+  settings->check_termination = 0;
+  settings->warm_start        = 0;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Basic QP test solve: Setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Check if iter == max_iter
+  mu_assert(
+    "Basic QP test check termination: Error in number of iterations taken!",
+    work->info->iter == work->settings->max_iter);
+
+  // Compare solver statuses
+  mu_assert("Basic QP test check termination: Error in solver status!",
+            work->info->status_val == sols_data->status_test);
+
+  // Compare primal solutions
+  mu_assert("Basic QP test check termination: Error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, sols_data->x_test,
+                              data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  // print_vec(work->solution->y, data->m, "y_sol");
+  // print_vec(sols_data->y_test, data->m, "y_test");
+  mu_assert("Basic QP test check termination: Error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, sols_data->y_test,
+                              data->m) < TESTS_TOL);
+
+  // Compare objective values
+  mu_assert("Basic QP test check termination: Error in objective value!",
+            c_absval(work->info->obj_val - sols_data->obj_value_test) <
+            TESTS_TOL);
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+  // Cleanup data
+  clean_problem_basic_qp(data);
+  clean_problem_basic_qp_sols_data(sols_data);
+
+  // Cleanup
+  c_free(settings);
+}
+
+void test_basic_qp_update_rho()
+{
+  c_int extiflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  basic_qp_sols_data *sols_data;
+
+  // Exitflag
+  c_int exitflag;
+
+  // rho to use
+  c_float rho;
+
+  // Define number of iterations to compare
+  c_int n_iter_new_solver, n_iter_update_rho;
+
+  // Populate data
+  data = generate_problem_basic_qp();
+  sols_data = generate_problem_basic_qp_sols_data();
+
+
+  // Define Solver settings as default
+  rho = 0.7;
+  osqp_set_default_settings(settings);
+  settings->rho               = rho;
+  settings->adaptive_rho      = 0; // Disable adaptive rho for this test
+  settings->eps_abs           = 5e-05;
+  settings->eps_rel           = 5e-05;
+  settings->check_termination = 1;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Basic QP test update rho: Setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Store number of iterations
+  n_iter_new_solver = work->info->iter;
+
+  // Compare solver statuses
+  mu_assert("Update rho test solve: Error in solver status!",
+            work->info->status_val == sols_data->status_test);
+
+  // Compare primal solutions
+  mu_assert("Update rho test solve: Error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, sols_data->x_test,
+                              data->n)/vec_norm_inf(sols_data->x_test, data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Update rho test solve: Error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, sols_data->y_test,
+                              data->m)/vec_norm_inf(sols_data->y_test, data->m) < TESTS_TOL);
+
+  // Compare objective values
+  mu_assert("Update rho test solve: Error in objective value!",
+            c_absval(work->info->obj_val - sols_data->obj_value_test) <
+            TESTS_TOL);
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+
+  // Create new problem with different rho and update it
+  osqp_set_default_settings(settings);
+  settings->rho               = 0.1;
+  settings->adaptive_rho      = 0;
+  settings->check_termination = 1;
+  settings->eps_abs           = 5e-05;
+  settings->eps_rel           = 5e-05;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Basic QP test update rho: Setup error!", exitflag == 0);
+
+  // Update rho
+  exitflag = osqp_update_rho(work, rho);
+  mu_assert("Basic QP test update rho: Error update rho!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Basic QP test update rho: Error in solver status!",
+            work->info->status_val == sols_data->status_test);
+
+  // Compare primal solutions
+  mu_assert("Basic QP test update rho: Error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, sols_data->x_test,
+                              data->n)/vec_norm_inf(sols_data->x_test, data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Basic QP test update rho: Error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, sols_data->y_test,
+                              data->m)/vec_norm_inf(sols_data->y_test, data->m)< TESTS_TOL);
+
+  // Compare objective values
+  mu_assert("Basic QP test update rho: Error in objective value!",
+            c_absval(work->info->obj_val - sols_data->obj_value_test) <
+            TESTS_TOL);
+
+  // Get number of iterations
+  n_iter_update_rho = work->info->iter;
+
+  // Assert same number of iterations
+  mu_assert("Basic QP test update rho: Error in number of iterations!",
+            n_iter_new_solver == n_iter_update_rho);
+
+  // Cleanup solver
+  osqp_cleanup(work);
+
+  // Cleanup data
+  clean_problem_basic_qp(data);
+  clean_problem_basic_qp_sols_data(sols_data);
+
+  // Cleanup
+  c_free(settings);
+}
+
+#ifdef PROFILING
+void test_basic_qp_time_limit()
+{
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  basic_qp_sols_data *sols_data;
+
+  // Populate data
+  data = generate_problem_basic_qp();
+  sols_data = generate_problem_basic_qp_sols_data();
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->rho = 20;
+  settings->adaptive_rho = 0;
+
+  // Check default time limit
+  mu_assert("Basic QP test time limit: Default not correct", settings->time_limit == 0);
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Basic QP test time limit: Setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Basic QP test time limit: Error in no time limit solver status!",
+	    work->info->status_val == sols_data->status_test);
+
+  // Update time limit
+# ifdef PRINTING
+  osqp_update_time_limit(work, 1e-5);
+  osqp_update_eps_rel(work, 1e-09);
+  osqp_update_eps_abs(work, 1e-09);
+# else
+  // Not printing makes the code run a lot faster, so we need to make it work harder
+  // to fail by time limit exceeded
+  osqp_update_time_limit(work, 1e-7);
+  osqp_update_eps_rel(work, 1e-12);
+  osqp_update_eps_abs(work, 1e-12);
+# endif
+  osqp_update_max_iter(work, (c_int)2e9);
+  osqp_update_check_termination(work, 0);
+
+  // Solve Problem
+  cold_start(work);
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Basic QP test time limit: Error in timed out solver status!",
+	    work->info->status_val == OSQP_TIME_LIMIT_REACHED);
+
+  // Cleanup solver
+  osqp_cleanup(work);
+
+  // Cleanup data
+  clean_problem_basic_qp(data);
+  clean_problem_basic_qp_sols_data(sols_data);
+
+  // Cleanup
+  c_free(settings);
+}
+#endif // PROFILING
+
+
+void test_basic_qp_warm_start()
+{
+  c_int exitflag, iter;
+
+  // Cold started variables
+  c_float x0[2] = { 0.0, 0.0, };
+  c_float y0[4] = { 0.0, 0.0, 0.0, 0.0, };
+
+  // Optimal solution
+  c_float xopt[2] = { 0.3, 0.7, };
+  c_float yopt[4] = {-2.9, 0.0, 0.2, 0.0, };
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  basic_qp_sols_data *sols_data;
+
+  // Populate data
+  data = generate_problem_basic_qp();
+  sols_data = generate_problem_basic_qp_sols_data();
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->check_termination = 1;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Solve Problem
+  osqp_solve(work);
+  iter = work->info->iter;
+
+  // Cold start and solve again
+  osqp_warm_start(work, x0, y0);
+  osqp_solve(work);
+
+  // Check if the number of iterations is the same
+  mu_assert("Basic QP test warm start: Cold start error!", work->info->iter == iter);
+
+  // Warm start from the solution and solve again
+  osqp_warm_start_x(work, xopt);
+  osqp_warm_start_y(work, yopt);
+  osqp_solve(work);
+
+  // Check that the number of iterations equals 1
+  mu_assert("Basic QP test warm start: Warm start error!", work->info->iter == 1);
+
+  // Cleanup solver
+  osqp_cleanup(work);
+
+  // Cleanup data
+  clean_problem_basic_qp(data);
+  clean_problem_basic_qp_sols_data(sols_data);
+
+  // Cleanup
+  c_free(settings);
+}
\ No newline at end of file
diff --git a/tests/basic_qp2/CMakeLists.txt b/tests/basic_qp2/CMakeLists.txt
new file mode 100644
index 0000000..5f7c7d4
--- /dev/null
+++ b/tests/basic_qp2/CMakeLists.txt
@@ -0,0 +1,13 @@
+get_directory_property(headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION headers)
+
+set(headers ${headers}
+${CMAKE_CURRENT_SOURCE_DIR}/test_basic_qp2.h PARENT_SCOPE)
+
+get_directory_property(codegen_headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION codegen_headers)
+
+set(codegen_headers ${codegen_headers}
+        ${CMAKE_CURRENT_SOURCE_DIR}/data.h PARENT_SCOPE)
diff --git a/tests/basic_qp2/__init__.py b/tests/basic_qp2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/basic_qp2/__init__.py
diff --git a/tests/basic_qp2/generate_problem.py b/tests/basic_qp2/generate_problem.py
new file mode 100644
index 0000000..23b48a9
--- /dev/null
+++ b/tests/basic_qp2/generate_problem.py
@@ -0,0 +1,34 @@
+import numpy as np
+from scipy import sparse
+import utils.codegen_utils as cu
+
+P = sparse.triu([[11., 0.], [0., 0.]], format='csc')
+q = np.array([3., 4.])
+
+A = sparse.csc_matrix(np.array([[-1., 0.], [0., -1.], [-1., 3.],
+                                [2., 5.], [3., 4]]))
+l = -np.inf * np.ones(A.shape[0])
+u = np.array([0., 0., -15., 100., 80.])
+
+n = P.shape[0]
+m = A.shape[0]
+
+# New data
+q_new = np.array([1., 1.])
+u_new = np.array([-2., 0., -20., 100., 80.])
+
+# Generate problem solutions
+sols_data = {'x_test': np.array([15., -0.]),
+             'y_test': np.array([0., 508., 168., 0., 0.]),
+             'obj_value_test': 1282.5,
+             'status_test': 'optimal',
+             'q_new': q_new,
+             'u_new': u_new,
+             'x_test_new': np.array([20., -0.]),
+             'y_test_new': np.array([0., 664., 221., 0., 0.]),
+             'obj_value_test_new': 2220.0,
+             'status_test_new': 'optimal'}
+
+
+# Generate problem data
+cu.generate_problem_data(P, q, A, l, u, 'basic_qp2', sols_data)
diff --git a/tests/basic_qp2/test_basic_qp2.h b/tests/basic_qp2/test_basic_qp2.h
new file mode 100644
index 0000000..c3445d2
--- /dev/null
+++ b/tests/basic_qp2/test_basic_qp2.h
@@ -0,0 +1,211 @@
+#include "osqp.h"        // OSQP API
+#include "osqp_tester.h" // Basic testing script header
+
+
+#include "basic_qp2/data.h"
+
+
+void test_basic_qp2_solve()
+{
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  basic_qp2_sols_data *sols_data;
+
+
+  // Populate data
+  data = generate_problem_basic_qp2();
+  sols_data = generate_problem_basic_qp2_sols_data();
+
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->alpha   = 1.6;
+  settings->rho     = 0.1;
+  settings->polish  = 1;
+  settings->verbose = 1;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Basic QP 2 test solve: Setup error!", exitflag == 0);
+
+  // Solve Problem first time
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Basic QP 2 test solve: Error in solver status!",
+            work->info->status_val == sols_data->status_test);
+
+  // Compare primal solutions
+  mu_assert("Basic QP 2 test solve: Error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, sols_data->x_test,
+                              data->n) /
+            vec_norm_inf(sols_data->x_test_new, data->n) < TESTS_TOL);
+
+
+  // Compare dual solutions
+  mu_assert("Basic QP 2 test solve: Error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, sols_data->y_test,
+                              data->m) /
+            vec_norm_inf(sols_data->y_test_new, data->m) < TESTS_TOL);
+
+  // Compare objective values
+  mu_assert("Basic QP 2 test solve: Error in objective value!",
+            c_absval(work->info->obj_val - sols_data->obj_value_test)/(c_absval(sols_data->obj_value_test)) < TESTS_TOL);
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+  // Cleanup settings and data
+  c_free(settings);
+  clean_problem_basic_qp2(data);
+  clean_problem_basic_qp2_sols_data(sols_data);
+}
+
+#ifdef ENABLE_MKL_PARDISO
+void test_basic_qp2_solve_pardiso()
+{
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  basic_qp2_sols_data *sols_data;
+
+
+  // Populate data
+  data = generate_problem_basic_qp2();
+  sols_data = generate_problem_basic_qp2_sols_data();
+
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->alpha         = 1.6;
+  settings->rho           = 0.1;
+  settings->polish        = 1;
+  settings->verbose       = 1;
+  settings->linsys_solver = MKL_PARDISO_SOLVER;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Basic QP 2 test solve: Setup error!", exitflag == 0);
+
+  // Solve Problem first time
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Basic QP 2 test solve: Error in solver status!",
+            work->info->status_val == sols_data->status_test);
+
+  // Compare primal solutions
+  mu_assert("Basic QP 2 test solve: Error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, sols_data->x_test,
+                              data->n) /
+            vec_norm_inf(sols_data->x_test_new, data->n) < TESTS_TOL);
+
+
+  // Compare dual solutions
+  mu_assert("Basic QP 2 test solve: Error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, sols_data->y_test,
+                              data->m) /
+            vec_norm_inf(sols_data->y_test_new, data->m) < TESTS_TOL);
+
+
+  // Compare objective values
+  mu_assert("Basic QP 2 test solve: Error in objective value!",
+            c_absval(work->info->obj_val - sols_data->obj_value_test) <
+            TESTS_TOL);
+
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+  // Cleanup settings and data
+  c_free(settings);
+  clean_problem_basic_qp2(data);
+  clean_problem_basic_qp2_sols_data(sols_data);
+}
+#endif
+
+void test_basic_qp2_update()
+{
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  basic_qp2_sols_data *sols_data;
+
+
+  // Populate data
+  data = generate_problem_basic_qp2();
+  sols_data = generate_problem_basic_qp2_sols_data();
+
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->alpha = 1.6;
+
+  settings->warm_start = 1;
+  settings->polish     = 1;
+  settings->verbose    = 1;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Basic QP 2 test update: Setup error!", exitflag == 0);
+
+
+  // Modify linear cost and upper bound
+  osqp_update_lin_cost(work, sols_data->q_new);
+  osqp_update_upper_bound(work, sols_data->u_new);
+
+  // Solve Problem second time(with different data now)
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Basic QP 2 test update: Error in solver status!",
+            work->info->status_val == sols_data->status_test_new);
+
+  // Compare primal solutions
+  mu_assert("Basic QP 2 test update: Error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, sols_data->x_test_new,
+                              data->n) /
+            vec_norm_inf(sols_data->x_test_new, data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Basic QP 2 test update: Error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, sols_data->y_test_new,
+                              data->m) /
+            vec_norm_inf(sols_data->y_test_new, data->m) < TESTS_TOL);
+
+
+  // Compare objective values
+  mu_assert("Basic QP 2 test update: Error in objective value!",
+            c_absval(
+              work->info->obj_val - sols_data->obj_value_test_new)/(c_absval(sols_data->obj_value_test_new)) < TESTS_TOL);
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+  // Cleanup settings and data
+  c_free(settings);
+  clean_problem_basic_qp2(data);
+  clean_problem_basic_qp2_sols_data(sols_data);
+}
diff --git a/tests/catch.hpp b/tests/catch.hpp
new file mode 100644
index 0000000..36eaeb2
--- /dev/null
+++ b/tests/catch.hpp
@@ -0,0 +1,17937 @@
+/*
+ *  Catch v2.13.6
+ *  Generated: 2021-04-16 18:23:38.044268
+ *  ----------------------------------------------------------
+ *  This file has been merged from multiple headers. Please don't edit it directly
+ *  Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved.
+ *
+ *  Distributed under the Boost Software License, Version 1.0. (See accompanying
+ *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+// start catch.hpp
+
+
+#define CATCH_VERSION_MAJOR 2
+#define CATCH_VERSION_MINOR 13
+#define CATCH_VERSION_PATCH 6
+
+#ifdef __clang__
+#    pragma clang system_header
+#elif defined __GNUC__
+#    pragma GCC system_header
+#endif
+
+// start catch_suppress_warnings.h
+
+#ifdef __clang__
+#   ifdef __ICC // icpc defines the __clang__ macro
+#       pragma warning(push)
+#       pragma warning(disable: 161 1682)
+#   else // __ICC
+#       pragma clang diagnostic push
+#       pragma clang diagnostic ignored "-Wpadded"
+#       pragma clang diagnostic ignored "-Wswitch-enum"
+#       pragma clang diagnostic ignored "-Wcovered-switch-default"
+#    endif
+#elif defined __GNUC__
+     // Because REQUIREs trigger GCC's -Wparentheses, and because still
+     // supported version of g++ have only buggy support for _Pragmas,
+     // Wparentheses have to be suppressed globally.
+#    pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details
+
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wunused-variable"
+#    pragma GCC diagnostic ignored "-Wpadded"
+#endif
+// end catch_suppress_warnings.h
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+#  define CATCH_IMPL
+#  define CATCH_CONFIG_ALL_PARTS
+#endif
+
+// In the impl file, we want to have access to all parts of the headers
+// Can also be used to sanely support PCHs
+#if defined(CATCH_CONFIG_ALL_PARTS)
+#  define CATCH_CONFIG_EXTERNAL_INTERFACES
+#  if defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#    undef CATCH_CONFIG_DISABLE_MATCHERS
+#  endif
+#  if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+#    define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#  endif
+#endif
+
+#if !defined(CATCH_CONFIG_IMPL_ONLY)
+// start catch_platform.h
+
+// See e.g.:
+// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
+#ifdef __APPLE__
+#  include <TargetConditionals.h>
+#  if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
+      (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
+#    define CATCH_PLATFORM_MAC
+#  elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+#    define CATCH_PLATFORM_IPHONE
+#  endif
+
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+#  define CATCH_PLATFORM_LINUX
+
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
+#  define CATCH_PLATFORM_WINDOWS
+#endif
+
+// end catch_platform.h
+
+#ifdef CATCH_IMPL
+#  ifndef CLARA_CONFIG_MAIN
+#    define CLARA_CONFIG_MAIN_NOT_DEFINED
+#    define CLARA_CONFIG_MAIN
+#  endif
+#endif
+
+// start catch_user_interfaces.h
+
+namespace Catch {
+    unsigned int rngSeed();
+}
+
+// end catch_user_interfaces.h
+// start catch_tag_alias_autoregistrar.h
+
+// start catch_common.h
+
+// start catch_compiler_capabilities.h
+
+// Detect a number of compiler features - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+#ifdef __cplusplus
+
+#  if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
+#    define CATCH_CPP14_OR_GREATER
+#  endif
+
+#  if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+#    define CATCH_CPP17_OR_GREATER
+#  endif
+
+#endif
+
+// Only GCC compiler should be used in this block, so other compilers trying to
+// mask themselves as GCC should be ignored.
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
+#    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
+#    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  _Pragma( "GCC diagnostic pop" )
+
+#    define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
+
+#endif
+
+#if defined(__clang__)
+
+#    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
+#    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  _Pragma( "clang diagnostic pop" )
+
+// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
+// which results in calls to destructors being emitted for each temporary,
+// without a matching initialization. In practice, this can result in something
+// like `std::string::~string` being called on an uninitialized value.
+//
+// For example, this code will likely segfault under IBM XL:
+// ```
+// REQUIRE(std::string("12") + "34" == "1234")
+// ```
+//
+// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
+#  if !defined(__ibmxl__) && !defined(__CUDACC__)
+#    define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
+#  endif
+
+#    define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+         _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
+         _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
+
+#    define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+         _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+
+#    define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+         _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
+
+#    define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+         _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
+
+#    define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+         _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Assume that non-Windows platforms support posix signals by default
+#if !defined(CATCH_PLATFORM_WINDOWS)
+    #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
+    #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#endif
+
+#ifdef __OS400__
+#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#       define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Android somehow still does not support std::to_string
+#if defined(__ANDROID__)
+#    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+#    define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Not all Windows environments support SEH properly
+#if defined(__MINGW32__)
+#    define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// PS4
+#if defined(__ORBIS__)
+#    define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+#   define _BSD_SOURCE
+// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
+// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
+# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
+           && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
+
+#    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+
+# endif
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#if defined(_MSC_VER)
+
+#  define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
+#  define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  __pragma( warning(pop) )
+
+// Universal Windows platform does not support SEH
+// Or console colours (or console at all...)
+#  if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+#    define CATCH_CONFIG_COLOUR_NONE
+#  else
+#    define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+#  endif
+
+// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
+// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
+// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
+#  if !defined(__clang__) // Handle Clang masquerading for msvc
+#    if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
+#      define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#    endif // MSVC_TRADITIONAL
+#  endif // __clang__
+
+#endif // _MSC_VER
+
+#if defined(_REENTRANT) || defined(_MSC_VER)
+// Enable async processing, as -pthread is specified or no additional linking is required
+# define CATCH_INTERNAL_CONFIG_USE_ASYNC
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if we are compiled with -fno-exceptions or equivalent
+#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
+#  define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// DJGPP
+#ifdef __DJGPP__
+#  define CATCH_INTERNAL_CONFIG_NO_WCHAR
+#endif // __DJGPP__
+
+////////////////////////////////////////////////////////////////////////////////
+// Embarcadero C++Build
+#if defined(__BORLANDC__)
+    #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use of __COUNTER__ is suppressed during code analysis in
+// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
+// handled by it.
+// Otherwise all supported compilers support COUNTER macro,
+// but user still might want to turn it off
+#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
+    #define CATCH_INTERNAL_CONFIG_COUNTER
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+// RTX is a special version of Windows that is real time.
+// This means that it is detected as Windows, but does not provide
+// the same set of capabilities as real Windows does.
+#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
+    #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+    #define CATCH_INTERNAL_CONFIG_NO_ASYNC
+    #define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
+#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
+#endif
+
+// Various stdlib support checks that require __has_include
+#if defined(__has_include)
+  // Check if string_view is available and usable
+  #if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
+  #    define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
+  #endif
+
+  // Check if optional is available and usable
+  #  if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
+  #    define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
+  #  endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
+
+  // Check if byte is available and usable
+  #  if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
+  #    include <cstddef>
+  #    if __cpp_lib_byte > 0
+  #      define CATCH_INTERNAL_CONFIG_CPP17_BYTE
+  #    endif
+  #  endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
+
+  // Check if variant is available and usable
+  #  if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
+  #    if defined(__clang__) && (__clang_major__ < 8)
+         // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
+         // fix should be in clang 8, workaround in libstdc++ 8.2
+  #      include <ciso646>
+  #      if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+  #        define CATCH_CONFIG_NO_CPP17_VARIANT
+  #      else
+  #        define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+  #      endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+  #    else
+  #      define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+  #    endif // defined(__clang__) && (__clang_major__ < 8)
+  #  endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
+#endif // defined(__has_include)
+
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+#   define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
+#   define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#   define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
+#   define CATCH_CONFIG_WCHAR
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
+#    define CATCH_CONFIG_CPP11_TO_STRING
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
+#  define CATCH_CONFIG_CPP17_OPTIONAL
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
+#  define CATCH_CONFIG_CPP17_STRING_VIEW
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT)
+#  define CATCH_CONFIG_CPP17_VARIANT
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
+#  define CATCH_CONFIG_CPP17_BYTE
+#endif
+
+#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+#  define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE)
+#  define CATCH_CONFIG_NEW_CAPTURE
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+#  define CATCH_CONFIG_DISABLE_EXCEPTIONS
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN)
+#  define CATCH_CONFIG_POLYFILL_ISNAN
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC)  && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
+#  define CATCH_CONFIG_USE_ASYNC
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
+#  define CATCH_CONFIG_ANDROID_LOGWRITE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+#  define CATCH_CONFIG_GLOBAL_NEXTAFTER
+#endif
+
+// Even if we do not think the compiler has that warning, we still have
+// to provide a macro that can be used by the code.
+#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
+#   define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+#endif
+#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
+#   define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
+#endif
+
+// The goal of this macro is to avoid evaluation of the arguments, but
+// still have the compiler warn on problems inside...
+#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
+#   define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
+#endif
+
+#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
+#   undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#elif defined(__clang__) && (__clang_major__ < 5)
+#   undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+#define CATCH_TRY if ((true))
+#define CATCH_CATCH_ALL if ((false))
+#define CATCH_CATCH_ANON(type) if ((false))
+#else
+#define CATCH_TRY try
+#define CATCH_CATCH_ALL catch (...)
+#define CATCH_CATCH_ANON(type) catch (type)
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR)
+#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#endif
+
+// end catch_compiler_capabilities.h
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#include <iosfwd>
+#include <string>
+#include <cstdint>
+
+// We need a dummy global operator<< so we can bring it into Catch namespace later
+struct Catch_global_namespace_dummy {};
+std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
+
+namespace Catch {
+
+    struct CaseSensitive { enum Choice {
+        Yes,
+        No
+    }; };
+
+    class NonCopyable {
+        NonCopyable( NonCopyable const& )              = delete;
+        NonCopyable( NonCopyable && )                  = delete;
+        NonCopyable& operator = ( NonCopyable const& ) = delete;
+        NonCopyable& operator = ( NonCopyable && )     = delete;
+
+    protected:
+        NonCopyable();
+        virtual ~NonCopyable();
+    };
+
+    struct SourceLineInfo {
+
+        SourceLineInfo() = delete;
+        SourceLineInfo( char const* _file, std::size_t _line ) noexcept
+        :   file( _file ),
+            line( _line )
+        {}
+
+        SourceLineInfo( SourceLineInfo const& other )            = default;
+        SourceLineInfo& operator = ( SourceLineInfo const& )     = default;
+        SourceLineInfo( SourceLineInfo&& )              noexcept = default;
+        SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
+
+        bool empty() const noexcept { return file[0] == '\0'; }
+        bool operator == ( SourceLineInfo const& other ) const noexcept;
+        bool operator < ( SourceLineInfo const& other ) const noexcept;
+
+        char const* file;
+        std::size_t line;
+    };
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+    // Bring in operator<< from global namespace into Catch namespace
+    // This is necessary because the overload of operator<< above makes
+    // lookup stop at namespace Catch
+    using ::operator<<;
+
+    // Use this in variadic streaming macros to allow
+    //    >> +StreamEndStop
+    // as well as
+    //    >> stuff +StreamEndStop
+    struct StreamEndStop {
+        std::string operator+() const;
+    };
+    template<typename T>
+    T const& operator + ( T const& value, StreamEndStop ) {
+        return value;
+    }
+}
+
+#define CATCH_INTERNAL_LINEINFO \
+    ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+
+// end catch_common.h
+namespace Catch {
+
+    struct RegistrarForTagAliases {
+        RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+    };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+    namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+// end catch_tag_alias_autoregistrar.h
+// start catch_test_registry.h
+
+// start catch_interfaces_testcase.h
+
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec;
+
+    struct ITestInvoker {
+        virtual void invoke () const = 0;
+        virtual ~ITestInvoker();
+    };
+
+    class TestCase;
+    struct IConfig;
+
+    struct ITestCaseRegistry {
+        virtual ~ITestCaseRegistry();
+        virtual std::vector<TestCase> const& getAllTests() const = 0;
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+    };
+
+    bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+// end catch_interfaces_testcase.h
+// start catch_stringref.h
+
+#include <cstddef>
+#include <string>
+#include <iosfwd>
+#include <cassert>
+
+namespace Catch {
+
+    /// A non-owning string class (similar to the forthcoming std::string_view)
+    /// Note that, because a StringRef may be a substring of another string,
+    /// it may not be null terminated.
+    class StringRef {
+    public:
+        using size_type = std::size_t;
+        using const_iterator = const char*;
+
+    private:
+        static constexpr char const* const s_empty = "";
+
+        char const* m_start = s_empty;
+        size_type m_size = 0;
+
+    public: // construction
+        constexpr StringRef() noexcept = default;
+
+        StringRef( char const* rawChars ) noexcept;
+
+        constexpr StringRef( char const* rawChars, size_type size ) noexcept
+        :   m_start( rawChars ),
+            m_size( size )
+        {}
+
+        StringRef( std::string const& stdString ) noexcept
+        :   m_start( stdString.c_str() ),
+            m_size( stdString.size() )
+        {}
+
+        explicit operator std::string() const {
+            return std::string(m_start, m_size);
+        }
+
+    public: // operators
+        auto operator == ( StringRef const& other ) const noexcept -> bool;
+        auto operator != (StringRef const& other) const noexcept -> bool {
+            return !(*this == other);
+        }
+
+        auto operator[] ( size_type index ) const noexcept -> char {
+            assert(index < m_size);
+            return m_start[index];
+        }
+
+    public: // named queries
+        constexpr auto empty() const noexcept -> bool {
+            return m_size == 0;
+        }
+        constexpr auto size() const noexcept -> size_type {
+            return m_size;
+        }
+
+        // Returns the current start pointer. If the StringRef is not
+        // null-terminated, throws std::domain_exception
+        auto c_str() const -> char const*;
+
+    public: // substrings and searches
+        // Returns a substring of [start, start + length).
+        // If start + length > size(), then the substring is [start, size()).
+        // If start > size(), then the substring is empty.
+        auto substr( size_type start, size_type length ) const noexcept -> StringRef;
+
+        // Returns the current start pointer. May not be null-terminated.
+        auto data() const noexcept -> char const*;
+
+        constexpr auto isNullTerminated() const noexcept -> bool {
+            return m_start[m_size] == '\0';
+        }
+
+    public: // iterators
+        constexpr const_iterator begin() const { return m_start; }
+        constexpr const_iterator end() const { return m_start + m_size; }
+    };
+
+    auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
+    auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
+
+    constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+        return StringRef( rawChars, size );
+    }
+} // namespace Catch
+
+constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+    return Catch::StringRef( rawChars, size );
+}
+
+// end catch_stringref.h
+// start catch_preprocessor.hpp
+
+
+#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
+#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__)))
+
+#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__
+// MSVC needs more evaluations
+#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__)))
+#define CATCH_RECURSE(...)  CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__))
+#else
+#define CATCH_RECURSE(...)  CATCH_RECURSION_LEVEL5(__VA_ARGS__)
+#endif
+
+#define CATCH_REC_END(...)
+#define CATCH_REC_OUT
+
+#define CATCH_EMPTY()
+#define CATCH_DEFER(id) id CATCH_EMPTY()
+
+#define CATCH_REC_GET_END2() 0, CATCH_REC_END
+#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2
+#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1
+#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT
+#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0)
+#define CATCH_REC_NEXT(test, next)  CATCH_REC_NEXT1(CATCH_REC_GET_END test, next)
+
+#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2(f, x, peek, ...)   f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+
+#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...)   f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+
+// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results,
+// and passes userdata as the first parameter to each invocation,
+// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c)
+#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
+#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
+#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
+#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
+#else
+// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
+#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
+#endif
+
+#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
+#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
+
+#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
+#else
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
+#endif
+
+#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
+    CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
+
+#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
+#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
+#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
+#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
+#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
+#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
+#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
+#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
+#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
+#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
+#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
+
+#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+
+#define INTERNAL_CATCH_TYPE_GEN\
+    template<typename...> struct TypeList {};\
+    template<typename...Ts>\
+    constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
+    template<template<typename...> class...> struct TemplateTypeList{};\
+    template<template<typename...> class...Cs>\
+    constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
+    template<typename...>\
+    struct append;\
+    template<typename...>\
+    struct rewrap;\
+    template<template<typename...> class, typename...>\
+    struct create;\
+    template<template<typename...> class, typename>\
+    struct convert;\
+    \
+    template<typename T> \
+    struct append<T> { using type = T; };\
+    template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
+    struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
+    template< template<typename...> class L1, typename...E1, typename...Rest>\
+    struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
+    \
+    template< template<typename...> class Container, template<typename...> class List, typename...elems>\
+    struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
+    template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
+    struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
+    \
+    template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
+    struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
+    template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
+    struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
+
+#define INTERNAL_CATCH_NTTP_1(signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
+    template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
+    template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
+    constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
+    \
+    template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
+    template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
+    struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\
+    template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
+    struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; };
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    static void TestName()
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    static void TestName()
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    static void TestName()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    static void TestName()
+
+#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
+    template<typename Type>\
+    void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
+    {\
+        Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+    }
+
+#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
+    {\
+        Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+    }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
+    template<typename Type>\
+    void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+    {\
+        Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+    }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+    {\
+        Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+    }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
+    template<typename TestType> \
+    struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
+        void test();\
+    }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
+    struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
+        void test();\
+    }
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
+    template<typename TestType> \
+    void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
+    void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_NTTP_0
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
+#else
+#define INTERNAL_CATCH_NTTP_0(signature)
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
+#endif
+
+// end catch_preprocessor.hpp
+// start catch_meta.hpp
+
+
+#include <type_traits>
+
+namespace Catch {
+    template<typename T>
+    struct always_false : std::false_type {};
+
+    template <typename> struct true_given : std::true_type {};
+    struct is_callable_tester {
+        template <typename Fun, typename... Args>
+        true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
+        template <typename...>
+        std::false_type static test(...);
+    };
+
+    template <typename T>
+    struct is_callable;
+
+    template <typename Fun, typename... Args>
+    struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
+
+#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
+    // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
+    // replaced with std::invoke_result here.
+    template <typename Func, typename... U>
+    using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U...>>>;
+#else
+    // Keep ::type here because we still support C++11
+    template <typename Func, typename... U>
+    using FunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U...)>::type>::type>::type;
+#endif
+
+} // namespace Catch
+
+namespace mpl_{
+    struct na;
+}
+
+// end catch_meta.hpp
+namespace Catch {
+
+template<typename C>
+class TestInvokerAsMethod : public ITestInvoker {
+    void (C::*m_testAsMethod)();
+public:
+    TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
+
+    void invoke() const override {
+        C obj;
+        (obj.*m_testAsMethod)();
+    }
+};
+
+auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
+
+template<typename C>
+auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
+    return new(std::nothrow) TestInvokerAsMethod<C>( testAsMethod );
+}
+
+struct NameAndTags {
+    NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
+    StringRef name;
+    StringRef tags;
+};
+
+struct AutoReg : NonCopyable {
+    AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
+    ~AutoReg();
+};
+
+} // end namespace Catch
+
+#if defined(CATCH_CONFIG_DISABLE)
+    #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
+        static void TestName()
+    #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
+        namespace{                        \
+            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+                void test();              \
+            };                            \
+        }                                 \
+        void TestName::test()
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... )  \
+        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... )    \
+        namespace{                                                                                  \
+            namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) {                                      \
+            INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+        }                                                                                           \
+        }                                                                                           \
+        INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
+    #else
+        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+    #endif
+
+    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
+    #else
+        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+    #endif
+
+    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+    #else
+        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+    #endif
+
+    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+    #else
+        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+    #endif
+#endif
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+        static void TestName(); \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        static void TestName()
+    #define INTERNAL_CATCH_TESTCASE( ... ) \
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        namespace{ \
+            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+        } \
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        void TestName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+        INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+        namespace {\
+        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+            INTERNAL_CATCH_TYPE_GEN\
+            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+            INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+            template<typename...Types> \
+            struct TestName{\
+                TestName(){\
+                    int index = 0;                                    \
+                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+                    using expander = int[];\
+                    (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+                }\
+            };\
+            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+            TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
+            return 0;\
+        }();\
+        }\
+        }\
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION                      \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS                      \
+        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS                \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS              \
+        template<typename TestType> static void TestFuncName();       \
+        namespace {\
+        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) {                                     \
+            INTERNAL_CATCH_TYPE_GEN                                                  \
+            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))         \
+            template<typename... Types>                               \
+            struct TestName {                                         \
+                void reg_tests() {                                          \
+                    int index = 0;                                    \
+                    using expander = int[];                           \
+                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+                    constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+                    constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\
+                }                                                     \
+            };                                                        \
+            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
+                using TestInit = typename create<TestName, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \
+                TestInit t;                                           \
+                t.reg_tests();                                        \
+                return 0;                                             \
+            }();                                                      \
+        }                                                             \
+        }                                                             \
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION                       \
+        template<typename TestType>                                   \
+        static void TestFuncName()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T,__VA_ARGS__)
+#else
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__)
+#else
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+    #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+        template<typename TestType> static void TestFunc();       \
+        namespace {\
+        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+        INTERNAL_CATCH_TYPE_GEN\
+        template<typename... Types>                               \
+        struct TestName {                                         \
+            void reg_tests() {                                          \
+                int index = 0;                                    \
+                using expander = int[];                           \
+                (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
+            }                                                     \
+        };\
+        static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
+                using TestInit = typename convert<TestName, TmplList>::type; \
+                TestInit t;                                           \
+                t.reg_tests();                                        \
+                return 0;                                             \
+            }();                                                      \
+        }}\
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION                       \
+        template<typename TestType>                                   \
+        static void TestFunc()
+
+    #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
+        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, TmplList )
+
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+        namespace {\
+        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
+            INTERNAL_CATCH_TYPE_GEN\
+            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+            INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+            INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+            template<typename...Types> \
+            struct TestNameClass{\
+                TestNameClass(){\
+                    int index = 0;                                    \
+                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+                    using expander = int[];\
+                    (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+                }\
+            };\
+            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+                TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
+                return 0;\
+        }();\
+        }\
+        }\
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+        template<typename TestType> \
+            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
+                void test();\
+            };\
+        namespace {\
+        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
+            INTERNAL_CATCH_TYPE_GEN                  \
+            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+            template<typename...Types>\
+            struct TestNameClass{\
+                void reg_tests(){\
+                    int index = 0;\
+                    using expander = int[];\
+                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+                    constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+                    constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
+                }\
+            };\
+            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+                using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
+                TestInit t;\
+                t.reg_tests();\
+                return 0;\
+            }(); \
+        }\
+        }\
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        template<typename TestType> \
+        void TestName<TestType>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
+#endif
+
+    #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+        template<typename TestType> \
+        struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
+            void test();\
+        };\
+        namespace {\
+        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
+            INTERNAL_CATCH_TYPE_GEN\
+            template<typename...Types>\
+            struct TestNameClass{\
+                void reg_tests(){\
+                    int index = 0;\
+                    using expander = int[];\
+                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
+                }\
+            };\
+            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+                using TestInit = typename convert<TestNameClass, TmplList>::type;\
+                TestInit t;\
+                t.reg_tests();\
+                return 0;\
+            }(); \
+        }}\
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        template<typename TestType> \
+        void TestName<TestType>::test()
+
+#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
+        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, TmplList )
+
+// end catch_test_registry.h
+// start catch_capture.hpp
+
+// start catch_assertionhandler.h
+
+// start catch_assertioninfo.h
+
+// start catch_result_type.h
+
+namespace Catch {
+
+    // ResultWas::OfType enum
+    struct ResultWas { enum OfType {
+        Unknown = -1,
+        Ok = 0,
+        Info = 1,
+        Warning = 2,
+
+        FailureBit = 0x10,
+
+        ExpressionFailed = FailureBit | 1,
+        ExplicitFailure = FailureBit | 2,
+
+        Exception = 0x100 | FailureBit,
+
+        ThrewException = Exception | 1,
+        DidntThrowException = Exception | 2,
+
+        FatalErrorCondition = 0x200 | FailureBit
+
+    }; };
+
+    bool isOk( ResultWas::OfType resultType );
+    bool isJustInfo( int flags );
+
+    // ResultDisposition::Flags enum
+    struct ResultDisposition { enum Flags {
+        Normal = 0x01,
+
+        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues
+        FalseTest = 0x04,           // Prefix expression with !
+        SuppressFail = 0x08         // Failures are reported but do not fail the test
+    }; };
+
+    ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
+
+    bool shouldContinueOnFailure( int flags );
+    inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+    bool shouldSuppressFailure( int flags );
+
+} // end namespace Catch
+
+// end catch_result_type.h
+namespace Catch {
+
+    struct AssertionInfo
+    {
+        StringRef macroName;
+        SourceLineInfo lineInfo;
+        StringRef capturedExpression;
+        ResultDisposition::Flags resultDisposition;
+
+        // We want to delete this constructor but a compiler bug in 4.8 means
+        // the struct is then treated as non-aggregate
+        //AssertionInfo() = delete;
+    };
+
+} // end namespace Catch
+
+// end catch_assertioninfo.h
+// start catch_decomposer.h
+
+// start catch_tostring.h
+
+#include <vector>
+#include <cstddef>
+#include <type_traits>
+#include <string>
+// start catch_stream.h
+
+#include <iosfwd>
+#include <cstddef>
+#include <ostream>
+
+namespace Catch {
+
+    std::ostream& cout();
+    std::ostream& cerr();
+    std::ostream& clog();
+
+    class StringRef;
+
+    struct IStream {
+        virtual ~IStream();
+        virtual std::ostream& stream() const = 0;
+    };
+
+    auto makeStream( StringRef const &filename ) -> IStream const*;
+
+    class ReusableStringStream : NonCopyable {
+        std::size_t m_index;
+        std::ostream* m_oss;
+    public:
+        ReusableStringStream();
+        ~ReusableStringStream();
+
+        auto str() const -> std::string;
+
+        template<typename T>
+        auto operator << ( T const& value ) -> ReusableStringStream& {
+            *m_oss << value;
+            return *this;
+        }
+        auto get() -> std::ostream& { return *m_oss; }
+    };
+}
+
+// end catch_stream.h
+// start catch_interfaces_enum_values_registry.h
+
+#include <vector>
+
+namespace Catch {
+
+    namespace Detail {
+        struct EnumInfo {
+            StringRef m_name;
+            std::vector<std::pair<int, StringRef>> m_values;
+
+            ~EnumInfo();
+
+            StringRef lookup( int value ) const;
+        };
+    } // namespace Detail
+
+    struct IMutableEnumValuesRegistry {
+        virtual ~IMutableEnumValuesRegistry();
+
+        virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
+
+        template<typename E>
+        Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
+            static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
+            std::vector<int> intValues;
+            intValues.reserve( values.size() );
+            for( auto enumValue : values )
+                intValues.push_back( static_cast<int>( enumValue ) );
+            return registerEnum( enumName, allEnums, intValues );
+        }
+    };
+
+} // Catch
+
+// end catch_interfaces_enum_values_registry.h
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+#include <string_view>
+#endif
+
+#ifdef __OBJC__
+// start catch_objc_arc.hpp
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+    [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+// end catch_objc_arc.hpp
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
+#endif
+
+namespace Catch {
+    namespace Detail {
+
+        extern const std::string unprintableString;
+
+        std::string rawMemoryToString( const void *object, std::size_t size );
+
+        template<typename T>
+        std::string rawMemoryToString( const T& object ) {
+          return rawMemoryToString( &object, sizeof(object) );
+        }
+
+        template<typename T>
+        class IsStreamInsertable {
+            template<typename Stream, typename U>
+            static auto test(int)
+                -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
+
+            template<typename, typename>
+            static auto test(...)->std::false_type;
+
+        public:
+            static const bool value = decltype(test<std::ostream, const T&>(0))::value;
+        };
+
+        template<typename E>
+        std::string convertUnknownEnumToString( E e );
+
+        template<typename T>
+        typename std::enable_if<
+            !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
+        std::string>::type convertUnstreamable( T const& ) {
+            return Detail::unprintableString;
+        }
+        template<typename T>
+        typename std::enable_if<
+            !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
+         std::string>::type convertUnstreamable(T const& ex) {
+            return ex.what();
+        }
+
+        template<typename T>
+        typename std::enable_if<
+            std::is_enum<T>::value
+        , std::string>::type convertUnstreamable( T const& value ) {
+            return convertUnknownEnumToString( value );
+        }
+
+#if defined(_MANAGED)
+        //! Convert a CLR string to a utf8 std::string
+        template<typename T>
+        std::string clrReferenceToString( T^ ref ) {
+            if (ref == nullptr)
+                return std::string("null");
+            auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
+            cli::pin_ptr<System::Byte> p = &bytes[0];
+            return std::string(reinterpret_cast<char const *>(p), bytes->Length);
+        }
+#endif
+
+    } // namespace Detail
+
+    // If we decide for C++14, change these to enable_if_ts
+    template <typename T, typename = void>
+    struct StringMaker {
+        template <typename Fake = T>
+        static
+        typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
+            convert(const Fake& value) {
+                ReusableStringStream rss;
+                // NB: call using the function-like syntax to avoid ambiguity with
+                // user-defined templated operator<< under clang.
+                rss.operator<<(value);
+                return rss.str();
+        }
+
+        template <typename Fake = T>
+        static
+        typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
+            convert( const Fake& value ) {
+#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
+            return Detail::convertUnstreamable(value);
+#else
+            return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
+#endif
+        }
+    };
+
+    namespace Detail {
+
+        // This function dispatches all stringification requests inside of Catch.
+        // Should be preferably called fully qualified, like ::Catch::Detail::stringify
+        template <typename T>
+        std::string stringify(const T& e) {
+            return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e);
+        }
+
+        template<typename E>
+        std::string convertUnknownEnumToString( E e ) {
+            return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e));
+        }
+
+#if defined(_MANAGED)
+        template <typename T>
+        std::string stringify( T^ e ) {
+            return ::Catch::StringMaker<T^>::convert(e);
+        }
+#endif
+
+    } // namespace Detail
+
+    // Some predefined specializations
+
+    template<>
+    struct StringMaker<std::string> {
+        static std::string convert(const std::string& str);
+    };
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+    template<>
+    struct StringMaker<std::string_view> {
+        static std::string convert(std::string_view str);
+    };
+#endif
+
+    template<>
+    struct StringMaker<char const *> {
+        static std::string convert(char const * str);
+    };
+    template<>
+    struct StringMaker<char *> {
+        static std::string convert(char * str);
+    };
+
+#ifdef CATCH_CONFIG_WCHAR
+    template<>
+    struct StringMaker<std::wstring> {
+        static std::string convert(const std::wstring& wstr);
+    };
+
+# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+    template<>
+    struct StringMaker<std::wstring_view> {
+        static std::string convert(std::wstring_view str);
+    };
+# endif
+
+    template<>
+    struct StringMaker<wchar_t const *> {
+        static std::string convert(wchar_t const * str);
+    };
+    template<>
+    struct StringMaker<wchar_t *> {
+        static std::string convert(wchar_t * str);
+    };
+#endif
+
+    // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
+    //      while keeping string semantics?
+    template<int SZ>
+    struct StringMaker<char[SZ]> {
+        static std::string convert(char const* str) {
+            return ::Catch::Detail::stringify(std::string{ str });
+        }
+    };
+    template<int SZ>
+    struct StringMaker<signed char[SZ]> {
+        static std::string convert(signed char const* str) {
+            return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
+        }
+    };
+    template<int SZ>
+    struct StringMaker<unsigned char[SZ]> {
+        static std::string convert(unsigned char const* str) {
+            return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
+        }
+    };
+
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+    template<>
+    struct StringMaker<std::byte> {
+        static std::string convert(std::byte value);
+    };
+#endif // defined(CATCH_CONFIG_CPP17_BYTE)
+    template<>
+    struct StringMaker<int> {
+        static std::string convert(int value);
+    };
+    template<>
+    struct StringMaker<long> {
+        static std::string convert(long value);
+    };
+    template<>
+    struct StringMaker<long long> {
+        static std::string convert(long long value);
+    };
+    template<>
+    struct StringMaker<unsigned int> {
+        static std::string convert(unsigned int value);
+    };
+    template<>
+    struct StringMaker<unsigned long> {
+        static std::string convert(unsigned long value);
+    };
+    template<>
+    struct StringMaker<unsigned long long> {
+        static std::string convert(unsigned long long value);
+    };
+
+    template<>
+    struct StringMaker<bool> {
+        static std::string convert(bool b);
+    };
+
+    template<>
+    struct StringMaker<char> {
+        static std::string convert(char c);
+    };
+    template<>
+    struct StringMaker<signed char> {
+        static std::string convert(signed char c);
+    };
+    template<>
+    struct StringMaker<unsigned char> {
+        static std::string convert(unsigned char c);
+    };
+
+    template<>
+    struct StringMaker<std::nullptr_t> {
+        static std::string convert(std::nullptr_t);
+    };
+
+    template<>
+    struct StringMaker<float> {
+        static std::string convert(float value);
+        static int precision;
+    };
+
+    template<>
+    struct StringMaker<double> {
+        static std::string convert(double value);
+        static int precision;
+    };
+
+    template <typename T>
+    struct StringMaker<T*> {
+        template <typename U>
+        static std::string convert(U* p) {
+            if (p) {
+                return ::Catch::Detail::rawMemoryToString(p);
+            } else {
+                return "nullptr";
+            }
+        }
+    };
+
+    template <typename R, typename C>
+    struct StringMaker<R C::*> {
+        static std::string convert(R C::* p) {
+            if (p) {
+                return ::Catch::Detail::rawMemoryToString(p);
+            } else {
+                return "nullptr";
+            }
+        }
+    };
+
+#if defined(_MANAGED)
+    template <typename T>
+    struct StringMaker<T^> {
+        static std::string convert( T^ ref ) {
+            return ::Catch::Detail::clrReferenceToString(ref);
+        }
+    };
+#endif
+
+    namespace Detail {
+        template<typename InputIterator, typename Sentinel = InputIterator>
+        std::string rangeToString(InputIterator first, Sentinel last) {
+            ReusableStringStream rss;
+            rss << "{ ";
+            if (first != last) {
+                rss << ::Catch::Detail::stringify(*first);
+                for (++first; first != last; ++first)
+                    rss << ", " << ::Catch::Detail::stringify(*first);
+            }
+            rss << " }";
+            return rss.str();
+        }
+    }
+
+#ifdef __OBJC__
+    template<>
+    struct StringMaker<NSString*> {
+        static std::string convert(NSString * nsstring) {
+            if (!nsstring)
+                return "nil";
+            return std::string("@") + [nsstring UTF8String];
+        }
+    };
+    template<>
+    struct StringMaker<NSObject*> {
+        static std::string convert(NSObject* nsObject) {
+            return ::Catch::Detail::stringify([nsObject description]);
+        }
+
+    };
+    namespace Detail {
+        inline std::string stringify( NSString* nsstring ) {
+            return StringMaker<NSString*>::convert( nsstring );
+        }
+
+    } // namespace Detail
+#endif // __OBJC__
+
+} // namespace Catch
+
+//////////////////////////////////////////////////////
+// Separate std-lib types stringification, so it can be selectively enabled
+// This means that we do not bring in
+
+#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
+#  define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+#  define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+#  define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+#  define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#  define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+#endif
+
+// Separate std::pair specialization
+#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
+#include <utility>
+namespace Catch {
+    template<typename T1, typename T2>
+    struct StringMaker<std::pair<T1, T2> > {
+        static std::string convert(const std::pair<T1, T2>& pair) {
+            ReusableStringStream rss;
+            rss << "{ "
+                << ::Catch::Detail::stringify(pair.first)
+                << ", "
+                << ::Catch::Detail::stringify(pair.second)
+                << " }";
+            return rss.str();
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+
+#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
+#include <optional>
+namespace Catch {
+    template<typename T>
+    struct StringMaker<std::optional<T> > {
+        static std::string convert(const std::optional<T>& optional) {
+            ReusableStringStream rss;
+            if (optional.has_value()) {
+                rss << ::Catch::Detail::stringify(*optional);
+            } else {
+                rss << "{ }";
+            }
+            return rss.str();
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+
+// Separate std::tuple specialization
+#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
+#include <tuple>
+namespace Catch {
+    namespace Detail {
+        template<
+            typename Tuple,
+            std::size_t N = 0,
+            bool = (N < std::tuple_size<Tuple>::value)
+            >
+            struct TupleElementPrinter {
+            static void print(const Tuple& tuple, std::ostream& os) {
+                os << (N ? ", " : " ")
+                    << ::Catch::Detail::stringify(std::get<N>(tuple));
+                TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
+            }
+        };
+
+        template<
+            typename Tuple,
+            std::size_t N
+        >
+            struct TupleElementPrinter<Tuple, N, false> {
+            static void print(const Tuple&, std::ostream&) {}
+        };
+
+    }
+
+    template<typename ...Types>
+    struct StringMaker<std::tuple<Types...>> {
+        static std::string convert(const std::tuple<Types...>& tuple) {
+            ReusableStringStream rss;
+            rss << '{';
+            Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
+            rss << " }";
+            return rss.str();
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+
+#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
+#include <variant>
+namespace Catch {
+    template<>
+    struct StringMaker<std::monostate> {
+        static std::string convert(const std::monostate&) {
+            return "{ }";
+        }
+    };
+
+    template<typename... Elements>
+    struct StringMaker<std::variant<Elements...>> {
+        static std::string convert(const std::variant<Elements...>& variant) {
+            if (variant.valueless_by_exception()) {
+                return "{valueless variant}";
+            } else {
+                return std::visit(
+                    [](const auto& value) {
+                        return ::Catch::Detail::stringify(value);
+                    },
+                    variant
+                );
+            }
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+
+namespace Catch {
+    // Import begin/ end from std here
+    using std::begin;
+    using std::end;
+
+    namespace detail {
+        template <typename...>
+        struct void_type {
+            using type = void;
+        };
+
+        template <typename T, typename = void>
+        struct is_range_impl : std::false_type {
+        };
+
+        template <typename T>
+        struct is_range_impl<T, typename void_type<decltype(begin(std::declval<T>()))>::type> : std::true_type {
+        };
+    } // namespace detail
+
+    template <typename T>
+    struct is_range : detail::is_range_impl<T> {
+    };
+
+#if defined(_MANAGED) // Managed types are never ranges
+    template <typename T>
+    struct is_range<T^> {
+        static const bool value = false;
+    };
+#endif
+
+    template<typename Range>
+    std::string rangeToString( Range const& range ) {
+        return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
+    }
+
+    // Handle vector<bool> specially
+    template<typename Allocator>
+    std::string rangeToString( std::vector<bool, Allocator> const& v ) {
+        ReusableStringStream rss;
+        rss << "{ ";
+        bool first = true;
+        for( bool b : v ) {
+            if( first )
+                first = false;
+            else
+                rss << ", ";
+            rss << ::Catch::Detail::stringify( b );
+        }
+        rss << " }";
+        return rss.str();
+    }
+
+    template<typename R>
+    struct StringMaker<R, typename std::enable_if<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>::type> {
+        static std::string convert( R const& range ) {
+            return rangeToString( range );
+        }
+    };
+
+    template <typename T, int SZ>
+    struct StringMaker<T[SZ]> {
+        static std::string convert(T const(&arr)[SZ]) {
+            return rangeToString(arr);
+        }
+    };
+
+} // namespace Catch
+
+// Separate std::chrono::duration specialization
+#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+#include <ctime>
+#include <ratio>
+#include <chrono>
+
+namespace Catch {
+
+template <class Ratio>
+struct ratio_string {
+    static std::string symbol();
+};
+
+template <class Ratio>
+std::string ratio_string<Ratio>::symbol() {
+    Catch::ReusableStringStream rss;
+    rss << '[' << Ratio::num << '/'
+        << Ratio::den << ']';
+    return rss.str();
+}
+template <>
+struct ratio_string<std::atto> {
+    static std::string symbol();
+};
+template <>
+struct ratio_string<std::femto> {
+    static std::string symbol();
+};
+template <>
+struct ratio_string<std::pico> {
+    static std::string symbol();
+};
+template <>
+struct ratio_string<std::nano> {
+    static std::string symbol();
+};
+template <>
+struct ratio_string<std::micro> {
+    static std::string symbol();
+};
+template <>
+struct ratio_string<std::milli> {
+    static std::string symbol();
+};
+
+    ////////////
+    // std::chrono::duration specializations
+    template<typename Value, typename Ratio>
+    struct StringMaker<std::chrono::duration<Value, Ratio>> {
+        static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
+            return rss.str();
+        }
+    };
+    template<typename Value>
+    struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
+        static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << " s";
+            return rss.str();
+        }
+    };
+    template<typename Value>
+    struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
+        static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << " m";
+            return rss.str();
+        }
+    };
+    template<typename Value>
+    struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
+        static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << " h";
+            return rss.str();
+        }
+    };
+
+    ////////////
+    // std::chrono::time_point specialization
+    // Generic time_point cannot be specialized, only std::chrono::time_point<system_clock>
+    template<typename Clock, typename Duration>
+    struct StringMaker<std::chrono::time_point<Clock, Duration>> {
+        static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
+            return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
+        }
+    };
+    // std::chrono::time_point<system_clock> specialization
+    template<typename Duration>
+    struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
+        static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
+            auto converted = std::chrono::system_clock::to_time_t(time_point);
+
+#ifdef _MSC_VER
+            std::tm timeInfo = {};
+            gmtime_s(&timeInfo, &converted);
+#else
+            std::tm* timeInfo = std::gmtime(&converted);
+#endif
+
+            auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
+            char timeStamp[timeStampSize];
+            const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+            std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+            std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+            return std::string(timeStamp);
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+
+#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
+namespace Catch { \
+    template<> struct StringMaker<enumName> { \
+        static std::string convert( enumName value ) { \
+            static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
+            return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
+        } \
+    }; \
+}
+
+#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// end catch_tostring.h
+#include <iosfwd>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#pragma warning(disable:4018) // more "signed/unsigned mismatch"
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+#pragma warning(disable:4180) // qualifier applied to function type has no meaning
+#pragma warning(disable:4800) // Forcing result to true or false
+#endif
+
+namespace Catch {
+
+    struct ITransientExpression {
+        auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
+        auto getResult() const -> bool { return m_result; }
+        virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
+
+        ITransientExpression( bool isBinaryExpression, bool result )
+        :   m_isBinaryExpression( isBinaryExpression ),
+            m_result( result )
+        {}
+
+        // We don't actually need a virtual destructor, but many static analysers
+        // complain if it's not here :-(
+        virtual ~ITransientExpression();
+
+        bool m_isBinaryExpression;
+        bool m_result;
+
+    };
+
+    void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
+
+    template<typename LhsT, typename RhsT>
+    class BinaryExpr  : public ITransientExpression {
+        LhsT m_lhs;
+        StringRef m_op;
+        RhsT m_rhs;
+
+        void streamReconstructedExpression( std::ostream &os ) const override {
+            formatReconstructedExpression
+                    ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
+        }
+
+    public:
+        BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
+        :   ITransientExpression{ true, comparisonResult },
+            m_lhs( lhs ),
+            m_op( op ),
+            m_rhs( rhs )
+        {}
+
+        template<typename T>
+        auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+    };
+
+    template<typename LhsT>
+    class UnaryExpr : public ITransientExpression {
+        LhsT m_lhs;
+
+        void streamReconstructedExpression( std::ostream &os ) const override {
+            os << Catch::Detail::stringify( m_lhs );
+        }
+
+    public:
+        explicit UnaryExpr( LhsT lhs )
+        :   ITransientExpression{ false, static_cast<bool>(lhs) },
+            m_lhs( lhs )
+        {}
+    };
+
+    // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int)
+    template<typename LhsT, typename RhsT>
+    auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); }
+    template<typename T>
+    auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
+    template<typename T>
+    auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
+
+    template<typename LhsT, typename RhsT>
+    auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); }
+    template<typename T>
+    auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
+    template<typename T>
+    auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
+
+    template<typename LhsT>
+    class ExprLhs {
+        LhsT m_lhs;
+    public:
+        explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
+
+        template<typename RhsT>
+        auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs };
+        }
+        auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
+            return { m_lhs == rhs, m_lhs, "==", rhs };
+        }
+
+        template<typename RhsT>
+        auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs };
+        }
+        auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
+            return { m_lhs != rhs, m_lhs, "!=", rhs };
+        }
+
+        template<typename RhsT>
+        auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { static_cast<bool>(m_lhs > rhs), m_lhs, ">", rhs };
+        }
+        template<typename RhsT>
+        auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { static_cast<bool>(m_lhs < rhs), m_lhs, "<", rhs };
+        }
+        template<typename RhsT>
+        auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">=", rhs };
+        }
+        template<typename RhsT>
+        auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs };
+        }
+        template <typename RhsT>
+        auto operator | (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { static_cast<bool>(m_lhs | rhs), m_lhs, "|", rhs };
+        }
+        template <typename RhsT>
+        auto operator & (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { static_cast<bool>(m_lhs & rhs), m_lhs, "&", rhs };
+        }
+        template <typename RhsT>
+        auto operator ^ (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { static_cast<bool>(m_lhs ^ rhs), m_lhs, "^", rhs };
+        }
+
+        template<typename RhsT>
+        auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<RhsT>::value,
+            "operator&& is not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename RhsT>
+        auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<RhsT>::value,
+            "operator|| is not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
+            return UnaryExpr<LhsT>{ m_lhs };
+        }
+    };
+
+    void handleExpression( ITransientExpression const& expr );
+
+    template<typename T>
+    void handleExpression( ExprLhs<T> const& expr ) {
+        handleExpression( expr.makeUnaryExpr() );
+    }
+
+    struct Decomposer {
+        template<typename T>
+        auto operator <= ( T const& lhs ) -> ExprLhs<T const&> {
+            return ExprLhs<T const&>{ lhs };
+        }
+
+        auto operator <=( bool value ) -> ExprLhs<bool> {
+            return ExprLhs<bool>{ value };
+        }
+    };
+
+} // end namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// end catch_decomposer.h
+// start catch_interfaces_capture.h
+
+#include <string>
+#include <chrono>
+
+namespace Catch {
+
+    class AssertionResult;
+    struct AssertionInfo;
+    struct SectionInfo;
+    struct SectionEndInfo;
+    struct MessageInfo;
+    struct MessageBuilder;
+    struct Counts;
+    struct AssertionReaction;
+    struct SourceLineInfo;
+
+    struct ITransientExpression;
+    struct IGeneratorTracker;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+    struct BenchmarkInfo;
+    template <typename Duration = std::chrono::duration<double, std::nano>>
+    struct BenchmarkStats;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+    struct IResultCapture {
+
+        virtual ~IResultCapture();
+
+        virtual bool sectionStarted(    SectionInfo const& sectionInfo,
+                                        Counts& assertions ) = 0;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
+
+        virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+        virtual void benchmarkPreparing( std::string const& name ) = 0;
+        virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
+        virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
+        virtual void benchmarkFailed( std::string const& error ) = 0;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+        virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+        virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+        virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;
+
+        virtual void handleFatalErrorCondition( StringRef message ) = 0;
+
+        virtual void handleExpr
+                (   AssertionInfo const& info,
+                    ITransientExpression const& expr,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleMessage
+                (   AssertionInfo const& info,
+                    ResultWas::OfType resultType,
+                    StringRef const& message,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleUnexpectedExceptionNotThrown
+                (   AssertionInfo const& info,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleUnexpectedInflightException
+                (   AssertionInfo const& info,
+                    std::string const& message,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleIncomplete
+                (   AssertionInfo const& info ) = 0;
+        virtual void handleNonExpr
+                (   AssertionInfo const &info,
+                    ResultWas::OfType resultType,
+                    AssertionReaction &reaction ) = 0;
+
+        virtual bool lastAssertionPassed() = 0;
+        virtual void assertionPassed() = 0;
+
+        // Deprecated, do not use:
+        virtual std::string getCurrentTestName() const = 0;
+        virtual const AssertionResult* getLastResult() const = 0;
+        virtual void exceptionEarlyReported() = 0;
+    };
+
+    IResultCapture& getResultCapture();
+}
+
+// end catch_interfaces_capture.h
+namespace Catch {
+
+    struct TestFailureException{};
+    struct AssertionResultData;
+    struct IResultCapture;
+    class RunContext;
+
+    class LazyExpression {
+        friend class AssertionHandler;
+        friend struct AssertionStats;
+        friend class RunContext;
+
+        ITransientExpression const* m_transientExpression = nullptr;
+        bool m_isNegated;
+    public:
+        LazyExpression( bool isNegated );
+        LazyExpression( LazyExpression const& other );
+        LazyExpression& operator = ( LazyExpression const& ) = delete;
+
+        explicit operator bool() const;
+
+        friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&;
+    };
+
+    struct AssertionReaction {
+        bool shouldDebugBreak = false;
+        bool shouldThrow = false;
+    };
+
+    class AssertionHandler {
+        AssertionInfo m_assertionInfo;
+        AssertionReaction m_reaction;
+        bool m_completed = false;
+        IResultCapture& m_resultCapture;
+
+    public:
+        AssertionHandler
+            (   StringRef const& macroName,
+                SourceLineInfo const& lineInfo,
+                StringRef capturedExpression,
+                ResultDisposition::Flags resultDisposition );
+        ~AssertionHandler() {
+            if ( !m_completed ) {
+                m_resultCapture.handleIncomplete( m_assertionInfo );
+            }
+        }
+
+        template<typename T>
+        void handleExpr( ExprLhs<T> const& expr ) {
+            handleExpr( expr.makeUnaryExpr() );
+        }
+        void handleExpr( ITransientExpression const& expr );
+
+        void handleMessage(ResultWas::OfType resultType, StringRef const& message);
+
+        void handleExceptionThrownAsExpected();
+        void handleUnexpectedExceptionNotThrown();
+        void handleExceptionNotThrownAsExpected();
+        void handleThrowingCallSkipped();
+        void handleUnexpectedInflightException();
+
+        void complete();
+        void setCompleted();
+
+        // query
+        auto allowThrows() const -> bool;
+    };
+
+    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString );
+
+} // namespace Catch
+
+// end catch_assertionhandler.h
+// start catch_message.h
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    struct MessageInfo {
+        MessageInfo(    StringRef const& _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        ResultWas::OfType _type );
+
+        StringRef macroName;
+        std::string message;
+        SourceLineInfo lineInfo;
+        ResultWas::OfType type;
+        unsigned int sequence;
+
+        bool operator == ( MessageInfo const& other ) const;
+        bool operator < ( MessageInfo const& other ) const;
+    private:
+        static unsigned int globalCount;
+    };
+
+    struct MessageStream {
+
+        template<typename T>
+        MessageStream& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        ReusableStringStream m_stream;
+    };
+
+    struct MessageBuilder : MessageStream {
+        MessageBuilder( StringRef const& macroName,
+                        SourceLineInfo const& lineInfo,
+                        ResultWas::OfType type );
+
+        template<typename T>
+        MessageBuilder& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        MessageInfo m_info;
+    };
+
+    class ScopedMessage {
+    public:
+        explicit ScopedMessage( MessageBuilder const& builder );
+        ScopedMessage( ScopedMessage& duplicate ) = delete;
+        ScopedMessage( ScopedMessage&& old );
+        ~ScopedMessage();
+
+        MessageInfo m_info;
+        bool m_moved;
+    };
+
+    class Capturer {
+        std::vector<MessageInfo> m_messages;
+        IResultCapture& m_resultCapture = getResultCapture();
+        size_t m_captured = 0;
+    public:
+        Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
+        ~Capturer();
+
+        void captureValue( size_t index, std::string const& value );
+
+        template<typename T>
+        void captureValues( size_t index, T const& value ) {
+            captureValue( index, Catch::Detail::stringify( value ) );
+        }
+
+        template<typename T, typename... Ts>
+        void captureValues( size_t index, T const& value, Ts const&... values ) {
+            captureValue( index, Catch::Detail::stringify(value) );
+            captureValues( index+1, values... );
+        }
+    };
+
+} // end namespace Catch
+
+// end catch_message.h
+#if !defined(CATCH_CONFIG_DISABLE)
+
+#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
+  #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__
+#else
+  #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"
+#endif
+
+#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+
+///////////////////////////////////////////////////////////////////////////////
+// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
+// macros.
+#define INTERNAL_CATCH_TRY
+#define INTERNAL_CATCH_CATCH( capturer )
+
+#else // CATCH_CONFIG_FAST_COMPILE
+
+#define INTERNAL_CATCH_TRY try
+#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); }
+
+#endif
+
+#define INTERNAL_CATCH_REACT( handler ) handler.complete();
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
+    do { \
+        CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
+        INTERNAL_CATCH_TRY { \
+            CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+            CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+            catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
+            CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
+    INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
+    if( Catch::getResultCapture().lastAssertionPassed() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \
+    INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
+    if( !Catch::getResultCapture().lastAssertionPassed() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
+        try { \
+            static_cast<void>(__VA_ARGS__); \
+            catchAssertionHandler.handleExceptionNotThrownAsExpected(); \
+        } \
+        catch( ... ) { \
+            catchAssertionHandler.handleUnexpectedInflightException(); \
+        } \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \
+        if( catchAssertionHandler.allowThrows() ) \
+            try { \
+                static_cast<void>(__VA_ARGS__); \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+            } \
+            catch( ... ) { \
+                catchAssertionHandler.handleExceptionThrownAsExpected(); \
+            } \
+        else \
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \
+        if( catchAssertionHandler.allowThrows() ) \
+            try { \
+                static_cast<void>(expr); \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+            } \
+            catch( exceptionType const& ) { \
+                catchAssertionHandler.handleExceptionThrownAsExpected(); \
+            } \
+            catch( ... ) { \
+                catchAssertionHandler.handleUnexpectedInflightException(); \
+            } \
+        else \
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \
+        catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
+    auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \
+    varName.captureValues( 0, __VA_ARGS__ )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( macroName, log ) \
+    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log );
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
+    Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
+
+///////////////////////////////////////////////////////////////////////////////
+// Although this is matcher-based, it can be used with just a string
+#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        if( catchAssertionHandler.allowThrows() ) \
+            try { \
+                static_cast<void>(__VA_ARGS__); \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+            } \
+            catch( ... ) { \
+                Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \
+            } \
+        else \
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+#endif // CATCH_CONFIG_DISABLE
+
+// end catch_capture.hpp
+// start catch_section.h
+
+// start catch_section_info.h
+
+// start catch_totals.h
+
+#include <cstddef>
+
+namespace Catch {
+
+    struct Counts {
+        Counts operator - ( Counts const& other ) const;
+        Counts& operator += ( Counts const& other );
+
+        std::size_t total() const;
+        bool allPassed() const;
+        bool allOk() const;
+
+        std::size_t passed = 0;
+        std::size_t failed = 0;
+        std::size_t failedButOk = 0;
+    };
+
+    struct Totals {
+
+        Totals operator - ( Totals const& other ) const;
+        Totals& operator += ( Totals const& other );
+
+        Totals delta( Totals const& prevTotals ) const;
+
+        int error = 0;
+        Counts assertions;
+        Counts testCases;
+    };
+}
+
+// end catch_totals.h
+#include <string>
+
+namespace Catch {
+
+    struct SectionInfo {
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name );
+
+        // Deprecated
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name,
+                std::string const& ) : SectionInfo( _lineInfo, _name ) {}
+
+        std::string name;
+        std::string description; // !Deprecated: this will always be empty
+        SourceLineInfo lineInfo;
+    };
+
+    struct SectionEndInfo {
+        SectionInfo sectionInfo;
+        Counts prevAssertions;
+        double durationInSeconds;
+    };
+
+} // end namespace Catch
+
+// end catch_section_info.h
+// start catch_timer.h
+
+#include <cstdint>
+
+namespace Catch {
+
+    auto getCurrentNanosecondsSinceEpoch() -> uint64_t;
+    auto getEstimatedClockResolution() -> uint64_t;
+
+    class Timer {
+        uint64_t m_nanoseconds = 0;
+    public:
+        void start();
+        auto getElapsedNanoseconds() const -> uint64_t;
+        auto getElapsedMicroseconds() const -> uint64_t;
+        auto getElapsedMilliseconds() const -> unsigned int;
+        auto getElapsedSeconds() const -> double;
+    };
+
+} // namespace Catch
+
+// end catch_timer.h
+#include <string>
+
+namespace Catch {
+
+    class Section : NonCopyable {
+    public:
+        Section( SectionInfo const& info );
+        ~Section();
+
+        // This indicates whether the section should be executed or not
+        explicit operator bool() const;
+
+    private:
+        SectionInfo m_info;
+
+        std::string m_name;
+        Counts m_assertions;
+        bool m_sectionIncluded;
+        Timer m_timer;
+    };
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_SECTION( ... ) \
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+    CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+    if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+    CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+    if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+// end catch_section.h
+// start catch_interfaces_exception.h
+
+// start catch_interfaces_registry_hub.h
+
+#include <string>
+#include <memory>
+
+namespace Catch {
+
+    class TestCase;
+    struct ITestCaseRegistry;
+    struct IExceptionTranslatorRegistry;
+    struct IExceptionTranslator;
+    struct IReporterRegistry;
+    struct IReporterFactory;
+    struct ITagAliasRegistry;
+    struct IMutableEnumValuesRegistry;
+
+    class StartupExceptionRegistry;
+
+    using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
+
+    struct IRegistryHub {
+        virtual ~IRegistryHub();
+
+        virtual IReporterRegistry const& getReporterRegistry() const = 0;
+        virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+        virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
+        virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
+
+        virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0;
+    };
+
+    struct IMutableRegistryHub {
+        virtual ~IMutableRegistryHub();
+        virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0;
+        virtual void registerListener( IReporterFactoryPtr const& factory ) = 0;
+        virtual void registerTest( TestCase const& testInfo ) = 0;
+        virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+        virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
+        virtual void registerStartupException() noexcept = 0;
+        virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
+    };
+
+    IRegistryHub const& getRegistryHub();
+    IMutableRegistryHub& getMutableRegistryHub();
+    void cleanUp();
+    std::string translateActiveException();
+
+}
+
+// end catch_interfaces_registry_hub.h
+#if defined(CATCH_CONFIG_DISABLE)
+    #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \
+        static std::string translatorName( signature )
+#endif
+
+#include <exception>
+#include <string>
+#include <vector>
+
+namespace Catch {
+    using exceptionTranslateFunction = std::string(*)();
+
+    struct IExceptionTranslator;
+    using ExceptionTranslators = std::vector<std::unique_ptr<IExceptionTranslator const>>;
+
+    struct IExceptionTranslator {
+        virtual ~IExceptionTranslator();
+        virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
+    };
+
+    struct IExceptionTranslatorRegistry {
+        virtual ~IExceptionTranslatorRegistry();
+
+        virtual std::string translateActiveException() const = 0;
+    };
+
+    class ExceptionTranslatorRegistrar {
+        template<typename T>
+        class ExceptionTranslator : public IExceptionTranslator {
+        public:
+
+            ExceptionTranslator( std::string(*translateFunction)( T& ) )
+            : m_translateFunction( translateFunction )
+            {}
+
+            std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+                return "";
+#else
+                try {
+                    if( it == itEnd )
+                        std::rethrow_exception(std::current_exception());
+                    else
+                        return (*it)->translate( it+1, itEnd );
+                }
+                catch( T& ex ) {
+                    return m_translateFunction( ex );
+                }
+#endif
+            }
+
+        protected:
+            std::string(*m_translateFunction)( T& );
+        };
+
+    public:
+        template<typename T>
+        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+            getMutableRegistryHub().registerTranslator
+                ( new ExceptionTranslator<T>( translateFunction ) );
+        }
+    };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+    static std::string translatorName( signature ); \
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+    static std::string translatorName( signature )
+
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+
+// end catch_interfaces_exception.h
+// start catch_approx.h
+
+#include <type_traits>
+
+namespace Catch {
+namespace Detail {
+
+    class Approx {
+    private:
+        bool equalityComparisonImpl(double other) const;
+        // Validates the new margin (margin >= 0)
+        // out-of-line to avoid including stdexcept in the header
+        void setMargin(double margin);
+        // Validates the new epsilon (0 < epsilon < 1)
+        // out-of-line to avoid including stdexcept in the header
+        void setEpsilon(double epsilon);
+
+    public:
+        explicit Approx ( double value );
+
+        static Approx custom();
+
+        Approx operator-() const;
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        Approx operator()( T const& value ) {
+            Approx approx( static_cast<double>(value) );
+            approx.m_epsilon = m_epsilon;
+            approx.m_margin = m_margin;
+            approx.m_scale = m_scale;
+            return approx;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        explicit Approx( T const& value ): Approx(static_cast<double>(value))
+        {}
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator == ( const T& lhs, Approx const& rhs ) {
+            auto lhs_v = static_cast<double>(lhs);
+            return rhs.equalityComparisonImpl(lhs_v);
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator == ( Approx const& lhs, const T& rhs ) {
+            return operator==( rhs, lhs );
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator != ( T const& lhs, Approx const& rhs ) {
+            return !operator==( lhs, rhs );
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator != ( Approx const& lhs, T const& rhs ) {
+            return !operator==( rhs, lhs );
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator <= ( T const& lhs, Approx const& rhs ) {
+            return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator <= ( Approx const& lhs, T const& rhs ) {
+            return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator >= ( T const& lhs, Approx const& rhs ) {
+            return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator >= ( Approx const& lhs, T const& rhs ) {
+            return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        Approx& epsilon( T const& newEpsilon ) {
+            double epsilonAsDouble = static_cast<double>(newEpsilon);
+            setEpsilon(epsilonAsDouble);
+            return *this;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        Approx& margin( T const& newMargin ) {
+            double marginAsDouble = static_cast<double>(newMargin);
+            setMargin(marginAsDouble);
+            return *this;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        Approx& scale( T const& newScale ) {
+            m_scale = static_cast<double>(newScale);
+            return *this;
+        }
+
+        std::string toString() const;
+
+    private:
+        double m_epsilon;
+        double m_margin;
+        double m_scale;
+        double m_value;
+    };
+} // end namespace Detail
+
+namespace literals {
+    Detail::Approx operator "" _a(long double val);
+    Detail::Approx operator "" _a(unsigned long long val);
+} // end namespace literals
+
+template<>
+struct StringMaker<Catch::Detail::Approx> {
+    static std::string convert(Catch::Detail::Approx const& value);
+};
+
+} // end namespace Catch
+
+// end catch_approx.h
+// start catch_string_manip.h
+
+#include <string>
+#include <iosfwd>
+#include <vector>
+
+namespace Catch {
+
+    bool startsWith( std::string const& s, std::string const& prefix );
+    bool startsWith( std::string const& s, char prefix );
+    bool endsWith( std::string const& s, std::string const& suffix );
+    bool endsWith( std::string const& s, char suffix );
+    bool contains( std::string const& s, std::string const& infix );
+    void toLowerInPlace( std::string& s );
+    std::string toLower( std::string const& s );
+    //! Returns a new string without whitespace at the start/end
+    std::string trim( std::string const& str );
+    //! Returns a substring of the original ref without whitespace. Beware lifetimes!
+    StringRef trim(StringRef ref);
+
+    // !!! Be aware, returns refs into original string - make sure original string outlives them
+    std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
+    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+    struct pluralise {
+        pluralise( std::size_t count, std::string const& label );
+
+        friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+        std::size_t m_count;
+        std::string m_label;
+    };
+}
+
+// end catch_string_manip.h
+#ifndef CATCH_CONFIG_DISABLE_MATCHERS
+// start catch_capture_matchers.h
+
+// start catch_matchers.h
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+namespace Matchers {
+    namespace Impl {
+
+        template<typename ArgT> struct MatchAllOf;
+        template<typename ArgT> struct MatchAnyOf;
+        template<typename ArgT> struct MatchNotOf;
+
+        class MatcherUntypedBase {
+        public:
+            MatcherUntypedBase() = default;
+            MatcherUntypedBase ( MatcherUntypedBase const& ) = default;
+            MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete;
+            std::string toString() const;
+
+        protected:
+            virtual ~MatcherUntypedBase();
+            virtual std::string describe() const = 0;
+            mutable std::string m_cachedToString;
+        };
+
+#ifdef __clang__
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+        template<typename ObjectT>
+        struct MatcherMethod {
+            virtual bool match( ObjectT const& arg ) const = 0;
+        };
+
+#if defined(__OBJC__)
+        // Hack to fix Catch GH issue #1661. Could use id for generic Object support.
+        // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation
+        template<>
+        struct MatcherMethod<NSString*> {
+            virtual bool match( NSString* arg ) const = 0;
+        };
+#endif
+
+#ifdef __clang__
+#    pragma clang diagnostic pop
+#endif
+
+        template<typename T>
+        struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> {
+
+            MatchAllOf<T> operator && ( MatcherBase const& other ) const;
+            MatchAnyOf<T> operator || ( MatcherBase const& other ) const;
+            MatchNotOf<T> operator ! () const;
+        };
+
+        template<typename ArgT>
+        struct MatchAllOf : MatcherBase<ArgT> {
+            bool match( ArgT const& arg ) const override {
+                for( auto matcher : m_matchers ) {
+                    if (!matcher->match(arg))
+                        return false;
+                }
+                return true;
+            }
+            std::string describe() const override {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                bool first = true;
+                for( auto matcher : m_matchers ) {
+                    if( first )
+                        first = false;
+                    else
+                        description += " and ";
+                    description += matcher->toString();
+                }
+                description += " )";
+                return description;
+            }
+
+            MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) {
+                auto copy(*this);
+                copy.m_matchers.push_back( &other );
+                return copy;
+            }
+
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
+        template<typename ArgT>
+        struct MatchAnyOf : MatcherBase<ArgT> {
+
+            bool match( ArgT const& arg ) const override {
+                for( auto matcher : m_matchers ) {
+                    if (matcher->match(arg))
+                        return true;
+                }
+                return false;
+            }
+            std::string describe() const override {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                bool first = true;
+                for( auto matcher : m_matchers ) {
+                    if( first )
+                        first = false;
+                    else
+                        description += " or ";
+                    description += matcher->toString();
+                }
+                description += " )";
+                return description;
+            }
+
+            MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) {
+                auto copy(*this);
+                copy.m_matchers.push_back( &other );
+                return copy;
+            }
+
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
+
+        template<typename ArgT>
+        struct MatchNotOf : MatcherBase<ArgT> {
+
+            MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
+
+            bool match( ArgT const& arg ) const override {
+                return !m_underlyingMatcher.match( arg );
+            }
+
+            std::string describe() const override {
+                return "not " + m_underlyingMatcher.toString();
+            }
+            MatcherBase<ArgT> const& m_underlyingMatcher;
+        };
+
+        template<typename T>
+        MatchAllOf<T> MatcherBase<T>::operator && ( MatcherBase const& other ) const {
+            return MatchAllOf<T>() && *this && other;
+        }
+        template<typename T>
+        MatchAnyOf<T> MatcherBase<T>::operator || ( MatcherBase const& other ) const {
+            return MatchAnyOf<T>() || *this || other;
+        }
+        template<typename T>
+        MatchNotOf<T> MatcherBase<T>::operator ! () const {
+            return MatchNotOf<T>( *this );
+        }
+
+    } // namespace Impl
+
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // namespace Catch
+
+// end catch_matchers.h
+// start catch_matchers_exception.hpp
+
+namespace Catch {
+namespace Matchers {
+namespace Exception {
+
+class ExceptionMessageMatcher : public MatcherBase<std::exception> {
+    std::string m_message;
+public:
+
+    ExceptionMessageMatcher(std::string const& message):
+        m_message(message)
+    {}
+
+    bool match(std::exception const& ex) const override;
+
+    std::string describe() const override;
+};
+
+} // namespace Exception
+
+Exception::ExceptionMessageMatcher Message(std::string const& message);
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_exception.hpp
+// start catch_matchers_floating.h
+
+namespace Catch {
+namespace Matchers {
+
+    namespace Floating {
+
+        enum class FloatingPointKind : uint8_t;
+
+        struct WithinAbsMatcher : MatcherBase<double> {
+            WithinAbsMatcher(double target, double margin);
+            bool match(double const& matchee) const override;
+            std::string describe() const override;
+        private:
+            double m_target;
+            double m_margin;
+        };
+
+        struct WithinUlpsMatcher : MatcherBase<double> {
+            WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType);
+            bool match(double const& matchee) const override;
+            std::string describe() const override;
+        private:
+            double m_target;
+            uint64_t m_ulps;
+            FloatingPointKind m_type;
+        };
+
+        // Given IEEE-754 format for floats and doubles, we can assume
+        // that float -> double promotion is lossless. Given this, we can
+        // assume that if we do the standard relative comparison of
+        // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
+        // the same result if we do this for floats, as if we do this for
+        // doubles that were promoted from floats.
+        struct WithinRelMatcher : MatcherBase<double> {
+            WithinRelMatcher(double target, double epsilon);
+            bool match(double const& matchee) const override;
+            std::string describe() const override;
+        private:
+            double m_target;
+            double m_epsilon;
+        };
+
+    } // namespace Floating
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+    Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
+    Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
+    Floating::WithinAbsMatcher WithinAbs(double target, double margin);
+    Floating::WithinRelMatcher WithinRel(double target, double eps);
+    // defaults epsilon to 100*numeric_limits<double>::epsilon()
+    Floating::WithinRelMatcher WithinRel(double target);
+    Floating::WithinRelMatcher WithinRel(float target, float eps);
+    // defaults epsilon to 100*numeric_limits<float>::epsilon()
+    Floating::WithinRelMatcher WithinRel(float target);
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_floating.h
+// start catch_matchers_generic.hpp
+
+#include <functional>
+#include <string>
+
+namespace Catch {
+namespace Matchers {
+namespace Generic {
+
+namespace Detail {
+    std::string finalizeDescription(const std::string& desc);
+}
+
+template <typename T>
+class PredicateMatcher : public MatcherBase<T> {
+    std::function<bool(T const&)> m_predicate;
+    std::string m_description;
+public:
+
+    PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr)
+        :m_predicate(std::move(elem)),
+        m_description(Detail::finalizeDescription(descr))
+    {}
+
+    bool match( T const& item ) const override {
+        return m_predicate(item);
+    }
+
+    std::string describe() const override {
+        return m_description;
+    }
+};
+
+} // namespace Generic
+
+    // The following functions create the actual matcher objects.
+    // The user has to explicitly specify type to the function, because
+    // inferring std::function<bool(T const&)> is hard (but possible) and
+    // requires a lot of TMP.
+    template<typename T>
+    Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") {
+        return Generic::PredicateMatcher<T>(predicate, description);
+    }
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_generic.hpp
+// start catch_matchers_string.h
+
+#include <string>
+
+namespace Catch {
+namespace Matchers {
+
+    namespace StdString {
+
+        struct CasedString
+        {
+            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
+            std::string adjustString( std::string const& str ) const;
+            std::string caseSensitivitySuffix() const;
+
+            CaseSensitive::Choice m_caseSensitivity;
+            std::string m_str;
+        };
+
+        struct StringMatcherBase : MatcherBase<std::string> {
+            StringMatcherBase( std::string const& operation, CasedString const& comparator );
+            std::string describe() const override;
+
+            CasedString m_comparator;
+            std::string m_operation;
+        };
+
+        struct EqualsMatcher : StringMatcherBase {
+            EqualsMatcher( CasedString const& comparator );
+            bool match( std::string const& source ) const override;
+        };
+        struct ContainsMatcher : StringMatcherBase {
+            ContainsMatcher( CasedString const& comparator );
+            bool match( std::string const& source ) const override;
+        };
+        struct StartsWithMatcher : StringMatcherBase {
+            StartsWithMatcher( CasedString const& comparator );
+            bool match( std::string const& source ) const override;
+        };
+        struct EndsWithMatcher : StringMatcherBase {
+            EndsWithMatcher( CasedString const& comparator );
+            bool match( std::string const& source ) const override;
+        };
+
+        struct RegexMatcher : MatcherBase<std::string> {
+            RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity );
+            bool match( std::string const& matchee ) const override;
+            std::string describe() const override;
+
+        private:
+            std::string m_regex;
+            CaseSensitive::Choice m_caseSensitivity;
+        };
+
+    } // namespace StdString
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+
+    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_string.h
+// start catch_matchers_vector.h
+
+#include <algorithm>
+
+namespace Catch {
+namespace Matchers {
+
+    namespace Vector {
+        template<typename T, typename Alloc>
+        struct ContainsElementMatcher : MatcherBase<std::vector<T, Alloc>> {
+
+            ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+
+            bool match(std::vector<T, Alloc> const &v) const override {
+                for (auto const& el : v) {
+                    if (el == m_comparator) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            std::string describe() const override {
+                return "Contains: " + ::Catch::Detail::stringify( m_comparator );
+            }
+
+            T const& m_comparator;
+        };
+
+        template<typename T, typename AllocComp, typename AllocMatch>
+        struct ContainsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+            ContainsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
+
+            bool match(std::vector<T, AllocMatch> const &v) const override {
+                // !TBD: see note in EqualsMatcher
+                if (m_comparator.size() > v.size())
+                    return false;
+                for (auto const& comparator : m_comparator) {
+                    auto present = false;
+                    for (const auto& el : v) {
+                        if (el == comparator) {
+                            present = true;
+                            break;
+                        }
+                    }
+                    if (!present) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+            std::string describe() const override {
+                return "Contains: " + ::Catch::Detail::stringify( m_comparator );
+            }
+
+            std::vector<T, AllocComp> const& m_comparator;
+        };
+
+        template<typename T, typename AllocComp, typename AllocMatch>
+        struct EqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+            EqualsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
+
+            bool match(std::vector<T, AllocMatch> const &v) const override {
+                // !TBD: This currently works if all elements can be compared using !=
+                // - a more general approach would be via a compare template that defaults
+                // to using !=. but could be specialised for, e.g. std::vector<T, Alloc> etc
+                // - then just call that directly
+                if (m_comparator.size() != v.size())
+                    return false;
+                for (std::size_t i = 0; i < v.size(); ++i)
+                    if (m_comparator[i] != v[i])
+                        return false;
+                return true;
+            }
+            std::string describe() const override {
+                return "Equals: " + ::Catch::Detail::stringify( m_comparator );
+            }
+            std::vector<T, AllocComp> const& m_comparator;
+        };
+
+        template<typename T, typename AllocComp, typename AllocMatch>
+        struct ApproxMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+            ApproxMatcher(std::vector<T, AllocComp> const& comparator) : m_comparator( comparator ) {}
+
+            bool match(std::vector<T, AllocMatch> const &v) const override {
+                if (m_comparator.size() != v.size())
+                    return false;
+                for (std::size_t i = 0; i < v.size(); ++i)
+                    if (m_comparator[i] != approx(v[i]))
+                        return false;
+                return true;
+            }
+            std::string describe() const override {
+                return "is approx: " + ::Catch::Detail::stringify( m_comparator );
+            }
+            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+            ApproxMatcher& epsilon( T const& newEpsilon ) {
+                approx.epsilon(newEpsilon);
+                return *this;
+            }
+            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+            ApproxMatcher& margin( T const& newMargin ) {
+                approx.margin(newMargin);
+                return *this;
+            }
+            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+            ApproxMatcher& scale( T const& newScale ) {
+                approx.scale(newScale);
+                return *this;
+            }
+
+            std::vector<T, AllocComp> const& m_comparator;
+            mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom();
+        };
+
+        template<typename T, typename AllocComp, typename AllocMatch>
+        struct UnorderedEqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+            UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target) : m_target(target) {}
+            bool match(std::vector<T, AllocMatch> const& vec) const override {
+                if (m_target.size() != vec.size()) {
+                    return false;
+                }
+                return std::is_permutation(m_target.begin(), m_target.end(), vec.begin());
+            }
+
+            std::string describe() const override {
+                return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
+            }
+        private:
+            std::vector<T, AllocComp> const& m_target;
+        };
+
+    } // namespace Vector
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+
+    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+    Vector::ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
+        return Vector::ContainsMatcher<T, AllocComp, AllocMatch>( comparator );
+    }
+
+    template<typename T, typename Alloc = std::allocator<T>>
+    Vector::ContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) {
+        return Vector::ContainsElementMatcher<T, Alloc>( comparator );
+    }
+
+    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+    Vector::EqualsMatcher<T, AllocComp, AllocMatch> Equals( std::vector<T, AllocComp> const& comparator ) {
+        return Vector::EqualsMatcher<T, AllocComp, AllocMatch>( comparator );
+    }
+
+    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+    Vector::ApproxMatcher<T, AllocComp, AllocMatch> Approx( std::vector<T, AllocComp> const& comparator ) {
+        return Vector::ApproxMatcher<T, AllocComp, AllocMatch>( comparator );
+    }
+
+    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+    Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch> UnorderedEquals(std::vector<T, AllocComp> const& target) {
+        return Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch>( target );
+    }
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_vector.h
+namespace Catch {
+
+    template<typename ArgT, typename MatcherT>
+    class MatchExpr : public ITransientExpression {
+        ArgT const& m_arg;
+        MatcherT m_matcher;
+        StringRef m_matcherString;
+    public:
+        MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString )
+        :   ITransientExpression{ true, matcher.match( arg ) },
+            m_arg( arg ),
+            m_matcher( matcher ),
+            m_matcherString( matcherString )
+        {}
+
+        void streamReconstructedExpression( std::ostream &os ) const override {
+            auto matcherAsString = m_matcher.toString();
+            os << Catch::Detail::stringify( m_arg ) << ' ';
+            if( matcherAsString == Detail::unprintableString )
+                os << m_matcherString;
+            else
+                os << matcherAsString;
+        }
+    };
+
+    using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
+
+    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  );
+
+    template<typename ArgT, typename MatcherT>
+    auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString  ) -> MatchExpr<ArgT, MatcherT> {
+        return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString );
+    }
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        INTERNAL_CATCH_TRY { \
+            catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \
+        } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        if( catchAssertionHandler.allowThrows() ) \
+            try { \
+                static_cast<void>(__VA_ARGS__ ); \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+            } \
+            catch( exceptionType const& ex ) { \
+                catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \
+            } \
+            catch( ... ) { \
+                catchAssertionHandler.handleUnexpectedInflightException(); \
+            } \
+        else \
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+// end catch_capture_matchers.h
+#endif
+// start catch_generators.hpp
+
+// start catch_interfaces_generatortracker.h
+
+
+#include <memory>
+
+namespace Catch {
+
+    namespace Generators {
+        class GeneratorUntypedBase {
+        public:
+            GeneratorUntypedBase() = default;
+            virtual ~GeneratorUntypedBase();
+            // Attempts to move the generator to the next element
+             //
+             // Returns true iff the move succeeded (and a valid element
+             // can be retrieved).
+            virtual bool next() = 0;
+        };
+        using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>;
+
+    } // namespace Generators
+
+    struct IGeneratorTracker {
+        virtual ~IGeneratorTracker();
+        virtual auto hasGenerator() const -> bool = 0;
+        virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
+        virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
+    };
+
+} // namespace Catch
+
+// end catch_interfaces_generatortracker.h
+// start catch_enforce.h
+
+#include <exception>
+
+namespace Catch {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+    template <typename Ex>
+    [[noreturn]]
+    void throw_exception(Ex const& e) {
+        throw e;
+    }
+#else // ^^ Exceptions are enabled //  Exceptions are disabled vv
+    [[noreturn]]
+    void throw_exception(std::exception const& e);
+#endif
+
+    [[noreturn]]
+    void throw_logic_error(std::string const& msg);
+    [[noreturn]]
+    void throw_domain_error(std::string const& msg);
+    [[noreturn]]
+    void throw_runtime_error(std::string const& msg);
+
+} // namespace Catch;
+
+#define CATCH_MAKE_MSG(...) \
+    (Catch::ReusableStringStream() << __VA_ARGS__).str()
+
+#define CATCH_INTERNAL_ERROR(...) \
+    Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__))
+
+#define CATCH_ERROR(...) \
+    Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+#define CATCH_RUNTIME_ERROR(...) \
+    Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+#define CATCH_ENFORCE( condition, ... ) \
+    do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
+
+// end catch_enforce.h
+#include <memory>
+#include <vector>
+#include <cassert>
+
+#include <utility>
+#include <exception>
+
+namespace Catch {
+
+class GeneratorException : public std::exception {
+    const char* const m_msg = "";
+
+public:
+    GeneratorException(const char* msg):
+        m_msg(msg)
+    {}
+
+    const char* what() const noexcept override final;
+};
+
+namespace Generators {
+
+    // !TBD move this into its own location?
+    namespace pf{
+        template<typename T, typename... Args>
+        std::unique_ptr<T> make_unique( Args&&... args ) {
+            return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+        }
+    }
+
+    template<typename T>
+    struct IGenerator : GeneratorUntypedBase {
+        virtual ~IGenerator() = default;
+
+        // Returns the current element of the generator
+        //
+        // \Precondition The generator is either freshly constructed,
+        // or the last call to `next()` returned true
+        virtual T const& get() const = 0;
+        using type = T;
+    };
+
+    template<typename T>
+    class SingleValueGenerator final : public IGenerator<T> {
+        T m_value;
+    public:
+        SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
+
+        T const& get() const override {
+            return m_value;
+        }
+        bool next() override {
+            return false;
+        }
+    };
+
+    template<typename T>
+    class FixedValuesGenerator final : public IGenerator<T> {
+        static_assert(!std::is_same<T, bool>::value,
+            "FixedValuesGenerator does not support bools because of std::vector<bool>"
+            "specialization, use SingleValue Generator instead.");
+        std::vector<T> m_values;
+        size_t m_idx = 0;
+    public:
+        FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
+
+        T const& get() const override {
+            return m_values[m_idx];
+        }
+        bool next() override {
+            ++m_idx;
+            return m_idx < m_values.size();
+        }
+    };
+
+    template <typename T>
+    class GeneratorWrapper final {
+        std::unique_ptr<IGenerator<T>> m_generator;
+    public:
+        GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator):
+            m_generator(std::move(generator))
+        {}
+        T const& get() const {
+            return m_generator->get();
+        }
+        bool next() {
+            return m_generator->next();
+        }
+    };
+
+    template <typename T>
+    GeneratorWrapper<T> value(T&& value) {
+        return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
+    }
+    template <typename T>
+    GeneratorWrapper<T> values(std::initializer_list<T> values) {
+        return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values));
+    }
+
+    template<typename T>
+    class Generators : public IGenerator<T> {
+        std::vector<GeneratorWrapper<T>> m_generators;
+        size_t m_current = 0;
+
+        void populate(GeneratorWrapper<T>&& generator) {
+            m_generators.emplace_back(std::move(generator));
+        }
+        void populate(T&& val) {
+            m_generators.emplace_back(value(std::forward<T>(val)));
+        }
+        template<typename U>
+        void populate(U&& val) {
+            populate(T(std::forward<U>(val)));
+        }
+        template<typename U, typename... Gs>
+        void populate(U&& valueOrGenerator, Gs &&... moreGenerators) {
+            populate(std::forward<U>(valueOrGenerator));
+            populate(std::forward<Gs>(moreGenerators)...);
+        }
+
+    public:
+        template <typename... Gs>
+        Generators(Gs &&... moreGenerators) {
+            m_generators.reserve(sizeof...(Gs));
+            populate(std::forward<Gs>(moreGenerators)...);
+        }
+
+        T const& get() const override {
+            return m_generators[m_current].get();
+        }
+
+        bool next() override {
+            if (m_current >= m_generators.size()) {
+                return false;
+            }
+            const bool current_status = m_generators[m_current].next();
+            if (!current_status) {
+                ++m_current;
+            }
+            return m_current < m_generators.size();
+        }
+    };
+
+    template<typename... Ts>
+    GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) {
+        return values<std::tuple<Ts...>>( tuples );
+    }
+
+    // Tag type to signal that a generator sequence should convert arguments to a specific type
+    template <typename T>
+    struct as {};
+
+    template<typename T, typename... Gs>
+    auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
+        return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
+    }
+    template<typename T>
+    auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
+        return Generators<T>(std::move(generator));
+    }
+    template<typename T, typename... Gs>
+    auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> {
+        return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
+    }
+    template<typename T, typename U, typename... Gs>
+    auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
+        return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
+    }
+
+    auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
+
+    template<typename L>
+    // Note: The type after -> is weird, because VS2015 cannot parse
+    //       the expression used in the typedef inside, when it is in
+    //       return type. Yeah.
+    auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
+        using UnderlyingType = typename decltype(generatorExpression())::type;
+
+        IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo );
+        if (!tracker.hasGenerator()) {
+            tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
+        }
+
+        auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
+        return generator.get();
+    }
+
+} // namespace Generators
+} // namespace Catch
+
+#define GENERATE( ... ) \
+    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+                                 CATCH_INTERNAL_LINEINFO, \
+                                 [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+#define GENERATE_COPY( ... ) \
+    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+                                 CATCH_INTERNAL_LINEINFO, \
+                                 [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+#define GENERATE_REF( ... ) \
+    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+                                 CATCH_INTERNAL_LINEINFO, \
+                                 [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+
+// end catch_generators.hpp
+// start catch_generators_generic.hpp
+
+namespace Catch {
+namespace Generators {
+
+    template <typename T>
+    class TakeGenerator : public IGenerator<T> {
+        GeneratorWrapper<T> m_generator;
+        size_t m_returned = 0;
+        size_t m_target;
+    public:
+        TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
+            m_generator(std::move(generator)),
+            m_target(target)
+        {
+            assert(target != 0 && "Empty generators are not allowed");
+        }
+        T const& get() const override {
+            return m_generator.get();
+        }
+        bool next() override {
+            ++m_returned;
+            if (m_returned >= m_target) {
+                return false;
+            }
+
+            const auto success = m_generator.next();
+            // If the underlying generator does not contain enough values
+            // then we cut short as well
+            if (!success) {
+                m_returned = m_target;
+            }
+            return success;
+        }
+    };
+
+    template <typename T>
+    GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
+        return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator)));
+    }
+
+    template <typename T, typename Predicate>
+    class FilterGenerator : public IGenerator<T> {
+        GeneratorWrapper<T> m_generator;
+        Predicate m_predicate;
+    public:
+        template <typename P = Predicate>
+        FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
+            m_generator(std::move(generator)),
+            m_predicate(std::forward<P>(pred))
+        {
+            if (!m_predicate(m_generator.get())) {
+                // It might happen that there are no values that pass the
+                // filter. In that case we throw an exception.
+                auto has_initial_value = next();
+                if (!has_initial_value) {
+                    Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));
+                }
+            }
+        }
+
+        T const& get() const override {
+            return m_generator.get();
+        }
+
+        bool next() override {
+            bool success = m_generator.next();
+            if (!success) {
+                return false;
+            }
+            while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
+            return success;
+        }
+    };
+
+    template <typename T, typename Predicate>
+    GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
+        return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator))));
+    }
+
+    template <typename T>
+    class RepeatGenerator : public IGenerator<T> {
+        static_assert(!std::is_same<T, bool>::value,
+            "RepeatGenerator currently does not support bools"
+            "because of std::vector<bool> specialization");
+        GeneratorWrapper<T> m_generator;
+        mutable std::vector<T> m_returned;
+        size_t m_target_repeats;
+        size_t m_current_repeat = 0;
+        size_t m_repeat_index = 0;
+    public:
+        RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
+            m_generator(std::move(generator)),
+            m_target_repeats(repeats)
+        {
+            assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
+        }
+
+        T const& get() const override {
+            if (m_current_repeat == 0) {
+                m_returned.push_back(m_generator.get());
+                return m_returned.back();
+            }
+            return m_returned[m_repeat_index];
+        }
+
+        bool next() override {
+            // There are 2 basic cases:
+            // 1) We are still reading the generator
+            // 2) We are reading our own cache
+
+            // In the first case, we need to poke the underlying generator.
+            // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
+            if (m_current_repeat == 0) {
+                const auto success = m_generator.next();
+                if (!success) {
+                    ++m_current_repeat;
+                }
+                return m_current_repeat < m_target_repeats;
+            }
+
+            // In the second case, we need to move indices forward and check that we haven't run up against the end
+            ++m_repeat_index;
+            if (m_repeat_index == m_returned.size()) {
+                m_repeat_index = 0;
+                ++m_current_repeat;
+            }
+            return m_current_repeat < m_target_repeats;
+        }
+    };
+
+    template <typename T>
+    GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
+        return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
+    }
+
+    template <typename T, typename U, typename Func>
+    class MapGenerator : public IGenerator<T> {
+        // TBD: provide static assert for mapping function, for friendly error message
+        GeneratorWrapper<U> m_generator;
+        Func m_function;
+        // To avoid returning dangling reference, we have to save the values
+        T m_cache;
+    public:
+        template <typename F2 = Func>
+        MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
+            m_generator(std::move(generator)),
+            m_function(std::forward<F2>(function)),
+            m_cache(m_function(m_generator.get()))
+        {}
+
+        T const& get() const override {
+            return m_cache;
+        }
+        bool next() override {
+            const auto success = m_generator.next();
+            if (success) {
+                m_cache = m_function(m_generator.get());
+            }
+            return success;
+        }
+    };
+
+    template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
+    GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
+        return GeneratorWrapper<T>(
+            pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
+        );
+    }
+
+    template <typename T, typename U, typename Func>
+    GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
+        return GeneratorWrapper<T>(
+            pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
+        );
+    }
+
+    template <typename T>
+    class ChunkGenerator final : public IGenerator<std::vector<T>> {
+        std::vector<T> m_chunk;
+        size_t m_chunk_size;
+        GeneratorWrapper<T> m_generator;
+        bool m_used_up = false;
+    public:
+        ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
+            m_chunk_size(size), m_generator(std::move(generator))
+        {
+            m_chunk.reserve(m_chunk_size);
+            if (m_chunk_size != 0) {
+                m_chunk.push_back(m_generator.get());
+                for (size_t i = 1; i < m_chunk_size; ++i) {
+                    if (!m_generator.next()) {
+                        Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
+                    }
+                    m_chunk.push_back(m_generator.get());
+                }
+            }
+        }
+        std::vector<T> const& get() const override {
+            return m_chunk;
+        }
+        bool next() override {
+            m_chunk.clear();
+            for (size_t idx = 0; idx < m_chunk_size; ++idx) {
+                if (!m_generator.next()) {
+                    return false;
+                }
+                m_chunk.push_back(m_generator.get());
+            }
+            return true;
+        }
+    };
+
+    template <typename T>
+    GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
+        return GeneratorWrapper<std::vector<T>>(
+            pf::make_unique<ChunkGenerator<T>>(size, std::move(generator))
+        );
+    }
+
+} // namespace Generators
+} // namespace Catch
+
+// end catch_generators_generic.hpp
+// start catch_generators_specific.hpp
+
+// start catch_context.h
+
+#include <memory>
+
+namespace Catch {
+
+    struct IResultCapture;
+    struct IRunner;
+    struct IConfig;
+    struct IMutableContext;
+
+    using IConfigPtr = std::shared_ptr<IConfig const>;
+
+    struct IContext
+    {
+        virtual ~IContext();
+
+        virtual IResultCapture* getResultCapture() = 0;
+        virtual IRunner* getRunner() = 0;
+        virtual IConfigPtr const& getConfig() const = 0;
+    };
+
+    struct IMutableContext : IContext
+    {
+        virtual ~IMutableContext();
+        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+        virtual void setRunner( IRunner* runner ) = 0;
+        virtual void setConfig( IConfigPtr const& config ) = 0;
+
+    private:
+        static IMutableContext *currentContext;
+        friend IMutableContext& getCurrentMutableContext();
+        friend void cleanUpContext();
+        static void createContext();
+    };
+
+    inline IMutableContext& getCurrentMutableContext()
+    {
+        if( !IMutableContext::currentContext )
+            IMutableContext::createContext();
+        // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
+        return *IMutableContext::currentContext;
+    }
+
+    inline IContext& getCurrentContext()
+    {
+        return getCurrentMutableContext();
+    }
+
+    void cleanUpContext();
+
+    class SimplePcg32;
+    SimplePcg32& rng();
+}
+
+// end catch_context.h
+// start catch_interfaces_config.h
+
+// start catch_option.hpp
+
+namespace Catch {
+
+    // An optional type
+    template<typename T>
+    class Option {
+    public:
+        Option() : nullableValue( nullptr ) {}
+        Option( T const& _value )
+        : nullableValue( new( storage ) T( _value ) )
+        {}
+        Option( Option const& _other )
+        : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
+        {}
+
+        ~Option() {
+            reset();
+        }
+
+        Option& operator= ( Option const& _other ) {
+            if( &_other != this ) {
+                reset();
+                if( _other )
+                    nullableValue = new( storage ) T( *_other );
+            }
+            return *this;
+        }
+        Option& operator = ( T const& _value ) {
+            reset();
+            nullableValue = new( storage ) T( _value );
+            return *this;
+        }
+
+        void reset() {
+            if( nullableValue )
+                nullableValue->~T();
+            nullableValue = nullptr;
+        }
+
+        T& operator*() { return *nullableValue; }
+        T const& operator*() const { return *nullableValue; }
+        T* operator->() { return nullableValue; }
+        const T* operator->() const { return nullableValue; }
+
+        T valueOr( T const& defaultValue ) const {
+            return nullableValue ? *nullableValue : defaultValue;
+        }
+
+        bool some() const { return nullableValue != nullptr; }
+        bool none() const { return nullableValue == nullptr; }
+
+        bool operator !() const { return nullableValue == nullptr; }
+        explicit operator bool() const {
+            return some();
+        }
+
+    private:
+        T *nullableValue;
+        alignas(alignof(T)) char storage[sizeof(T)];
+    };
+
+} // end namespace Catch
+
+// end catch_option.hpp
+#include <chrono>
+#include <iosfwd>
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+    enum class Verbosity {
+        Quiet = 0,
+        Normal,
+        High
+    };
+
+    struct WarnAbout { enum What {
+        Nothing = 0x00,
+        NoAssertions = 0x01,
+        NoTests = 0x02
+    }; };
+
+    struct ShowDurations { enum OrNot {
+        DefaultForReporter,
+        Always,
+        Never
+    }; };
+    struct RunTests { enum InWhatOrder {
+        InDeclarationOrder,
+        InLexicographicalOrder,
+        InRandomOrder
+    }; };
+    struct UseColour { enum YesOrNo {
+        Auto,
+        Yes,
+        No
+    }; };
+    struct WaitForKeypress { enum When {
+        Never,
+        BeforeStart = 1,
+        BeforeExit = 2,
+        BeforeStartAndExit = BeforeStart | BeforeExit
+    }; };
+
+    class TestSpec;
+
+    struct IConfig : NonCopyable {
+
+        virtual ~IConfig();
+
+        virtual bool allowThrows() const = 0;
+        virtual std::ostream& stream() const = 0;
+        virtual std::string name() const = 0;
+        virtual bool includeSuccessfulResults() const = 0;
+        virtual bool shouldDebugBreak() const = 0;
+        virtual bool warnAboutMissingAssertions() const = 0;
+        virtual bool warnAboutNoTests() const = 0;
+        virtual int abortAfter() const = 0;
+        virtual bool showInvisibles() const = 0;
+        virtual ShowDurations::OrNot showDurations() const = 0;
+        virtual double minDuration() const = 0;
+        virtual TestSpec const& testSpec() const = 0;
+        virtual bool hasTestFilters() const = 0;
+        virtual std::vector<std::string> const& getTestsOrTags() const = 0;
+        virtual RunTests::InWhatOrder runOrder() const = 0;
+        virtual unsigned int rngSeed() const = 0;
+        virtual UseColour::YesOrNo useColour() const = 0;
+        virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+        virtual Verbosity verbosity() const = 0;
+
+        virtual bool benchmarkNoAnalysis() const = 0;
+        virtual int benchmarkSamples() const = 0;
+        virtual double benchmarkConfidenceInterval() const = 0;
+        virtual unsigned int benchmarkResamples() const = 0;
+        virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0;
+    };
+
+    using IConfigPtr = std::shared_ptr<IConfig const>;
+}
+
+// end catch_interfaces_config.h
+// start catch_random_number_generator.h
+
+#include <cstdint>
+
+namespace Catch {
+
+    // This is a simple implementation of C++11 Uniform Random Number
+    // Generator. It does not provide all operators, because Catch2
+    // does not use it, but it should behave as expected inside stdlib's
+    // distributions.
+    // The implementation is based on the PCG family (http://pcg-random.org)
+    class SimplePcg32 {
+        using state_type = std::uint64_t;
+    public:
+        using result_type = std::uint32_t;
+        static constexpr result_type (min)() {
+            return 0;
+        }
+        static constexpr result_type (max)() {
+            return static_cast<result_type>(-1);
+        }
+
+        // Provide some default initial state for the default constructor
+        SimplePcg32():SimplePcg32(0xed743cc4U) {}
+
+        explicit SimplePcg32(result_type seed_);
+
+        void seed(result_type seed_);
+        void discard(uint64_t skip);
+
+        result_type operator()();
+
+    private:
+        friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
+        friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
+
+        // In theory we also need operator<< and operator>>
+        // In practice we do not use them, so we will skip them for now
+
+        std::uint64_t m_state;
+        // This part of the state determines which "stream" of the numbers
+        // is chosen -- we take it as a constant for Catch2, so we only
+        // need to deal with seeding the main state.
+        // Picked by reading 8 bytes from `/dev/random` :-)
+        static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL;
+    };
+
+} // end namespace Catch
+
+// end catch_random_number_generator.h
+#include <random>
+
+namespace Catch {
+namespace Generators {
+
+template <typename Float>
+class RandomFloatingGenerator final : public IGenerator<Float> {
+    Catch::SimplePcg32& m_rng;
+    std::uniform_real_distribution<Float> m_dist;
+    Float m_current_number;
+public:
+
+    RandomFloatingGenerator(Float a, Float b):
+        m_rng(rng()),
+        m_dist(a, b) {
+        static_cast<void>(next());
+    }
+
+    Float const& get() const override {
+        return m_current_number;
+    }
+    bool next() override {
+        m_current_number = m_dist(m_rng);
+        return true;
+    }
+};
+
+template <typename Integer>
+class RandomIntegerGenerator final : public IGenerator<Integer> {
+    Catch::SimplePcg32& m_rng;
+    std::uniform_int_distribution<Integer> m_dist;
+    Integer m_current_number;
+public:
+
+    RandomIntegerGenerator(Integer a, Integer b):
+        m_rng(rng()),
+        m_dist(a, b) {
+        static_cast<void>(next());
+    }
+
+    Integer const& get() const override {
+        return m_current_number;
+    }
+    bool next() override {
+        m_current_number = m_dist(m_rng);
+        return true;
+    }
+};
+
+// TODO: Ideally this would be also constrained against the various char types,
+//       but I don't expect users to run into that in practice.
+template <typename T>
+typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value,
+GeneratorWrapper<T>>::type
+random(T a, T b) {
+    return GeneratorWrapper<T>(
+        pf::make_unique<RandomIntegerGenerator<T>>(a, b)
+    );
+}
+
+template <typename T>
+typename std::enable_if<std::is_floating_point<T>::value,
+GeneratorWrapper<T>>::type
+random(T a, T b) {
+    return GeneratorWrapper<T>(
+        pf::make_unique<RandomFloatingGenerator<T>>(a, b)
+    );
+}
+
+template <typename T>
+class RangeGenerator final : public IGenerator<T> {
+    T m_current;
+    T m_end;
+    T m_step;
+    bool m_positive;
+
+public:
+    RangeGenerator(T const& start, T const& end, T const& step):
+        m_current(start),
+        m_end(end),
+        m_step(step),
+        m_positive(m_step > T(0))
+    {
+        assert(m_current != m_end && "Range start and end cannot be equal");
+        assert(m_step != T(0) && "Step size cannot be zero");
+        assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end");
+    }
+
+    RangeGenerator(T const& start, T const& end):
+        RangeGenerator(start, end, (start < end) ? T(1) : T(-1))
+    {}
+
+    T const& get() const override {
+        return m_current;
+    }
+
+    bool next() override {
+        m_current += m_step;
+        return (m_positive) ? (m_current < m_end) : (m_current > m_end);
+    }
+};
+
+template <typename T>
+GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
+    static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric");
+    return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step));
+}
+
+template <typename T>
+GeneratorWrapper<T> range(T const& start, T const& end) {
+    static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
+    return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end));
+}
+
+template <typename T>
+class IteratorGenerator final : public IGenerator<T> {
+    static_assert(!std::is_same<T, bool>::value,
+        "IteratorGenerator currently does not support bools"
+        "because of std::vector<bool> specialization");
+
+    std::vector<T> m_elems;
+    size_t m_current = 0;
+public:
+    template <typename InputIterator, typename InputSentinel>
+    IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) {
+        if (m_elems.empty()) {
+            Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values"));
+        }
+    }
+
+    T const& get() const override {
+        return m_elems[m_current];
+    }
+
+    bool next() override {
+        ++m_current;
+        return m_current != m_elems.size();
+    }
+};
+
+template <typename InputIterator,
+          typename InputSentinel,
+          typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
+GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
+    return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(from, to));
+}
+
+template <typename Container,
+          typename ResultType = typename Container::value_type>
+GeneratorWrapper<ResultType> from_range(Container const& cnt) {
+    return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end()));
+}
+
+} // namespace Generators
+} // namespace Catch
+
+// end catch_generators_specific.hpp
+
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// start catch_test_case_info.h
+
+#include <string>
+#include <vector>
+#include <memory>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    struct ITestInvoker;
+
+    struct TestCaseInfo {
+        enum SpecialProperties{
+            None = 0,
+            IsHidden = 1 << 1,
+            ShouldFail = 1 << 2,
+            MayFail = 1 << 3,
+            Throws = 1 << 4,
+            NonPortable = 1 << 5,
+            Benchmark = 1 << 6
+        };
+
+        TestCaseInfo(   std::string const& _name,
+                        std::string const& _className,
+                        std::string const& _description,
+                        std::vector<std::string> const& _tags,
+                        SourceLineInfo const& _lineInfo );
+
+        friend void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags );
+
+        bool isHidden() const;
+        bool throws() const;
+        bool okToFail() const;
+        bool expectedToFail() const;
+
+        std::string tagsAsString() const;
+
+        std::string name;
+        std::string className;
+        std::string description;
+        std::vector<std::string> tags;
+        std::vector<std::string> lcaseTags;
+        SourceLineInfo lineInfo;
+        SpecialProperties properties;
+    };
+
+    class TestCase : public TestCaseInfo {
+    public:
+
+        TestCase( ITestInvoker* testCase, TestCaseInfo&& info );
+
+        TestCase withName( std::string const& _newName ) const;
+
+        void invoke() const;
+
+        TestCaseInfo const& getTestCaseInfo() const;
+
+        bool operator == ( TestCase const& other ) const;
+        bool operator < ( TestCase const& other ) const;
+
+    private:
+        std::shared_ptr<ITestInvoker> test;
+    };
+
+    TestCase makeTestCase(  ITestInvoker* testCase,
+                            std::string const& className,
+                            NameAndTags const& nameAndTags,
+                            SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// end catch_test_case_info.h
+// start catch_interfaces_runner.h
+
+namespace Catch {
+
+    struct IRunner {
+        virtual ~IRunner();
+        virtual bool aborting() const = 0;
+    };
+}
+
+// end catch_interfaces_runner.h
+
+#ifdef __OBJC__
+// start catch_objc.hpp
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+@protocol OcFixture
+
+@optional
+
+-(void) setUp;
+-(void) tearDown;
+
+@end
+
+namespace Catch {
+
+    class OcMethod : public ITestInvoker {
+
+    public:
+        OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
+
+        virtual void invoke() const {
+            id obj = [[m_cls alloc] init];
+
+            performOptionalSelector( obj, @selector(setUp)  );
+            performOptionalSelector( obj, m_sel );
+            performOptionalSelector( obj, @selector(tearDown)  );
+
+            arcSafeRelease( obj );
+        }
+    private:
+        virtual ~OcMethod() {}
+
+        Class m_cls;
+        SEL m_sel;
+    };
+
+    namespace Detail{
+
+        inline std::string getAnnotation(   Class cls,
+                                            std::string const& annotationName,
+                                            std::string const& testCaseName ) {
+            NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+            SEL sel = NSSelectorFromString( selStr );
+            arcSafeRelease( selStr );
+            id value = performOptionalSelector( cls, sel );
+            if( value )
+                return [(NSString*)value UTF8String];
+            return "";
+        }
+    }
+
+    inline std::size_t registerTestMethods() {
+        std::size_t noTestMethods = 0;
+        int noClasses = objc_getClassList( nullptr, 0 );
+
+        Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+        objc_getClassList( classes, noClasses );
+
+        for( int c = 0; c < noClasses; c++ ) {
+            Class cls = classes[c];
+            {
+                u_int count;
+                Method* methods = class_copyMethodList( cls, &count );
+                for( u_int m = 0; m < count ; m++ ) {
+                    SEL selector = method_getName(methods[m]);
+                    std::string methodName = sel_getName(selector);
+                    if( startsWith( methodName, "Catch_TestCase_" ) ) {
+                        std::string testCaseName = methodName.substr( 15 );
+                        std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+                        std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+                        const char* className = class_getName( cls );
+
+                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) );
+                        noTestMethods++;
+                    }
+                }
+                free(methods);
+            }
+        }
+        return noTestMethods;
+    }
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+
+    namespace Matchers {
+        namespace Impl {
+        namespace NSStringMatchers {
+
+            struct StringHolder : MatcherBase<NSString*>{
+                StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+                StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+                StringHolder() {
+                    arcSafeRelease( m_substr );
+                }
+
+                bool match( NSString* str ) const override {
+                    return false;
+                }
+
+                NSString* CATCH_ARC_STRONG m_substr;
+            };
+
+            struct Equals : StringHolder {
+                Equals( NSString* substr ) : StringHolder( substr ){}
+
+                bool match( NSString* str ) const override {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str isEqualToString:m_substr];
+                }
+
+                std::string describe() const override {
+                    return "equals string: " + Catch::Detail::stringify( m_substr );
+                }
+            };
+
+            struct Contains : StringHolder {
+                Contains( NSString* substr ) : StringHolder( substr ){}
+
+                bool match( NSString* str ) const override {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location != NSNotFound;
+                }
+
+                std::string describe() const override {
+                    return "contains string: " + Catch::Detail::stringify( m_substr );
+                }
+            };
+
+            struct StartsWith : StringHolder {
+                StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+                bool match( NSString* str ) const override {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == 0;
+                }
+
+                std::string describe() const override {
+                    return "starts with: " + Catch::Detail::stringify( m_substr );
+                }
+            };
+            struct EndsWith : StringHolder {
+                EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+                bool match( NSString* str ) const override {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+                }
+
+                std::string describe() const override {
+                    return "ends with: " + Catch::Detail::stringify( m_substr );
+                }
+            };
+
+        } // namespace NSStringMatchers
+        } // namespace Impl
+
+        inline Impl::NSStringMatchers::Equals
+            Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+        inline Impl::NSStringMatchers::Contains
+            Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+        inline Impl::NSStringMatchers::StartsWith
+            StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+        inline Impl::NSStringMatchers::EndsWith
+            EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+    } // namespace Matchers
+
+    using namespace Matchers;
+
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix
+#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \
++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \
+{ \
+return @ name; \
+} \
++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \
+{ \
+return @ desc; \
+} \
+-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix )
+
+#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ )
+
+// end catch_objc.hpp
+#endif
+
+// Benchmarking needs the externally-facing parts of reporters to work
+#if defined(CATCH_CONFIG_EXTERNAL_INTERFACES) || defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+// start catch_external_interfaces.h
+
+// start catch_reporter_bases.hpp
+
+// start catch_interfaces_reporter.h
+
+// start catch_config.hpp
+
+// start catch_test_spec_parser.h
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// start catch_test_spec.h
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// start catch_wildcard_pattern.h
+
+namespace Catch
+{
+    class WildcardPattern {
+        enum WildcardPosition {
+            NoWildcard = 0,
+            WildcardAtStart = 1,
+            WildcardAtEnd = 2,
+            WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+        };
+
+    public:
+
+        WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity );
+        virtual ~WildcardPattern() = default;
+        virtual bool matches( std::string const& str ) const;
+
+    private:
+        std::string normaliseString( std::string const& str ) const;
+        CaseSensitive::Choice m_caseSensitivity;
+        WildcardPosition m_wildcard = NoWildcard;
+        std::string m_pattern;
+    };
+}
+
+// end catch_wildcard_pattern.h
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+    struct IConfig;
+
+    class TestSpec {
+        class Pattern {
+        public:
+            explicit Pattern( std::string const& name );
+            virtual ~Pattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+            std::string const& name() const;
+        private:
+            std::string const m_name;
+        };
+        using PatternPtr = std::shared_ptr<Pattern>;
+
+        class NamePattern : public Pattern {
+        public:
+            explicit NamePattern( std::string const& name, std::string const& filterString );
+            bool matches( TestCaseInfo const& testCase ) const override;
+        private:
+            WildcardPattern m_wildcardPattern;
+        };
+
+        class TagPattern : public Pattern {
+        public:
+            explicit TagPattern( std::string const& tag, std::string const& filterString );
+            bool matches( TestCaseInfo const& testCase ) const override;
+        private:
+            std::string m_tag;
+        };
+
+        class ExcludedPattern : public Pattern {
+        public:
+            explicit ExcludedPattern( PatternPtr const& underlyingPattern );
+            bool matches( TestCaseInfo const& testCase ) const override;
+        private:
+            PatternPtr m_underlyingPattern;
+        };
+
+        struct Filter {
+            std::vector<PatternPtr> m_patterns;
+
+            bool matches( TestCaseInfo const& testCase ) const;
+            std::string name() const;
+        };
+
+    public:
+        struct FilterMatch {
+            std::string name;
+            std::vector<TestCase const*> tests;
+        };
+        using Matches = std::vector<FilterMatch>;
+        using vectorStrings = std::vector<std::string>;
+
+        bool hasFilters() const;
+        bool matches( TestCaseInfo const& testCase ) const;
+        Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
+        const vectorStrings & getInvalidArgs() const;
+
+    private:
+        std::vector<Filter> m_filters;
+        std::vector<std::string> m_invalidArgs;
+        friend class TestSpecParser;
+    };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// end catch_test_spec.h
+// start catch_interfaces_tag_alias_registry.h
+
+#include <string>
+
+namespace Catch {
+
+    struct TagAlias;
+
+    struct ITagAliasRegistry {
+        virtual ~ITagAliasRegistry();
+        // Nullptr if not present
+        virtual TagAlias const* find( std::string const& alias ) const = 0;
+        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+        static ITagAliasRegistry const& get();
+    };
+
+} // end namespace Catch
+
+// end catch_interfaces_tag_alias_registry.h
+namespace Catch {
+
+    class TestSpecParser {
+        enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+        Mode m_mode = None;
+        Mode lastMode = None;
+        bool m_exclusion = false;
+        std::size_t m_pos = 0;
+        std::size_t m_realPatternPos = 0;
+        std::string m_arg;
+        std::string m_substring;
+        std::string m_patternName;
+        std::vector<std::size_t> m_escapeChars;
+        TestSpec::Filter m_currentFilter;
+        TestSpec m_testSpec;
+        ITagAliasRegistry const* m_tagAliases = nullptr;
+
+    public:
+        TestSpecParser( ITagAliasRegistry const& tagAliases );
+
+        TestSpecParser& parse( std::string const& arg );
+        TestSpec testSpec();
+
+    private:
+        bool visitChar( char c );
+        void startNewMode( Mode mode );
+        bool processNoneChar( char c );
+        void processNameChar( char c );
+        bool processOtherChar( char c );
+        void endMode();
+        void escape();
+        bool isControlChar( char c ) const;
+        void saveLastMode();
+        void revertBackToLastMode();
+        void addFilter();
+        bool separate();
+
+        // Handles common preprocessing of the pattern for name/tag patterns
+        std::string preprocessPattern();
+        // Adds the current pattern as a test name
+        void addNamePattern();
+        // Adds the current pattern as a tag
+        void addTagPattern();
+
+        inline void addCharToPattern(char c) {
+            m_substring += c;
+            m_patternName += c;
+            m_realPatternPos++;
+        }
+
+    };
+    TestSpec parseTestSpec( std::string const& arg );
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// end catch_test_spec_parser.h
+// Libstdc++ doesn't like incomplete classes for unique_ptr
+
+#include <memory>
+#include <vector>
+#include <string>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+    struct IStream;
+
+    struct ConfigData {
+        bool listTests = false;
+        bool listTags = false;
+        bool listReporters = false;
+        bool listTestNamesOnly = false;
+
+        bool showSuccessfulTests = false;
+        bool shouldDebugBreak = false;
+        bool noThrow = false;
+        bool showHelp = false;
+        bool showInvisibles = false;
+        bool filenamesAsTags = false;
+        bool libIdentify = false;
+
+        int abortAfter = -1;
+        unsigned int rngSeed = 0;
+
+        bool benchmarkNoAnalysis = false;
+        unsigned int benchmarkSamples = 100;
+        double benchmarkConfidenceInterval = 0.95;
+        unsigned int benchmarkResamples = 100000;
+        std::chrono::milliseconds::rep benchmarkWarmupTime = 100;
+
+        Verbosity verbosity = Verbosity::Normal;
+        WarnAbout::What warnings = WarnAbout::Nothing;
+        ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter;
+        double minDuration = -1;
+        RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder;
+        UseColour::YesOrNo useColour = UseColour::Auto;
+        WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
+
+        std::string outputFilename;
+        std::string name;
+        std::string processName;
+#ifndef CATCH_CONFIG_DEFAULT_REPORTER
+#define CATCH_CONFIG_DEFAULT_REPORTER "console"
+#endif
+        std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER;
+#undef CATCH_CONFIG_DEFAULT_REPORTER
+
+        std::vector<std::string> testsOrTags;
+        std::vector<std::string> sectionsToRun;
+    };
+
+    class Config : public IConfig {
+    public:
+
+        Config() = default;
+        Config( ConfigData const& data );
+        virtual ~Config() = default;
+
+        std::string const& getFilename() const;
+
+        bool listTests() const;
+        bool listTestNamesOnly() const;
+        bool listTags() const;
+        bool listReporters() const;
+
+        std::string getProcessName() const;
+        std::string const& getReporterName() const;
+
+        std::vector<std::string> const& getTestsOrTags() const override;
+        std::vector<std::string> const& getSectionsToRun() const override;
+
+        TestSpec const& testSpec() const override;
+        bool hasTestFilters() const override;
+
+        bool showHelp() const;
+
+        // IConfig interface
+        bool allowThrows() const override;
+        std::ostream& stream() const override;
+        std::string name() const override;
+        bool includeSuccessfulResults() const override;
+        bool warnAboutMissingAssertions() const override;
+        bool warnAboutNoTests() const override;
+        ShowDurations::OrNot showDurations() const override;
+        double minDuration() const override;
+        RunTests::InWhatOrder runOrder() const override;
+        unsigned int rngSeed() const override;
+        UseColour::YesOrNo useColour() const override;
+        bool shouldDebugBreak() const override;
+        int abortAfter() const override;
+        bool showInvisibles() const override;
+        Verbosity verbosity() const override;
+        bool benchmarkNoAnalysis() const override;
+        int benchmarkSamples() const override;
+        double benchmarkConfidenceInterval() const override;
+        unsigned int benchmarkResamples() const override;
+        std::chrono::milliseconds benchmarkWarmupTime() const override;
+
+    private:
+
+        IStream const* openStream();
+        ConfigData m_data;
+
+        std::unique_ptr<IStream const> m_stream;
+        TestSpec m_testSpec;
+        bool m_hasTestFilters = false;
+    };
+
+} // end namespace Catch
+
+// end catch_config.hpp
+// start catch_assertionresult.h
+
+#include <string>
+
+namespace Catch {
+
+    struct AssertionResultData
+    {
+        AssertionResultData() = delete;
+
+        AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression );
+
+        std::string message;
+        mutable std::string reconstructedExpression;
+        LazyExpression lazyExpression;
+        ResultWas::OfType resultType;
+
+        std::string reconstructExpression() const;
+    };
+
+    class AssertionResult {
+    public:
+        AssertionResult() = delete;
+        AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+
+        bool isOk() const;
+        bool succeeded() const;
+        ResultWas::OfType getResultType() const;
+        bool hasExpression() const;
+        bool hasMessage() const;
+        std::string getExpression() const;
+        std::string getExpressionInMacro() const;
+        bool hasExpandedExpression() const;
+        std::string getExpandedExpression() const;
+        std::string getMessage() const;
+        SourceLineInfo getSourceInfo() const;
+        StringRef getTestMacroName() const;
+
+    //protected:
+        AssertionInfo m_info;
+        AssertionResultData m_resultData;
+    };
+
+} // end namespace Catch
+
+// end catch_assertionresult.h
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+// start catch_estimate.hpp
+
+ // Statistics estimates
+
+
+namespace Catch {
+    namespace Benchmark {
+        template <typename Duration>
+        struct Estimate {
+            Duration point;
+            Duration lower_bound;
+            Duration upper_bound;
+            double confidence_interval;
+
+            template <typename Duration2>
+            operator Estimate<Duration2>() const {
+                return { point, lower_bound, upper_bound, confidence_interval };
+            }
+        };
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_estimate.hpp
+// start catch_outlier_classification.hpp
+
+// Outlier information
+
+namespace Catch {
+    namespace Benchmark {
+        struct OutlierClassification {
+            int samples_seen = 0;
+            int low_severe = 0;     // more than 3 times IQR below Q1
+            int low_mild = 0;       // 1.5 to 3 times IQR below Q1
+            int high_mild = 0;      // 1.5 to 3 times IQR above Q3
+            int high_severe = 0;    // more than 3 times IQR above Q3
+
+            int total() const {
+                return low_severe + low_mild + high_mild + high_severe;
+            }
+        };
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_outlier_classification.hpp
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+#include <string>
+#include <iosfwd>
+#include <map>
+#include <set>
+#include <memory>
+#include <algorithm>
+
+namespace Catch {
+
+    struct ReporterConfig {
+        explicit ReporterConfig( IConfigPtr const& _fullConfig );
+
+        ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream );
+
+        std::ostream& stream() const;
+        IConfigPtr fullConfig() const;
+
+    private:
+        std::ostream* m_stream;
+        IConfigPtr m_fullConfig;
+    };
+
+    struct ReporterPreferences {
+        bool shouldRedirectStdOut = false;
+        bool shouldReportAllAssertions = false;
+    };
+
+    template<typename T>
+    struct LazyStat : Option<T> {
+        LazyStat& operator=( T const& _value ) {
+            Option<T>::operator=( _value );
+            used = false;
+            return *this;
+        }
+        void reset() {
+            Option<T>::reset();
+            used = false;
+        }
+        bool used = false;
+    };
+
+    struct TestRunInfo {
+        TestRunInfo( std::string const& _name );
+        std::string name;
+    };
+    struct GroupInfo {
+        GroupInfo(  std::string const& _name,
+                    std::size_t _groupIndex,
+                    std::size_t _groupsCount );
+
+        std::string name;
+        std::size_t groupIndex;
+        std::size_t groupsCounts;
+    };
+
+    struct AssertionStats {
+        AssertionStats( AssertionResult const& _assertionResult,
+                        std::vector<MessageInfo> const& _infoMessages,
+                        Totals const& _totals );
+
+        AssertionStats( AssertionStats const& )              = default;
+        AssertionStats( AssertionStats && )                  = default;
+        AssertionStats& operator = ( AssertionStats const& ) = delete;
+        AssertionStats& operator = ( AssertionStats && )     = delete;
+        virtual ~AssertionStats();
+
+        AssertionResult assertionResult;
+        std::vector<MessageInfo> infoMessages;
+        Totals totals;
+    };
+
+    struct SectionStats {
+        SectionStats(   SectionInfo const& _sectionInfo,
+                        Counts const& _assertions,
+                        double _durationInSeconds,
+                        bool _missingAssertions );
+        SectionStats( SectionStats const& )              = default;
+        SectionStats( SectionStats && )                  = default;
+        SectionStats& operator = ( SectionStats const& ) = default;
+        SectionStats& operator = ( SectionStats && )     = default;
+        virtual ~SectionStats();
+
+        SectionInfo sectionInfo;
+        Counts assertions;
+        double durationInSeconds;
+        bool missingAssertions;
+    };
+
+    struct TestCaseStats {
+        TestCaseStats(  TestCaseInfo const& _testInfo,
+                        Totals const& _totals,
+                        std::string const& _stdOut,
+                        std::string const& _stdErr,
+                        bool _aborting );
+
+        TestCaseStats( TestCaseStats const& )              = default;
+        TestCaseStats( TestCaseStats && )                  = default;
+        TestCaseStats& operator = ( TestCaseStats const& ) = default;
+        TestCaseStats& operator = ( TestCaseStats && )     = default;
+        virtual ~TestCaseStats();
+
+        TestCaseInfo testInfo;
+        Totals totals;
+        std::string stdOut;
+        std::string stdErr;
+        bool aborting;
+    };
+
+    struct TestGroupStats {
+        TestGroupStats( GroupInfo const& _groupInfo,
+                        Totals const& _totals,
+                        bool _aborting );
+        TestGroupStats( GroupInfo const& _groupInfo );
+
+        TestGroupStats( TestGroupStats const& )              = default;
+        TestGroupStats( TestGroupStats && )                  = default;
+        TestGroupStats& operator = ( TestGroupStats const& ) = default;
+        TestGroupStats& operator = ( TestGroupStats && )     = default;
+        virtual ~TestGroupStats();
+
+        GroupInfo groupInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+    struct TestRunStats {
+        TestRunStats(   TestRunInfo const& _runInfo,
+                        Totals const& _totals,
+                        bool _aborting );
+
+        TestRunStats( TestRunStats const& )              = default;
+        TestRunStats( TestRunStats && )                  = default;
+        TestRunStats& operator = ( TestRunStats const& ) = default;
+        TestRunStats& operator = ( TestRunStats && )     = default;
+        virtual ~TestRunStats();
+
+        TestRunInfo runInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+    struct BenchmarkInfo {
+        std::string name;
+        double estimatedDuration;
+        int iterations;
+        int samples;
+        unsigned int resamples;
+        double clockResolution;
+        double clockCost;
+    };
+
+    template <class Duration>
+    struct BenchmarkStats {
+        BenchmarkInfo info;
+
+        std::vector<Duration> samples;
+        Benchmark::Estimate<Duration> mean;
+        Benchmark::Estimate<Duration> standardDeviation;
+        Benchmark::OutlierClassification outliers;
+        double outlierVariance;
+
+        template <typename Duration2>
+        operator BenchmarkStats<Duration2>() const {
+            std::vector<Duration2> samples2;
+            samples2.reserve(samples.size());
+            std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
+            return {
+                info,
+                std::move(samples2),
+                mean,
+                standardDeviation,
+                outliers,
+                outlierVariance,
+            };
+        }
+    };
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+    struct IStreamingReporter {
+        virtual ~IStreamingReporter() = default;
+
+        // Implementing class must also provide the following static methods:
+        // static std::string getDescription();
+        // static std::set<Verbosity> getSupportedVerbosities()
+
+        virtual ReporterPreferences getPreferences() const = 0;
+
+        virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+        virtual void reportInvalidArguments(std::string const&) {}
+
+        virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+        virtual void benchmarkPreparing( std::string const& ) {}
+        virtual void benchmarkStarting( BenchmarkInfo const& ) {}
+        virtual void benchmarkEnded( BenchmarkStats<> const& ) {}
+        virtual void benchmarkFailed( std::string const& ) {}
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+        virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+        // The return value indicates if the messages buffer should be cleared:
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+
+        virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+        virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+
+        virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+
+        // Default empty implementation provided
+        virtual void fatalErrorEncountered( StringRef name );
+
+        virtual bool isMulti() const;
+    };
+    using IStreamingReporterPtr = std::unique_ptr<IStreamingReporter>;
+
+    struct IReporterFactory {
+        virtual ~IReporterFactory();
+        virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0;
+        virtual std::string getDescription() const = 0;
+    };
+    using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
+
+    struct IReporterRegistry {
+        using FactoryMap = std::map<std::string, IReporterFactoryPtr>;
+        using Listeners = std::vector<IReporterFactoryPtr>;
+
+        virtual ~IReporterRegistry();
+        virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0;
+        virtual FactoryMap const& getFactories() const = 0;
+        virtual Listeners const& getListeners() const = 0;
+    };
+
+} // end namespace Catch
+
+// end catch_interfaces_reporter.h
+#include <algorithm>
+#include <cstring>
+#include <cfloat>
+#include <cstdio>
+#include <cassert>
+#include <memory>
+#include <ostream>
+
+namespace Catch {
+    void prepareExpandedExpression(AssertionResult& result);
+
+    // Returns double formatted as %.3f (format expected on output)
+    std::string getFormattedDuration( double duration );
+
+    //! Should the reporter show
+    bool shouldShowDuration( IConfig const& config, double duration );
+
+    std::string serializeFilters( std::vector<std::string> const& container );
+
+    template<typename DerivedT>
+    struct StreamingReporterBase : IStreamingReporter {
+
+        StreamingReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+            if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) )
+                CATCH_ERROR( "Verbosity level not supported by this reporter" );
+        }
+
+        ReporterPreferences getPreferences() const override {
+            return m_reporterPrefs;
+        }
+
+        static std::set<Verbosity> getSupportedVerbosities() {
+            return { Verbosity::Normal };
+        }
+
+        ~StreamingReporterBase() override = default;
+
+        void noMatchingTestCases(std::string const&) override {}
+
+        void reportInvalidArguments(std::string const&) override {}
+
+        void testRunStarting(TestRunInfo const& _testRunInfo) override {
+            currentTestRunInfo = _testRunInfo;
+        }
+
+        void testGroupStarting(GroupInfo const& _groupInfo) override {
+            currentGroupInfo = _groupInfo;
+        }
+
+        void testCaseStarting(TestCaseInfo const& _testInfo) override  {
+            currentTestCaseInfo = _testInfo;
+        }
+        void sectionStarting(SectionInfo const& _sectionInfo) override {
+            m_sectionStack.push_back(_sectionInfo);
+        }
+
+        void sectionEnded(SectionStats const& /* _sectionStats */) override {
+            m_sectionStack.pop_back();
+        }
+        void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override {
+            currentTestCaseInfo.reset();
+        }
+        void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override {
+            currentGroupInfo.reset();
+        }
+        void testRunEnded(TestRunStats const& /* _testRunStats */) override {
+            currentTestCaseInfo.reset();
+            currentGroupInfo.reset();
+            currentTestRunInfo.reset();
+        }
+
+        void skipTest(TestCaseInfo const&) override {
+            // Don't do anything with this by default.
+            // It can optionally be overridden in the derived class.
+        }
+
+        IConfigPtr m_config;
+        std::ostream& stream;
+
+        LazyStat<TestRunInfo> currentTestRunInfo;
+        LazyStat<GroupInfo> currentGroupInfo;
+        LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+        std::vector<SectionInfo> m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
+    };
+
+    template<typename DerivedT>
+    struct CumulativeReporterBase : IStreamingReporter {
+        template<typename T, typename ChildNodeT>
+        struct Node {
+            explicit Node( T const& _value ) : value( _value ) {}
+            virtual ~Node() {}
+
+            using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>;
+            T value;
+            ChildNodes children;
+        };
+        struct SectionNode {
+            explicit SectionNode(SectionStats const& _stats) : stats(_stats) {}
+            virtual ~SectionNode() = default;
+
+            bool operator == (SectionNode const& other) const {
+                return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+            }
+            bool operator == (std::shared_ptr<SectionNode> const& other) const {
+                return operator==(*other);
+            }
+
+            SectionStats stats;
+            using ChildSections = std::vector<std::shared_ptr<SectionNode>>;
+            using Assertions = std::vector<AssertionStats>;
+            ChildSections childSections;
+            Assertions assertions;
+            std::string stdOut;
+            std::string stdErr;
+        };
+
+        struct BySectionInfo {
+            BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+            BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+            bool operator() (std::shared_ptr<SectionNode> const& node) const {
+                return ((node->stats.sectionInfo.name == m_other.name) &&
+                        (node->stats.sectionInfo.lineInfo == m_other.lineInfo));
+            }
+            void operator=(BySectionInfo const&) = delete;
+
+        private:
+            SectionInfo const& m_other;
+        };
+
+        using TestCaseNode = Node<TestCaseStats, SectionNode>;
+        using TestGroupNode = Node<TestGroupStats, TestCaseNode>;
+        using TestRunNode = Node<TestRunStats, TestGroupNode>;
+
+        CumulativeReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+            if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) )
+                CATCH_ERROR( "Verbosity level not supported by this reporter" );
+        }
+        ~CumulativeReporterBase() override = default;
+
+        ReporterPreferences getPreferences() const override {
+            return m_reporterPrefs;
+        }
+
+        static std::set<Verbosity> getSupportedVerbosities() {
+            return { Verbosity::Normal };
+        }
+
+        void testRunStarting( TestRunInfo const& ) override {}
+        void testGroupStarting( GroupInfo const& ) override {}
+
+        void testCaseStarting( TestCaseInfo const& ) override {}
+
+        void sectionStarting( SectionInfo const& sectionInfo ) override {
+            SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+            std::shared_ptr<SectionNode> node;
+            if( m_sectionStack.empty() ) {
+                if( !m_rootSection )
+                    m_rootSection = std::make_shared<SectionNode>( incompleteStats );
+                node = m_rootSection;
+            }
+            else {
+                SectionNode& parentNode = *m_sectionStack.back();
+                auto it =
+                    std::find_if(   parentNode.childSections.begin(),
+                                    parentNode.childSections.end(),
+                                    BySectionInfo( sectionInfo ) );
+                if( it == parentNode.childSections.end() ) {
+                    node = std::make_shared<SectionNode>( incompleteStats );
+                    parentNode.childSections.push_back( node );
+                }
+                else
+                    node = *it;
+            }
+            m_sectionStack.push_back( node );
+            m_deepestSection = std::move(node);
+        }
+
+        void assertionStarting(AssertionInfo const&) override {}
+
+        bool assertionEnded(AssertionStats const& assertionStats) override {
+            assert(!m_sectionStack.empty());
+            // AssertionResult holds a pointer to a temporary DecomposedExpression,
+            // which getExpandedExpression() calls to build the expression string.
+            // Our section stack copy of the assertionResult will likely outlive the
+            // temporary, so it must be expanded or discarded now to avoid calling
+            // a destroyed object later.
+            prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) );
+            SectionNode& sectionNode = *m_sectionStack.back();
+            sectionNode.assertions.push_back(assertionStats);
+            return true;
+        }
+        void sectionEnded(SectionStats const& sectionStats) override {
+            assert(!m_sectionStack.empty());
+            SectionNode& node = *m_sectionStack.back();
+            node.stats = sectionStats;
+            m_sectionStack.pop_back();
+        }
+        void testCaseEnded(TestCaseStats const& testCaseStats) override {
+            auto node = std::make_shared<TestCaseNode>(testCaseStats);
+            assert(m_sectionStack.size() == 0);
+            node->children.push_back(m_rootSection);
+            m_testCases.push_back(node);
+            m_rootSection.reset();
+
+            assert(m_deepestSection);
+            m_deepestSection->stdOut = testCaseStats.stdOut;
+            m_deepestSection->stdErr = testCaseStats.stdErr;
+        }
+        void testGroupEnded(TestGroupStats const& testGroupStats) override {
+            auto node = std::make_shared<TestGroupNode>(testGroupStats);
+            node->children.swap(m_testCases);
+            m_testGroups.push_back(node);
+        }
+        void testRunEnded(TestRunStats const& testRunStats) override {
+            auto node = std::make_shared<TestRunNode>(testRunStats);
+            node->children.swap(m_testGroups);
+            m_testRuns.push_back(node);
+            testRunEndedCumulative();
+        }
+        virtual void testRunEndedCumulative() = 0;
+
+        void skipTest(TestCaseInfo const&) override {}
+
+        IConfigPtr m_config;
+        std::ostream& stream;
+        std::vector<AssertionStats> m_assertions;
+        std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections;
+        std::vector<std::shared_ptr<TestCaseNode>> m_testCases;
+        std::vector<std::shared_ptr<TestGroupNode>> m_testGroups;
+
+        std::vector<std::shared_ptr<TestRunNode>> m_testRuns;
+
+        std::shared_ptr<SectionNode> m_rootSection;
+        std::shared_ptr<SectionNode> m_deepestSection;
+        std::vector<std::shared_ptr<SectionNode>> m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
+    };
+
+    template<char C>
+    char const* getLineOfChars() {
+        static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+        if( !*line ) {
+            std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+            line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+        }
+        return line;
+    }
+
+    struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> {
+        TestEventListenerBase( ReporterConfig const& _config );
+
+        static std::set<Verbosity> getSupportedVerbosities();
+
+        void assertionStarting(AssertionInfo const&) override;
+        bool assertionEnded(AssertionStats const&) override;
+    };
+
+} // end namespace Catch
+
+// end catch_reporter_bases.hpp
+// start catch_console_colour.h
+
+namespace Catch {
+
+    struct Colour {
+        enum Code {
+            None = 0,
+
+            White,
+            Red,
+            Green,
+            Blue,
+            Cyan,
+            Yellow,
+            Grey,
+
+            Bright = 0x10,
+
+            BrightRed = Bright | Red,
+            BrightGreen = Bright | Green,
+            LightGrey = Bright | Grey,
+            BrightWhite = Bright | White,
+            BrightYellow = Bright | Yellow,
+
+            // By intention
+            FileName = LightGrey,
+            Warning = BrightYellow,
+            ResultError = BrightRed,
+            ResultSuccess = BrightGreen,
+            ResultExpectedFailure = Warning,
+
+            Error = BrightRed,
+            Success = Green,
+
+            OriginalExpression = Cyan,
+            ReconstructedExpression = BrightYellow,
+
+            SecondaryText = LightGrey,
+            Headers = White
+        };
+
+        // Use constructed object for RAII guard
+        Colour( Code _colourCode );
+        Colour( Colour&& other ) noexcept;
+        Colour& operator=( Colour&& other ) noexcept;
+        ~Colour();
+
+        // Use static method for one-shot changes
+        static void use( Code _colourCode );
+
+    private:
+        bool m_moved = false;
+    };
+
+    std::ostream& operator << ( std::ostream& os, Colour const& );
+
+} // end namespace Catch
+
+// end catch_console_colour.h
+// start catch_reporter_registrars.hpp
+
+
+namespace Catch {
+
+    template<typename T>
+    class ReporterRegistrar {
+
+        class ReporterFactory : public IReporterFactory {
+
+            IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+                return std::unique_ptr<T>( new T( config ) );
+            }
+
+            std::string getDescription() const override {
+                return T::getDescription();
+            }
+        };
+
+    public:
+
+        explicit ReporterRegistrar( std::string const& name ) {
+            getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() );
+        }
+    };
+
+    template<typename T>
+    class ListenerRegistrar {
+
+        class ListenerFactory : public IReporterFactory {
+
+            IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+                return std::unique_ptr<T>( new T( config ) );
+            }
+            std::string getDescription() const override {
+                return std::string();
+            }
+        };
+
+    public:
+
+        ListenerRegistrar() {
+            getMutableRegistryHub().registerListener( std::make_shared<ListenerFactory>() );
+        }
+    };
+}
+
+#if !defined(CATCH_CONFIG_DISABLE)
+
+#define CATCH_REGISTER_REPORTER( name, reporterType ) \
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION         \
+    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS          \
+    namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#define CATCH_REGISTER_LISTENER( listenerType ) \
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION   \
+    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS    \
+    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+#else // CATCH_CONFIG_DISABLE
+
+#define CATCH_REGISTER_REPORTER(name, reporterType)
+#define CATCH_REGISTER_LISTENER(listenerType)
+
+#endif // CATCH_CONFIG_DISABLE
+
+// end catch_reporter_registrars.hpp
+// Allow users to base their work off existing reporters
+// start catch_reporter_compact.h
+
+namespace Catch {
+
+    struct CompactReporter : StreamingReporterBase<CompactReporter> {
+
+        using StreamingReporterBase::StreamingReporterBase;
+
+        ~CompactReporter() override;
+
+        static std::string getDescription();
+
+        void noMatchingTestCases(std::string const& spec) override;
+
+        void assertionStarting(AssertionInfo const&) override;
+
+        bool assertionEnded(AssertionStats const& _assertionStats) override;
+
+        void sectionEnded(SectionStats const& _sectionStats) override;
+
+        void testRunEnded(TestRunStats const& _testRunStats) override;
+
+    };
+
+} // end namespace Catch
+
+// end catch_reporter_compact.h
+// start catch_reporter_console.h
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+                              // Note that 4062 (not all labels are handled
+                              // and default is missing) is enabled
+#endif
+
+namespace Catch {
+    // Fwd decls
+    struct SummaryColumn;
+    class TablePrinter;
+
+    struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> {
+        std::unique_ptr<TablePrinter> m_tablePrinter;
+
+        ConsoleReporter(ReporterConfig const& config);
+        ~ConsoleReporter() override;
+        static std::string getDescription();
+
+        void noMatchingTestCases(std::string const& spec) override;
+
+        void reportInvalidArguments(std::string const&arg) override;
+
+        void assertionStarting(AssertionInfo const&) override;
+
+        bool assertionEnded(AssertionStats const& _assertionStats) override;
+
+        void sectionStarting(SectionInfo const& _sectionInfo) override;
+        void sectionEnded(SectionStats const& _sectionStats) override;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+        void benchmarkPreparing(std::string const& name) override;
+        void benchmarkStarting(BenchmarkInfo const& info) override;
+        void benchmarkEnded(BenchmarkStats<> const& stats) override;
+        void benchmarkFailed(std::string const& error) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+        void testCaseEnded(TestCaseStats const& _testCaseStats) override;
+        void testGroupEnded(TestGroupStats const& _testGroupStats) override;
+        void testRunEnded(TestRunStats const& _testRunStats) override;
+        void testRunStarting(TestRunInfo const& _testRunInfo) override;
+    private:
+
+        void lazyPrint();
+
+        void lazyPrintWithoutClosingBenchmarkTable();
+        void lazyPrintRunInfo();
+        void lazyPrintGroupInfo();
+        void printTestCaseAndSectionHeader();
+
+        void printClosedHeader(std::string const& _name);
+        void printOpenHeader(std::string const& _name);
+
+        // if string has a : in first line will set indent to follow it on
+        // subsequent lines
+        void printHeaderString(std::string const& _string, std::size_t indent = 0);
+
+        void printTotals(Totals const& totals);
+        void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row);
+
+        void printTotalsDivider(Totals const& totals);
+        void printSummaryDivider();
+        void printTestFilters();
+
+    private:
+        bool m_headerPrinted = false;
+    };
+
+} // end namespace Catch
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+// end catch_reporter_console.h
+// start catch_reporter_junit.h
+
+// start catch_xmlwriter.h
+
+#include <vector>
+
+namespace Catch {
+    enum class XmlFormatting {
+        None = 0x00,
+        Indent = 0x01,
+        Newline = 0x02,
+    };
+
+    XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
+    XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
+
+    class XmlEncode {
+    public:
+        enum ForWhat { ForTextNodes, ForAttributes };
+
+        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes );
+
+        void encodeTo( std::ostream& os ) const;
+
+        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
+
+    private:
+        std::string m_str;
+        ForWhat m_forWhat;
+    };
+
+    class XmlWriter {
+    public:
+
+        class ScopedElement {
+        public:
+            ScopedElement( XmlWriter* writer, XmlFormatting fmt );
+
+            ScopedElement( ScopedElement&& other ) noexcept;
+            ScopedElement& operator=( ScopedElement&& other ) noexcept;
+
+            ~ScopedElement();
+
+            ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent );
+
+            template<typename T>
+            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+                m_writer->writeAttribute( name, attribute );
+                return *this;
+            }
+
+        private:
+            mutable XmlWriter* m_writer = nullptr;
+            XmlFormatting m_fmt;
+        };
+
+        XmlWriter( std::ostream& os = Catch::cout() );
+        ~XmlWriter();
+
+        XmlWriter( XmlWriter const& ) = delete;
+        XmlWriter& operator=( XmlWriter const& ) = delete;
+
+        XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+        ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+        XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
+
+        XmlWriter& writeAttribute( std::string const& name, bool attribute );
+
+        template<typename T>
+        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+            ReusableStringStream rss;
+            rss << attribute;
+            return writeAttribute( name, rss.str() );
+        }
+
+        XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+        XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+        void writeStylesheetRef( std::string const& url );
+
+        XmlWriter& writeBlankLine();
+
+        void ensureTagClosed();
+
+    private:
+
+        void applyFormatting(XmlFormatting fmt);
+
+        void writeDeclaration();
+
+        void newlineIfNecessary();
+
+        bool m_tagIsOpen = false;
+        bool m_needsNewline = false;
+        std::vector<std::string> m_tags;
+        std::string m_indent;
+        std::ostream& m_os;
+    };
+
+}
+
+// end catch_xmlwriter.h
+namespace Catch {
+
+    class JunitReporter : public CumulativeReporterBase<JunitReporter> {
+    public:
+        JunitReporter(ReporterConfig const& _config);
+
+        ~JunitReporter() override;
+
+        static std::string getDescription();
+
+        void noMatchingTestCases(std::string const& /*spec*/) override;
+
+        void testRunStarting(TestRunInfo const& runInfo) override;
+
+        void testGroupStarting(GroupInfo const& groupInfo) override;
+
+        void testCaseStarting(TestCaseInfo const& testCaseInfo) override;
+        bool assertionEnded(AssertionStats const& assertionStats) override;
+
+        void testCaseEnded(TestCaseStats const& testCaseStats) override;
+
+        void testGroupEnded(TestGroupStats const& testGroupStats) override;
+
+        void testRunEndedCumulative() override;
+
+        void writeGroup(TestGroupNode const& groupNode, double suiteTime);
+
+        void writeTestCase(TestCaseNode const& testCaseNode);
+
+        void writeSection(std::string const& className,
+                          std::string const& rootName,
+                          SectionNode const& sectionNode);
+
+        void writeAssertions(SectionNode const& sectionNode);
+        void writeAssertion(AssertionStats const& stats);
+
+        XmlWriter xml;
+        Timer suiteTimer;
+        std::string stdOutForSuite;
+        std::string stdErrForSuite;
+        unsigned int unexpectedExceptions = 0;
+        bool m_okToFail = false;
+    };
+
+} // end namespace Catch
+
+// end catch_reporter_junit.h
+// start catch_reporter_xml.h
+
+namespace Catch {
+    class XmlReporter : public StreamingReporterBase<XmlReporter> {
+    public:
+        XmlReporter(ReporterConfig const& _config);
+
+        ~XmlReporter() override;
+
+        static std::string getDescription();
+
+        virtual std::string getStylesheetRef() const;
+
+        void writeSourceInfo(SourceLineInfo const& sourceInfo);
+
+    public: // StreamingReporterBase
+
+        void noMatchingTestCases(std::string const& s) override;
+
+        void testRunStarting(TestRunInfo const& testInfo) override;
+
+        void testGroupStarting(GroupInfo const& groupInfo) override;
+
+        void testCaseStarting(TestCaseInfo const& testInfo) override;
+
+        void sectionStarting(SectionInfo const& sectionInfo) override;
+
+        void assertionStarting(AssertionInfo const&) override;
+
+        bool assertionEnded(AssertionStats const& assertionStats) override;
+
+        void sectionEnded(SectionStats const& sectionStats) override;
+
+        void testCaseEnded(TestCaseStats const& testCaseStats) override;
+
+        void testGroupEnded(TestGroupStats const& testGroupStats) override;
+
+        void testRunEnded(TestRunStats const& testRunStats) override;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+        void benchmarkPreparing(std::string const& name) override;
+        void benchmarkStarting(BenchmarkInfo const&) override;
+        void benchmarkEnded(BenchmarkStats<> const&) override;
+        void benchmarkFailed(std::string const&) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+    private:
+        Timer m_testCaseTimer;
+        XmlWriter m_xml;
+        int m_sectionDepth = 0;
+    };
+
+} // end namespace Catch
+
+// end catch_reporter_xml.h
+
+// end catch_external_interfaces.h
+#endif
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+// start catch_benchmarking_all.hpp
+
+// A proxy header that includes all of the benchmarking headers to allow
+// concise include of the benchmarking features. You should prefer the
+// individual includes in standard use.
+
+// start catch_benchmark.hpp
+
+ // Benchmark
+
+// start catch_chronometer.hpp
+
+// User-facing chronometer
+
+
+// start catch_clock.hpp
+
+// Clocks
+
+
+#include <chrono>
+#include <ratio>
+
+namespace Catch {
+    namespace Benchmark {
+        template <typename Clock>
+        using ClockDuration = typename Clock::duration;
+        template <typename Clock>
+        using FloatDuration = std::chrono::duration<double, typename Clock::period>;
+
+        template <typename Clock>
+        using TimePoint = typename Clock::time_point;
+
+        using default_clock = std::chrono::steady_clock;
+
+        template <typename Clock>
+        struct now {
+            TimePoint<Clock> operator()() const {
+                return Clock::now();
+            }
+        };
+
+        using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_clock.hpp
+// start catch_optimizer.hpp
+
+ // Hinting the optimizer
+
+
+#if defined(_MSC_VER)
+#   include <atomic> // atomic_thread_fence
+#endif
+
+namespace Catch {
+    namespace Benchmark {
+#if defined(__GNUC__) || defined(__clang__)
+        template <typename T>
+        inline void keep_memory(T* p) {
+            asm volatile("" : : "g"(p) : "memory");
+        }
+        inline void keep_memory() {
+            asm volatile("" : : : "memory");
+        }
+
+        namespace Detail {
+            inline void optimizer_barrier() { keep_memory(); }
+        } // namespace Detail
+#elif defined(_MSC_VER)
+
+#pragma optimize("", off)
+        template <typename T>
+        inline void keep_memory(T* p) {
+            // thanks @milleniumbug
+            *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
+        }
+        // TODO equivalent keep_memory()
+#pragma optimize("", on)
+
+        namespace Detail {
+            inline void optimizer_barrier() {
+                std::atomic_thread_fence(std::memory_order_seq_cst);
+            }
+        } // namespace Detail
+
+#endif
+
+        template <typename T>
+        inline void deoptimize_value(T&& x) {
+            keep_memory(&x);
+        }
+
+        template <typename Fn, typename... Args>
+        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type {
+            deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...)));
+        }
+
+        template <typename Fn, typename... Args>
+        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<std::is_same<void, decltype(fn(args...))>::value>::type {
+            std::forward<Fn>(fn) (std::forward<Args...>(args...));
+        }
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_optimizer.hpp
+// start catch_complete_invoke.hpp
+
+// Invoke with a special case for void
+
+
+#include <type_traits>
+#include <utility>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename T>
+            struct CompleteType { using type = T; };
+            template <>
+            struct CompleteType<void> { struct type {}; };
+
+            template <typename T>
+            using CompleteType_t = typename CompleteType<T>::type;
+
+            template <typename Result>
+            struct CompleteInvoker {
+                template <typename Fun, typename... Args>
+                static Result invoke(Fun&& fun, Args&&... args) {
+                    return std::forward<Fun>(fun)(std::forward<Args>(args)...);
+                }
+            };
+            template <>
+            struct CompleteInvoker<void> {
+                template <typename Fun, typename... Args>
+                static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) {
+                    std::forward<Fun>(fun)(std::forward<Args>(args)...);
+                    return {};
+                }
+            };
+
+            // invoke and not return void :(
+            template <typename Fun, typename... Args>
+            CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) {
+                return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...);
+            }
+
+            const std::string benchmarkErrorMsg = "a benchmark failed to run successfully";
+        } // namespace Detail
+
+        template <typename Fun>
+        Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) {
+            CATCH_TRY{
+                return Detail::complete_invoke(std::forward<Fun>(fun));
+            } CATCH_CATCH_ALL{
+                getResultCapture().benchmarkFailed(translateActiveException());
+                CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg);
+            }
+        }
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_complete_invoke.hpp
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            struct ChronometerConcept {
+                virtual void start() = 0;
+                virtual void finish() = 0;
+                virtual ~ChronometerConcept() = default;
+            };
+            template <typename Clock>
+            struct ChronometerModel final : public ChronometerConcept {
+                void start() override { started = Clock::now(); }
+                void finish() override { finished = Clock::now(); }
+
+                ClockDuration<Clock> elapsed() const { return finished - started; }
+
+                TimePoint<Clock> started;
+                TimePoint<Clock> finished;
+            };
+        } // namespace Detail
+
+        struct Chronometer {
+        public:
+            template <typename Fun>
+            void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); }
+
+            int runs() const { return k; }
+
+            Chronometer(Detail::ChronometerConcept& meter, int k)
+                : impl(&meter)
+                , k(k) {}
+
+        private:
+            template <typename Fun>
+            void measure(Fun&& fun, std::false_type) {
+                measure([&fun](int) { return fun(); }, std::true_type());
+            }
+
+            template <typename Fun>
+            void measure(Fun&& fun, std::true_type) {
+                Detail::optimizer_barrier();
+                impl->start();
+                for (int i = 0; i < k; ++i) invoke_deoptimized(fun, i);
+                impl->finish();
+                Detail::optimizer_barrier();
+            }
+
+            Detail::ChronometerConcept* impl;
+            int k;
+        };
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_chronometer.hpp
+// start catch_environment.hpp
+
+// Environment information
+
+
+namespace Catch {
+    namespace Benchmark {
+        template <typename Duration>
+        struct EnvironmentEstimate {
+            Duration mean;
+            OutlierClassification outliers;
+
+            template <typename Duration2>
+            operator EnvironmentEstimate<Duration2>() const {
+                return { mean, outliers };
+            }
+        };
+        template <typename Clock>
+        struct Environment {
+            using clock_type = Clock;
+            EnvironmentEstimate<FloatDuration<Clock>> clock_resolution;
+            EnvironmentEstimate<FloatDuration<Clock>> clock_cost;
+        };
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_environment.hpp
+// start catch_execution_plan.hpp
+
+ // Execution plan
+
+
+// start catch_benchmark_function.hpp
+
+ // Dumb std::function implementation for consistent call overhead
+
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+#include <memory>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename T>
+            using Decay = typename std::decay<T>::type;
+            template <typename T, typename U>
+            struct is_related
+                : std::is_same<Decay<T>, Decay<U>> {};
+
+            /// We need to reinvent std::function because every piece of code that might add overhead
+            /// in a measurement context needs to have consistent performance characteristics so that we
+            /// can account for it in the measurement.
+            /// Implementations of std::function with optimizations that aren't always applicable, like
+            /// small buffer optimizations, are not uncommon.
+            /// This is effectively an implementation of std::function without any such optimizations;
+            /// it may be slow, but it is consistently slow.
+            struct BenchmarkFunction {
+            private:
+                struct callable {
+                    virtual void call(Chronometer meter) const = 0;
+                    virtual callable* clone() const = 0;
+                    virtual ~callable() = default;
+                };
+                template <typename Fun>
+                struct model : public callable {
+                    model(Fun&& fun) : fun(std::move(fun)) {}
+                    model(Fun const& fun) : fun(fun) {}
+
+                    model<Fun>* clone() const override { return new model<Fun>(*this); }
+
+                    void call(Chronometer meter) const override {
+                        call(meter, is_callable<Fun(Chronometer)>());
+                    }
+                    void call(Chronometer meter, std::true_type) const {
+                        fun(meter);
+                    }
+                    void call(Chronometer meter, std::false_type) const {
+                        meter.measure(fun);
+                    }
+
+                    Fun fun;
+                };
+
+                struct do_nothing { void operator()() const {} };
+
+                template <typename T>
+                BenchmarkFunction(model<T>* c) : f(c) {}
+
+            public:
+                BenchmarkFunction()
+                    : f(new model<do_nothing>{ {} }) {}
+
+                template <typename Fun,
+                    typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0>
+                    BenchmarkFunction(Fun&& fun)
+                    : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {}
+
+                BenchmarkFunction(BenchmarkFunction&& that)
+                    : f(std::move(that.f)) {}
+
+                BenchmarkFunction(BenchmarkFunction const& that)
+                    : f(that.f->clone()) {}
+
+                BenchmarkFunction& operator=(BenchmarkFunction&& that) {
+                    f = std::move(that.f);
+                    return *this;
+                }
+
+                BenchmarkFunction& operator=(BenchmarkFunction const& that) {
+                    f.reset(that.f->clone());
+                    return *this;
+                }
+
+                void operator()(Chronometer meter) const { f->call(meter); }
+
+            private:
+                std::unique_ptr<callable> f;
+            };
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_benchmark_function.hpp
+// start catch_repeat.hpp
+
+// repeat algorithm
+
+
+#include <type_traits>
+#include <utility>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename Fun>
+            struct repeater {
+                void operator()(int k) const {
+                    for (int i = 0; i < k; ++i) {
+                        fun();
+                    }
+                }
+                Fun fun;
+            };
+            template <typename Fun>
+            repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) {
+                return { std::forward<Fun>(fun) };
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_repeat.hpp
+// start catch_run_for_at_least.hpp
+
+// Run a function for a minimum amount of time
+
+
+// start catch_measure.hpp
+
+// Measure
+
+
+// start catch_timing.hpp
+
+// Timing
+
+
+#include <tuple>
+#include <type_traits>
+
+namespace Catch {
+    namespace Benchmark {
+        template <typename Duration, typename Result>
+        struct Timing {
+            Duration elapsed;
+            Result result;
+            int iterations;
+        };
+        template <typename Clock, typename Func, typename... Args>
+        using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_timing.hpp
+#include <utility>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename Clock, typename Fun, typename... Args>
+            TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) {
+                auto start = Clock::now();
+                auto&& r = Detail::complete_invoke(fun, std::forward<Args>(args)...);
+                auto end = Clock::now();
+                auto delta = end - start;
+                return { delta, std::forward<decltype(r)>(r), 1 };
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_measure.hpp
+#include <utility>
+#include <type_traits>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename Clock, typename Fun>
+            TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) {
+                return Detail::measure<Clock>(fun, iters);
+            }
+            template <typename Clock, typename Fun>
+            TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) {
+                Detail::ChronometerModel<Clock> meter;
+                auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
+
+                return { meter.elapsed(), std::move(result), iters };
+            }
+
+            template <typename Clock, typename Fun>
+            using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type;
+
+            struct optimized_away_error : std::exception {
+                const char* what() const noexcept override {
+                    return "could not measure benchmark, maybe it was optimized away";
+                }
+            };
+
+            template <typename Clock, typename Fun>
+            TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>> run_for_at_least(ClockDuration<Clock> how_long, int seed, Fun&& fun) {
+                auto iters = seed;
+                while (iters < (1 << 30)) {
+                    auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>());
+
+                    if (Timing.elapsed >= how_long) {
+                        return { Timing.elapsed, std::move(Timing.result), iters };
+                    }
+                    iters *= 2;
+                }
+                throw optimized_away_error{};
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_run_for_at_least.hpp
+#include <algorithm>
+
+namespace Catch {
+    namespace Benchmark {
+        template <typename Duration>
+        struct ExecutionPlan {
+            int iterations_per_sample;
+            Duration estimated_duration;
+            Detail::BenchmarkFunction benchmark;
+            Duration warmup_time;
+            int warmup_iterations;
+
+            template <typename Duration2>
+            operator ExecutionPlan<Duration2>() const {
+                return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations };
+            }
+
+            template <typename Clock>
+            std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
+                // warmup a bit
+                Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{}));
+
+                std::vector<FloatDuration<Clock>> times;
+                times.reserve(cfg.benchmarkSamples());
+                std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] {
+                    Detail::ChronometerModel<Clock> model;
+                    this->benchmark(Chronometer(model, iterations_per_sample));
+                    auto sample_time = model.elapsed() - env.clock_cost.mean;
+                    if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero();
+                    return sample_time / iterations_per_sample;
+                });
+                return times;
+            }
+        };
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_execution_plan.hpp
+// start catch_estimate_clock.hpp
+
+ // Environment measurement
+
+
+// start catch_stats.hpp
+
+// Statistical analysis tools
+
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+#include <iterator>
+#include <numeric>
+#include <tuple>
+#include <cmath>
+#include <utility>
+#include <cstddef>
+#include <random>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            using sample = std::vector<double>;
+
+            double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
+
+            template <typename Iterator>
+            OutlierClassification classify_outliers(Iterator first, Iterator last) {
+                std::vector<double> copy(first, last);
+
+                auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end());
+                auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end());
+                auto iqr = q3 - q1;
+                auto los = q1 - (iqr * 3.);
+                auto lom = q1 - (iqr * 1.5);
+                auto him = q3 + (iqr * 1.5);
+                auto his = q3 + (iqr * 3.);
+
+                OutlierClassification o;
+                for (; first != last; ++first) {
+                    auto&& t = *first;
+                    if (t < los) ++o.low_severe;
+                    else if (t < lom) ++o.low_mild;
+                    else if (t > his) ++o.high_severe;
+                    else if (t > him) ++o.high_mild;
+                    ++o.samples_seen;
+                }
+                return o;
+            }
+
+            template <typename Iterator>
+            double mean(Iterator first, Iterator last) {
+                auto count = last - first;
+                double sum = std::accumulate(first, last, 0.);
+                return sum / count;
+            }
+
+            template <typename URng, typename Iterator, typename Estimator>
+            sample resample(URng& rng, int resamples, Iterator first, Iterator last, Estimator& estimator) {
+                auto n = last - first;
+                std::uniform_int_distribution<decltype(n)> dist(0, n - 1);
+
+                sample out;
+                out.reserve(resamples);
+                std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] {
+                    std::vector<double> resampled;
+                    resampled.reserve(n);
+                    std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; });
+                    return estimator(resampled.begin(), resampled.end());
+                });
+                std::sort(out.begin(), out.end());
+                return out;
+            }
+
+            template <typename Estimator, typename Iterator>
+            sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
+                auto n = last - first;
+                auto second = std::next(first);
+                sample results;
+                results.reserve(n);
+
+                for (auto it = first; it != last; ++it) {
+                    std::iter_swap(it, first);
+                    results.push_back(estimator(second, last));
+                }
+
+                return results;
+            }
+
+            inline double normal_cdf(double x) {
+                return std::erfc(-x / std::sqrt(2.0)) / 2.0;
+            }
+
+            double erfc_inv(double x);
+
+            double normal_quantile(double p);
+
+            template <typename Iterator, typename Estimator>
+            Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) {
+                auto n_samples = last - first;
+
+                double point = estimator(first, last);
+                // Degenerate case with a single sample
+                if (n_samples == 1) return { point, point, point, confidence_level };
+
+                sample jack = jackknife(estimator, first, last);
+                double jack_mean = mean(jack.begin(), jack.end());
+                double sum_squares, sum_cubes;
+                std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> {
+                    auto d = jack_mean - x;
+                    auto d2 = d * d;
+                    auto d3 = d2 * d;
+                    return { sqcb.first + d2, sqcb.second + d3 };
+                });
+
+                double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
+                int n = static_cast<int>(resample.size());
+                double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n;
+                // degenerate case with uniform samples
+                if (prob_n == 0) return { point, point, point, confidence_level };
+
+                double bias = normal_quantile(prob_n);
+                double z1 = normal_quantile((1. - confidence_level) / 2.);
+
+                auto cumn = [n](double x) -> int {
+                    return std::lround(normal_cdf(x) * n); };
+                auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
+                double b1 = bias + z1;
+                double b2 = bias - z1;
+                double a1 = a(b1);
+                double a2 = a(b2);
+                auto lo = (std::max)(cumn(a1), 0);
+                auto hi = (std::min)(cumn(a2), n - 1);
+
+                return { point, resample[lo], resample[hi], confidence_level };
+            }
+
+            double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n);
+
+            struct bootstrap_analysis {
+                Estimate<double> mean;
+                Estimate<double> standard_deviation;
+                double outlier_variance;
+            };
+
+            bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_stats.hpp
+#include <algorithm>
+#include <iterator>
+#include <tuple>
+#include <vector>
+#include <cmath>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename Clock>
+            std::vector<double> resolution(int k) {
+                std::vector<TimePoint<Clock>> times;
+                times.reserve(k + 1);
+                std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
+
+                std::vector<double> deltas;
+                deltas.reserve(k);
+                std::transform(std::next(times.begin()), times.end(), times.begin(),
+                    std::back_inserter(deltas),
+                    [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); });
+
+                return deltas;
+            }
+
+            const auto warmup_iterations = 10000;
+            const auto warmup_time = std::chrono::milliseconds(100);
+            const auto minimum_ticks = 1000;
+            const auto warmup_seed = 10000;
+            const auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
+            const auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
+            const auto clock_cost_estimation_tick_limit = 100000;
+            const auto clock_cost_estimation_time = std::chrono::milliseconds(10);
+            const auto clock_cost_estimation_iterations = 10000;
+
+            template <typename Clock>
+            int warmup() {
+                return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>)
+                    .iterations;
+            }
+            template <typename Clock>
+            EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) {
+                auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
+                    .result;
+                return {
+                    FloatDuration<Clock>(mean(r.begin(), r.end())),
+                    classify_outliers(r.begin(), r.end()),
+                };
+            }
+            template <typename Clock>
+            EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
+                auto time_limit = (std::min)(
+                    resolution * clock_cost_estimation_tick_limit,
+                    FloatDuration<Clock>(clock_cost_estimation_time_limit));
+                auto time_clock = [](int k) {
+                    return Detail::measure<Clock>([k] {
+                        for (int i = 0; i < k; ++i) {
+                            volatile auto ignored = Clock::now();
+                            (void)ignored;
+                        }
+                    }).elapsed;
+                };
+                time_clock(1);
+                int iters = clock_cost_estimation_iterations;
+                auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock);
+                std::vector<double> times;
+                int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
+                times.reserve(nsamples);
+                std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] {
+                    return static_cast<double>((time_clock(r.iterations) / r.iterations).count());
+                });
+                return {
+                    FloatDuration<Clock>(mean(times.begin(), times.end())),
+                    classify_outliers(times.begin(), times.end()),
+                };
+            }
+
+            template <typename Clock>
+            Environment<FloatDuration<Clock>> measure_environment() {
+                static Environment<FloatDuration<Clock>>* env = nullptr;
+                if (env) {
+                    return *env;
+                }
+
+                auto iters = Detail::warmup<Clock>();
+                auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
+                auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
+
+                env = new Environment<FloatDuration<Clock>>{ resolution, cost };
+                return *env;
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_estimate_clock.hpp
+// start catch_analyse.hpp
+
+ // Run and analyse one benchmark
+
+
+// start catch_sample_analysis.hpp
+
+// Benchmark results
+
+
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <iterator>
+
+namespace Catch {
+    namespace Benchmark {
+        template <typename Duration>
+        struct SampleAnalysis {
+            std::vector<Duration> samples;
+            Estimate<Duration> mean;
+            Estimate<Duration> standard_deviation;
+            OutlierClassification outliers;
+            double outlier_variance;
+
+            template <typename Duration2>
+            operator SampleAnalysis<Duration2>() const {
+                std::vector<Duration2> samples2;
+                samples2.reserve(samples.size());
+                std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
+                return {
+                    std::move(samples2),
+                    mean,
+                    standard_deviation,
+                    outliers,
+                    outlier_variance,
+                };
+            }
+        };
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_sample_analysis.hpp
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename Duration, typename Iterator>
+            SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) {
+                if (!cfg.benchmarkNoAnalysis()) {
+                    std::vector<double> samples;
+                    samples.reserve(last - first);
+                    std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
+
+                    auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end());
+                    auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end());
+
+                    auto wrap_estimate = [](Estimate<double> e) {
+                        return Estimate<Duration> {
+                            Duration(e.point),
+                                Duration(e.lower_bound),
+                                Duration(e.upper_bound),
+                                e.confidence_interval,
+                        };
+                    };
+                    std::vector<Duration> samples2;
+                    samples2.reserve(samples.size());
+                    std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
+                    return {
+                        std::move(samples2),
+                        wrap_estimate(analysis.mean),
+                        wrap_estimate(analysis.standard_deviation),
+                        outliers,
+                        analysis.outlier_variance,
+                    };
+                } else {
+                    std::vector<Duration> samples;
+                    samples.reserve(last - first);
+
+                    Duration mean = Duration(0);
+                    int i = 0;
+                    for (auto it = first; it < last; ++it, ++i) {
+                        samples.push_back(Duration(*it));
+                        mean += Duration(*it);
+                    }
+                    mean /= i;
+
+                    return {
+                        std::move(samples),
+                        Estimate<Duration>{mean, mean, mean, 0.0},
+                        Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
+                        OutlierClassification{},
+                        0.0
+                    };
+                }
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+// end catch_analyse.hpp
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <vector>
+#include <cmath>
+
+namespace Catch {
+    namespace Benchmark {
+        struct Benchmark {
+            Benchmark(std::string &&name)
+                : name(std::move(name)) {}
+
+            template <class FUN>
+            Benchmark(std::string &&name, FUN &&func)
+                : fun(std::move(func)), name(std::move(name)) {}
+
+            template <typename Clock>
+            ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
+                auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
+                auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
+                auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun);
+                int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
+                return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
+            }
+
+            template <typename Clock = default_clock>
+            void run() {
+                IConfigPtr cfg = getCurrentContext().getConfig();
+
+                auto env = Detail::measure_environment<Clock>();
+
+                getResultCapture().benchmarkPreparing(name);
+                CATCH_TRY{
+                    auto plan = user_code([&] {
+                        return prepare<Clock>(*cfg, env);
+                    });
+
+                    BenchmarkInfo info {
+                        name,
+                        plan.estimated_duration.count(),
+                        plan.iterations_per_sample,
+                        cfg->benchmarkSamples(),
+                        cfg->benchmarkResamples(),
+                        env.clock_resolution.mean.count(),
+                        env.clock_cost.mean.count()
+                    };
+
+                    getResultCapture().benchmarkStarting(info);
+
+                    auto samples = user_code([&] {
+                        return plan.template run<Clock>(*cfg, env);
+                    });
+
+                    auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
+                    BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
+                    getResultCapture().benchmarkEnded(stats);
+
+                } CATCH_CATCH_ALL{
+                    if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow.
+                        std::rethrow_exception(std::current_exception());
+                }
+            }
+
+            // sets lambda to be used in fun *and* executes benchmark!
+            template <typename Fun,
+                typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0>
+                Benchmark & operator=(Fun func) {
+                fun = Detail::BenchmarkFunction(func);
+                run();
+                return *this;
+            }
+
+            explicit operator bool() {
+                return true;
+            }
+
+        private:
+            Detail::BenchmarkFunction fun;
+            std::string name;
+        };
+    }
+} // namespace Catch
+
+#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1
+#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2
+
+#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\
+    if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
+        BenchmarkName = [&](int benchmarkIndex)
+
+#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\
+    if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
+        BenchmarkName = [&]
+
+// end catch_benchmark.hpp
+// start catch_constructor.hpp
+
+// Constructor and destructor helpers
+
+
+#include <type_traits>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename T, bool Destruct>
+            struct ObjectStorage
+            {
+                using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
+
+                ObjectStorage() : data() {}
+
+                ObjectStorage(const ObjectStorage& other)
+                {
+                    new(&data) T(other.stored_object());
+                }
+
+                ObjectStorage(ObjectStorage&& other)
+                {
+                    new(&data) T(std::move(other.stored_object()));
+                }
+
+                ~ObjectStorage() { destruct_on_exit<T>(); }
+
+                template <typename... Args>
+                void construct(Args&&... args)
+                {
+                    new (&data) T(std::forward<Args>(args)...);
+                }
+
+                template <bool AllowManualDestruction = !Destruct>
+                typename std::enable_if<AllowManualDestruction>::type destruct()
+                {
+                    stored_object().~T();
+                }
+
+            private:
+                // If this is a constructor benchmark, destruct the underlying object
+                template <typename U>
+                void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
+                // Otherwise, don't
+                template <typename U>
+                void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
+
+                T& stored_object() {
+                    return *static_cast<T*>(static_cast<void*>(&data));
+                }
+
+                T const& stored_object() const {
+                    return *static_cast<T*>(static_cast<void*>(&data));
+                }
+
+                TStorage data;
+            };
+        }
+
+        template <typename T>
+        using storage_for = Detail::ObjectStorage<T, true>;
+
+        template <typename T>
+        using destructable_object = Detail::ObjectStorage<T, false>;
+    }
+}
+
+// end catch_constructor.hpp
+// end catch_benchmarking_all.hpp
+#endif
+
+#endif // ! CATCH_CONFIG_IMPL_ONLY
+
+#ifdef CATCH_IMPL
+// start catch_impl.hpp
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// Keep these here for external reporters
+// start catch_test_case_tracker.h
+
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+namespace TestCaseTracking {
+
+    struct NameAndLocation {
+        std::string name;
+        SourceLineInfo location;
+
+        NameAndLocation( std::string const& _name, SourceLineInfo const& _location );
+        friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
+            return lhs.name == rhs.name
+                && lhs.location == rhs.location;
+        }
+    };
+
+    class ITracker;
+
+    using ITrackerPtr = std::shared_ptr<ITracker>;
+
+    class  ITracker {
+        NameAndLocation m_nameAndLocation;
+
+    public:
+        ITracker(NameAndLocation const& nameAndLoc) :
+            m_nameAndLocation(nameAndLoc)
+        {}
+
+        // static queries
+        NameAndLocation const& nameAndLocation() const {
+            return m_nameAndLocation;
+        }
+
+        virtual ~ITracker();
+
+        // dynamic queries
+        virtual bool isComplete() const = 0; // Successfully completed or failed
+        virtual bool isSuccessfullyCompleted() const = 0;
+        virtual bool isOpen() const = 0; // Started but not complete
+        virtual bool hasChildren() const = 0;
+        virtual bool hasStarted() const = 0;
+
+        virtual ITracker& parent() = 0;
+
+        // actions
+        virtual void close() = 0; // Successfully complete
+        virtual void fail() = 0;
+        virtual void markAsNeedingAnotherRun() = 0;
+
+        virtual void addChild( ITrackerPtr const& child ) = 0;
+        virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0;
+        virtual void openChild() = 0;
+
+        // Debug/ checking
+        virtual bool isSectionTracker() const = 0;
+        virtual bool isGeneratorTracker() const = 0;
+    };
+
+    class TrackerContext {
+
+        enum RunState {
+            NotStarted,
+            Executing,
+            CompletedCycle
+        };
+
+        ITrackerPtr m_rootTracker;
+        ITracker* m_currentTracker = nullptr;
+        RunState m_runState = NotStarted;
+
+    public:
+
+        ITracker& startRun();
+        void endRun();
+
+        void startCycle();
+        void completeCycle();
+
+        bool completedCycle() const;
+        ITracker& currentTracker();
+        void setCurrentTracker( ITracker* tracker );
+    };
+
+    class TrackerBase : public ITracker {
+    protected:
+        enum CycleState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            NeedsAnotherRun,
+            CompletedSuccessfully,
+            Failed
+        };
+
+        using Children = std::vector<ITrackerPtr>;
+        TrackerContext& m_ctx;
+        ITracker* m_parent;
+        Children m_children;
+        CycleState m_runState = NotStarted;
+
+    public:
+        TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+        bool isComplete() const override;
+        bool isSuccessfullyCompleted() const override;
+        bool isOpen() const override;
+        bool hasChildren() const override;
+        bool hasStarted() const override {
+            return m_runState != NotStarted;
+        }
+
+        void addChild( ITrackerPtr const& child ) override;
+
+        ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override;
+        ITracker& parent() override;
+
+        void openChild() override;
+
+        bool isSectionTracker() const override;
+        bool isGeneratorTracker() const override;
+
+        void open();
+
+        void close() override;
+        void fail() override;
+        void markAsNeedingAnotherRun() override;
+
+    private:
+        void moveToParent();
+        void moveToThis();
+    };
+
+    class SectionTracker : public TrackerBase {
+        std::vector<std::string> m_filters;
+        std::string m_trimmed_name;
+    public:
+        SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+        bool isSectionTracker() const override;
+
+        bool isComplete() const override;
+
+        static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation );
+
+        void tryOpen();
+
+        void addInitialFilters( std::vector<std::string> const& filters );
+        void addNextFilters( std::vector<std::string> const& filters );
+        //! Returns filters active in this tracker
+        std::vector<std::string> const& getFilters() const;
+        //! Returns whitespace-trimmed name of the tracked section
+        std::string const& trimmedName() const;
+    };
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+
+} // namespace Catch
+
+// end catch_test_case_tracker.h
+
+// start catch_leak_detector.h
+
+namespace Catch {
+
+    struct LeakDetector {
+        LeakDetector();
+        ~LeakDetector();
+    };
+
+}
+// end catch_leak_detector.h
+// Cpp files will be included in the single-header file here
+// start catch_stats.cpp
+
+// Statistical analysis tools
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+
+#include <cassert>
+#include <random>
+
+#if defined(CATCH_CONFIG_USE_ASYNC)
+#include <future>
+#endif
+
+namespace {
+    double erf_inv(double x) {
+        // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2
+        double w, p;
+
+        w = -log((1.0 - x) * (1.0 + x));
+
+        if (w < 6.250000) {
+            w = w - 3.125000;
+            p = -3.6444120640178196996e-21;
+            p = -1.685059138182016589e-19 + p * w;
+            p = 1.2858480715256400167e-18 + p * w;
+            p = 1.115787767802518096e-17 + p * w;
+            p = -1.333171662854620906e-16 + p * w;
+            p = 2.0972767875968561637e-17 + p * w;
+            p = 6.6376381343583238325e-15 + p * w;
+            p = -4.0545662729752068639e-14 + p * w;
+            p = -8.1519341976054721522e-14 + p * w;
+            p = 2.6335093153082322977e-12 + p * w;
+            p = -1.2975133253453532498e-11 + p * w;
+            p = -5.4154120542946279317e-11 + p * w;
+            p = 1.051212273321532285e-09 + p * w;
+            p = -4.1126339803469836976e-09 + p * w;
+            p = -2.9070369957882005086e-08 + p * w;
+            p = 4.2347877827932403518e-07 + p * w;
+            p = -1.3654692000834678645e-06 + p * w;
+            p = -1.3882523362786468719e-05 + p * w;
+            p = 0.0001867342080340571352 + p * w;
+            p = -0.00074070253416626697512 + p * w;
+            p = -0.0060336708714301490533 + p * w;
+            p = 0.24015818242558961693 + p * w;
+            p = 1.6536545626831027356 + p * w;
+        } else if (w < 16.000000) {
+            w = sqrt(w) - 3.250000;
+            p = 2.2137376921775787049e-09;
+            p = 9.0756561938885390979e-08 + p * w;
+            p = -2.7517406297064545428e-07 + p * w;
+            p = 1.8239629214389227755e-08 + p * w;
+            p = 1.5027403968909827627e-06 + p * w;
+            p = -4.013867526981545969e-06 + p * w;
+            p = 2.9234449089955446044e-06 + p * w;
+            p = 1.2475304481671778723e-05 + p * w;
+            p = -4.7318229009055733981e-05 + p * w;
+            p = 6.8284851459573175448e-05 + p * w;
+            p = 2.4031110387097893999e-05 + p * w;
+            p = -0.0003550375203628474796 + p * w;
+            p = 0.00095328937973738049703 + p * w;
+            p = -0.0016882755560235047313 + p * w;
+            p = 0.0024914420961078508066 + p * w;
+            p = -0.0037512085075692412107 + p * w;
+            p = 0.005370914553590063617 + p * w;
+            p = 1.0052589676941592334 + p * w;
+            p = 3.0838856104922207635 + p * w;
+        } else {
+            w = sqrt(w) - 5.000000;
+            p = -2.7109920616438573243e-11;
+            p = -2.5556418169965252055e-10 + p * w;
+            p = 1.5076572693500548083e-09 + p * w;
+            p = -3.7894654401267369937e-09 + p * w;
+            p = 7.6157012080783393804e-09 + p * w;
+            p = -1.4960026627149240478e-08 + p * w;
+            p = 2.9147953450901080826e-08 + p * w;
+            p = -6.7711997758452339498e-08 + p * w;
+            p = 2.2900482228026654717e-07 + p * w;
+            p = -9.9298272942317002539e-07 + p * w;
+            p = 4.5260625972231537039e-06 + p * w;
+            p = -1.9681778105531670567e-05 + p * w;
+            p = 7.5995277030017761139e-05 + p * w;
+            p = -0.00021503011930044477347 + p * w;
+            p = -0.00013871931833623122026 + p * w;
+            p = 1.0103004648645343977 + p * w;
+            p = 4.8499064014085844221 + p * w;
+        }
+        return p * x;
+    }
+
+    double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) {
+        auto m = Catch::Benchmark::Detail::mean(first, last);
+        double variance = std::accumulate(first, last, 0., [m](double a, double b) {
+            double diff = b - m;
+            return a + diff * diff;
+            }) / (last - first);
+            return std::sqrt(variance);
+    }
+
+}
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+
+            double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) {
+                auto count = last - first;
+                double idx = (count - 1) * k / static_cast<double>(q);
+                int j = static_cast<int>(idx);
+                double g = idx - j;
+                std::nth_element(first, first + j, last);
+                auto xj = first[j];
+                if (g == 0) return xj;
+
+                auto xj1 = *std::min_element(first + (j + 1), last);
+                return xj + g * (xj1 - xj);
+            }
+
+            double erfc_inv(double x) {
+                return erf_inv(1.0 - x);
+            }
+
+            double normal_quantile(double p) {
+                static const double ROOT_TWO = std::sqrt(2.0);
+
+                double result = 0.0;
+                assert(p >= 0 && p <= 1);
+                if (p < 0 || p > 1) {
+                    return result;
+                }
+
+                result = -erfc_inv(2.0 * p);
+                // result *= normal distribution standard deviation (1.0) * sqrt(2)
+                result *= /*sd * */ ROOT_TWO;
+                // result += normal disttribution mean (0)
+                return result;
+            }
+
+            double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) {
+                double sb = stddev.point;
+                double mn = mean.point / n;
+                double mg_min = mn / 2.;
+                double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
+                double sg2 = sg * sg;
+                double sb2 = sb * sb;
+
+                auto c_max = [n, mn, sb2, sg2](double x) -> double {
+                    double k = mn - x;
+                    double d = k * k;
+                    double nd = n * d;
+                    double k0 = -n * nd;
+                    double k1 = sb2 - n * sg2 + nd;
+                    double det = k1 * k1 - 4 * sg2 * k0;
+                    return (int)(-2. * k0 / (k1 + std::sqrt(det)));
+                };
+
+                auto var_out = [n, sb2, sg2](double c) {
+                    double nc = n - c;
+                    return (nc / n) * (sb2 - nc * sg2);
+                };
+
+                return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
+            }
+
+            bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
+                CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+                CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+                static std::random_device entropy;
+                CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+                auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
+
+                auto mean = &Detail::mean<std::vector<double>::iterator>;
+                auto stddev = &standard_deviation;
+
+#if defined(CATCH_CONFIG_USE_ASYNC)
+                auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
+                    auto seed = entropy();
+                    return std::async(std::launch::async, [=] {
+                        std::mt19937 rng(seed);
+                        auto resampled = resample(rng, n_resamples, first, last, f);
+                        return bootstrap(confidence_level, first, last, resampled, f);
+                    });
+                };
+
+                auto mean_future = Estimate(mean);
+                auto stddev_future = Estimate(stddev);
+
+                auto mean_estimate = mean_future.get();
+                auto stddev_estimate = stddev_future.get();
+#else
+                auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
+                    auto seed = entropy();
+                    std::mt19937 rng(seed);
+                    auto resampled = resample(rng, n_resamples, first, last, f);
+                    return bootstrap(confidence_level, first, last, resampled, f);
+                };
+
+                auto mean_estimate = Estimate(mean);
+                auto stddev_estimate = Estimate(stddev);
+#endif // CATCH_USE_ASYNC
+
+                double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n);
+
+                return { mean_estimate, stddev_estimate, outlier_variance };
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+// end catch_stats.cpp
+// start catch_approx.cpp
+
+#include <cmath>
+#include <limits>
+
+namespace {
+
+// Performs equivalent check of std::fabs(lhs - rhs) <= margin
+// But without the subtraction to allow for INFINITY in comparison
+bool marginComparison(double lhs, double rhs, double margin) {
+    return (lhs + margin >= rhs) && (rhs + margin >= lhs);
+}
+
+}
+
+namespace Catch {
+namespace Detail {
+
+    Approx::Approx ( double value )
+    :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+        m_margin( 0.0 ),
+        m_scale( 0.0 ),
+        m_value( value )
+    {}
+
+    Approx Approx::custom() {
+        return Approx( 0 );
+    }
+
+    Approx Approx::operator-() const {
+        auto temp(*this);
+        temp.m_value = -temp.m_value;
+        return temp;
+    }
+
+    std::string Approx::toString() const {
+        ReusableStringStream rss;
+        rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
+        return rss.str();
+    }
+
+    bool Approx::equalityComparisonImpl(const double other) const {
+        // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
+        // Thanks to Richard Harris for his help refining the scaled margin value
+        return marginComparison(m_value, other, m_margin)
+            || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
+    }
+
+    void Approx::setMargin(double newMargin) {
+        CATCH_ENFORCE(newMargin >= 0,
+            "Invalid Approx::margin: " << newMargin << '.'
+            << " Approx::Margin has to be non-negative.");
+        m_margin = newMargin;
+    }
+
+    void Approx::setEpsilon(double newEpsilon) {
+        CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
+            "Invalid Approx::epsilon: " << newEpsilon << '.'
+            << " Approx::epsilon has to be in [0, 1]");
+        m_epsilon = newEpsilon;
+    }
+
+} // end namespace Detail
+
+namespace literals {
+    Detail::Approx operator "" _a(long double val) {
+        return Detail::Approx(val);
+    }
+    Detail::Approx operator "" _a(unsigned long long val) {
+        return Detail::Approx(val);
+    }
+} // end namespace literals
+
+std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx const& value) {
+    return value.toString();
+}
+
+} // end namespace Catch
+// end catch_approx.cpp
+// start catch_assertionhandler.cpp
+
+// start catch_debugger.h
+
+namespace Catch {
+    bool isDebuggerActive();
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+    #if defined(__i386__) || defined(__x86_64__)
+        #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
+    #elif defined(__aarch64__)
+        #define CATCH_TRAP()  __asm__(".inst 0xd4200000")
+    #endif
+
+#elif defined(CATCH_PLATFORM_IPHONE)
+
+    // use inline assembler
+    #if defined(__i386__) || defined(__x86_64__)
+        #define CATCH_TRAP()  __asm__("int $3")
+    #elif defined(__aarch64__)
+        #define CATCH_TRAP()  __asm__(".inst 0xd4200000")
+    #elif defined(__arm__) && !defined(__thumb__)
+        #define CATCH_TRAP()  __asm__(".inst 0xe7f001f0")
+    #elif defined(__arm__) &&  defined(__thumb__)
+        #define CATCH_TRAP()  __asm__(".inst 0xde01")
+    #endif
+
+#elif defined(CATCH_PLATFORM_LINUX)
+    // If we can use inline assembler, do it because this allows us to break
+    // directly at the location of the failing check instead of breaking inside
+    // raise() called from it, i.e. one stack frame below.
+    #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+        #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */
+    #else // Fall back to the generic way.
+        #include <signal.h>
+
+        #define CATCH_TRAP() raise(SIGTRAP)
+    #endif
+#elif defined(_MSC_VER)
+    #define CATCH_TRAP() __debugbreak()
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+    #define CATCH_TRAP() DebugBreak()
+#endif
+
+#ifndef CATCH_BREAK_INTO_DEBUGGER
+    #ifdef CATCH_TRAP
+        #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
+    #else
+        #define CATCH_BREAK_INTO_DEBUGGER() []{}()
+    #endif
+#endif
+
+// end catch_debugger.h
+// start catch_run_context.h
+
+// start catch_fatal_condition.h
+
+#include <cassert>
+
+namespace Catch {
+
+    // Wrapper for platform-specific fatal error (signals/SEH) handlers
+    //
+    // Tries to be cooperative with other handlers, and not step over
+    // other handlers. This means that unknown structured exceptions
+    // are passed on, previous signal handlers are called, and so on.
+    //
+    // Can only be instantiated once, and assumes that once a signal
+    // is caught, the binary will end up terminating. Thus, there
+    class FatalConditionHandler {
+        bool m_started = false;
+
+        // Install/disengage implementation for specific platform.
+        // Should be if-defed to work on current platform, can assume
+        // engage-disengage 1:1 pairing.
+        void engage_platform();
+        void disengage_platform();
+    public:
+        // Should also have platform-specific implementations as needed
+        FatalConditionHandler();
+        ~FatalConditionHandler();
+
+        void engage() {
+            assert(!m_started && "Handler cannot be installed twice.");
+            m_started = true;
+            engage_platform();
+        }
+
+        void disengage() {
+            assert(m_started && "Handler cannot be uninstalled without being installed first");
+            m_started = false;
+            disengage_platform();
+        }
+    };
+
+    //! Simple RAII guard for (dis)engaging the FatalConditionHandler
+    class FatalConditionHandlerGuard {
+        FatalConditionHandler* m_handler;
+    public:
+        FatalConditionHandlerGuard(FatalConditionHandler* handler):
+            m_handler(handler) {
+            m_handler->engage();
+        }
+        ~FatalConditionHandlerGuard() {
+            m_handler->disengage();
+        }
+    };
+
+} // end namespace Catch
+
+// end catch_fatal_condition.h
+#include <string>
+
+namespace Catch {
+
+    struct IMutableContext;
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class RunContext : public IResultCapture, public IRunner {
+
+    public:
+        RunContext( RunContext const& ) = delete;
+        RunContext& operator =( RunContext const& ) = delete;
+
+        explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter );
+
+        ~RunContext() override;
+
+        void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount );
+        void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount );
+
+        Totals runTest(TestCase const& testCase);
+
+        IConfigPtr config() const;
+        IStreamingReporter& reporter() const;
+
+    public: // IResultCapture
+
+        // Assertion handlers
+        void handleExpr
+                (   AssertionInfo const& info,
+                    ITransientExpression const& expr,
+                    AssertionReaction& reaction ) override;
+        void handleMessage
+                (   AssertionInfo const& info,
+                    ResultWas::OfType resultType,
+                    StringRef const& message,
+                    AssertionReaction& reaction ) override;
+        void handleUnexpectedExceptionNotThrown
+                (   AssertionInfo const& info,
+                    AssertionReaction& reaction ) override;
+        void handleUnexpectedInflightException
+                (   AssertionInfo const& info,
+                    std::string const& message,
+                    AssertionReaction& reaction ) override;
+        void handleIncomplete
+                (   AssertionInfo const& info ) override;
+        void handleNonExpr
+                (   AssertionInfo const &info,
+                    ResultWas::OfType resultType,
+                    AssertionReaction &reaction ) override;
+
+        bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override;
+
+        void sectionEnded( SectionEndInfo const& endInfo ) override;
+        void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
+
+        auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+        void benchmarkPreparing( std::string const& name ) override;
+        void benchmarkStarting( BenchmarkInfo const& info ) override;
+        void benchmarkEnded( BenchmarkStats<> const& stats ) override;
+        void benchmarkFailed( std::string const& error ) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+        void pushScopedMessage( MessageInfo const& message ) override;
+        void popScopedMessage( MessageInfo const& message ) override;
+
+        void emplaceUnscopedMessage( MessageBuilder const& builder ) override;
+
+        std::string getCurrentTestName() const override;
+
+        const AssertionResult* getLastResult() const override;
+
+        void exceptionEarlyReported() override;
+
+        void handleFatalErrorCondition( StringRef message ) override;
+
+        bool lastAssertionPassed() override;
+
+        void assertionPassed() override;
+
+    public:
+        // !TBD We need to do this another way!
+        bool aborting() const final;
+
+    private:
+
+        void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
+        void invokeActiveTestCase();
+
+        void resetAssertionInfo();
+        bool testForMissingAssertions( Counts& assertions );
+
+        void assertionEnded( AssertionResult const& result );
+        void reportExpr
+                (   AssertionInfo const &info,
+                    ResultWas::OfType resultType,
+                    ITransientExpression const *expr,
+                    bool negated );
+
+        void populateReaction( AssertionReaction& reaction );
+
+    private:
+
+        void handleUnfinishedSections();
+
+        TestRunInfo m_runInfo;
+        IMutableContext& m_context;
+        TestCase const* m_activeTestCase = nullptr;
+        ITracker* m_testCaseTracker = nullptr;
+        Option<AssertionResult> m_lastResult;
+
+        IConfigPtr m_config;
+        Totals m_totals;
+        IStreamingReporterPtr m_reporter;
+        std::vector<MessageInfo> m_messages;
+        std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
+        AssertionInfo m_lastAssertionInfo;
+        std::vector<SectionEndInfo> m_unfinishedSections;
+        std::vector<ITracker*> m_activeSections;
+        TrackerContext m_trackerContext;
+        FatalConditionHandler m_fatalConditionhandler;
+        bool m_lastAssertionPassed = false;
+        bool m_shouldReportUnexpected = true;
+        bool m_includeSuccessfulResults;
+    };
+
+    void seedRng(IConfig const& config);
+    unsigned int rngSeed();
+} // end namespace Catch
+
+// end catch_run_context.h
+namespace Catch {
+
+    namespace {
+        auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& {
+            expr.streamReconstructedExpression( os );
+            return os;
+        }
+    }
+
+    LazyExpression::LazyExpression( bool isNegated )
+    :   m_isNegated( isNegated )
+    {}
+
+    LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {}
+
+    LazyExpression::operator bool() const {
+        return m_transientExpression != nullptr;
+    }
+
+    auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& {
+        if( lazyExpr.m_isNegated )
+            os << "!";
+
+        if( lazyExpr ) {
+            if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() )
+                os << "(" << *lazyExpr.m_transientExpression << ")";
+            else
+                os << *lazyExpr.m_transientExpression;
+        }
+        else {
+            os << "{** error - unchecked empty expression requested **}";
+        }
+        return os;
+    }
+
+    AssertionHandler::AssertionHandler
+        (   StringRef const& macroName,
+            SourceLineInfo const& lineInfo,
+            StringRef capturedExpression,
+            ResultDisposition::Flags resultDisposition )
+    :   m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
+        m_resultCapture( getResultCapture() )
+    {}
+
+    void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
+        m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
+    }
+    void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) {
+        m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
+    }
+
+    auto AssertionHandler::allowThrows() const -> bool {
+        return getCurrentContext().getConfig()->allowThrows();
+    }
+
+    void AssertionHandler::complete() {
+        setCompleted();
+        if( m_reaction.shouldDebugBreak ) {
+
+            // If you find your debugger stopping you here then go one level up on the
+            // call-stack for the code that caused it (typically a failed assertion)
+
+            // (To go back to the test and change execution, jump over the throw, next)
+            CATCH_BREAK_INTO_DEBUGGER();
+        }
+        if (m_reaction.shouldThrow) {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+            throw Catch::TestFailureException();
+#else
+            CATCH_ERROR( "Test failure requires aborting test!" );
+#endif
+        }
+    }
+    void AssertionHandler::setCompleted() {
+        m_completed = true;
+    }
+
+    void AssertionHandler::handleUnexpectedInflightException() {
+        m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );
+    }
+
+    void AssertionHandler::handleExceptionThrownAsExpected() {
+        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+    }
+    void AssertionHandler::handleExceptionNotThrownAsExpected() {
+        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+    }
+
+    void AssertionHandler::handleUnexpectedExceptionNotThrown() {
+        m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction );
+    }
+
+    void AssertionHandler::handleThrowingCallSkipped() {
+        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+    }
+
+    // This is the overload that takes a string and infers the Equals matcher from it
+    // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp
+    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString  ) {
+        handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString );
+    }
+
+} // namespace Catch
+// end catch_assertionhandler.cpp
+// start catch_assertionresult.cpp
+
+namespace Catch {
+    AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression):
+        lazyExpression(_lazyExpression),
+        resultType(_resultType) {}
+
+    std::string AssertionResultData::reconstructExpression() const {
+
+        if( reconstructedExpression.empty() ) {
+            if( lazyExpression ) {
+                ReusableStringStream rss;
+                rss << lazyExpression;
+                reconstructedExpression = rss.str();
+            }
+        }
+        return reconstructedExpression;
+    }
+
+    AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+    :   m_info( info ),
+        m_resultData( data )
+    {}
+
+    // Result was a success
+    bool AssertionResult::succeeded() const {
+        return Catch::isOk( m_resultData.resultType );
+    }
+
+    // Result was a success, or failure is suppressed
+    bool AssertionResult::isOk() const {
+        return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+    }
+
+    ResultWas::OfType AssertionResult::getResultType() const {
+        return m_resultData.resultType;
+    }
+
+    bool AssertionResult::hasExpression() const {
+        return !m_info.capturedExpression.empty();
+    }
+
+    bool AssertionResult::hasMessage() const {
+        return !m_resultData.message.empty();
+    }
+
+    std::string AssertionResult::getExpression() const {
+        // Possibly overallocating by 3 characters should be basically free
+        std::string expr; expr.reserve(m_info.capturedExpression.size() + 3);
+        if (isFalseTest(m_info.resultDisposition)) {
+            expr += "!(";
+        }
+        expr += m_info.capturedExpression;
+        if (isFalseTest(m_info.resultDisposition)) {
+            expr += ')';
+        }
+        return expr;
+    }
+
+    std::string AssertionResult::getExpressionInMacro() const {
+        std::string expr;
+        if( m_info.macroName.empty() )
+            expr = static_cast<std::string>(m_info.capturedExpression);
+        else {
+            expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
+            expr += m_info.macroName;
+            expr += "( ";
+            expr += m_info.capturedExpression;
+            expr += " )";
+        }
+        return expr;
+    }
+
+    bool AssertionResult::hasExpandedExpression() const {
+        return hasExpression() && getExpandedExpression() != getExpression();
+    }
+
+    std::string AssertionResult::getExpandedExpression() const {
+        std::string expr = m_resultData.reconstructExpression();
+        return expr.empty()
+                ? getExpression()
+                : expr;
+    }
+
+    std::string AssertionResult::getMessage() const {
+        return m_resultData.message;
+    }
+    SourceLineInfo AssertionResult::getSourceInfo() const {
+        return m_info.lineInfo;
+    }
+
+    StringRef AssertionResult::getTestMacroName() const {
+        return m_info.macroName;
+    }
+
+} // end namespace Catch
+// end catch_assertionresult.cpp
+// start catch_capture_matchers.cpp
+
+namespace Catch {
+
+    using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
+
+    // This is the general overload that takes a any string matcher
+    // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers
+    // the Equals matcher (so the header does not mention matchers)
+    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  ) {
+        std::string exceptionMessage = Catch::translateActiveException();
+        MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString );
+        handler.handleExpr( expr );
+    }
+
+} // namespace Catch
+// end catch_capture_matchers.cpp
+// start catch_commandline.cpp
+
+// start catch_commandline.h
+
+// start catch_clara.h
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#endif
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#pragma clang diagnostic ignored "-Wshadow"
+#endif
+
+// start clara.hpp
+// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// See https://github.com/philsquared/Clara for more details
+
+// Clara v1.1.5
+
+
+#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#ifndef CLARA_CONFIG_OPTIONAL_TYPE
+#ifdef __has_include
+#if __has_include(<optional>) && __cplusplus >= 201703L
+#include <optional>
+#define CLARA_CONFIG_OPTIONAL_TYPE std::optional
+#endif
+#endif
+#endif
+
+// ----------- #included from clara_textflow.hpp -----------
+
+// TextFlowCpp
+//
+// A single-header library for wrapping and laying out basic text, by Phil Nash
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// This project is hosted at https://github.com/philsquared/textflowcpp
+
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+#include <vector>
+
+#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+namespace clara {
+namespace TextFlow {
+
+inline auto isWhitespace(char c) -> bool {
+	static std::string chars = " \t\n\r";
+	return chars.find(c) != std::string::npos;
+}
+inline auto isBreakableBefore(char c) -> bool {
+	static std::string chars = "[({<|";
+	return chars.find(c) != std::string::npos;
+}
+inline auto isBreakableAfter(char c) -> bool {
+	static std::string chars = "])}>.,:;*+-=&/\\";
+	return chars.find(c) != std::string::npos;
+}
+
+class Columns;
+
+class Column {
+	std::vector<std::string> m_strings;
+	size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH;
+	size_t m_indent = 0;
+	size_t m_initialIndent = std::string::npos;
+
+public:
+	class iterator {
+		friend Column;
+
+		Column const& m_column;
+		size_t m_stringIndex = 0;
+		size_t m_pos = 0;
+
+		size_t m_len = 0;
+		size_t m_end = 0;
+		bool m_suffix = false;
+
+		iterator(Column const& column, size_t stringIndex)
+			: m_column(column),
+			m_stringIndex(stringIndex) {}
+
+		auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; }
+
+		auto isBoundary(size_t at) const -> bool {
+			assert(at > 0);
+			assert(at <= line().size());
+
+			return at == line().size() ||
+				(isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) ||
+				isBreakableBefore(line()[at]) ||
+				isBreakableAfter(line()[at - 1]);
+		}
+
+		void calcLength() {
+			assert(m_stringIndex < m_column.m_strings.size());
+
+			m_suffix = false;
+			auto width = m_column.m_width - indent();
+			m_end = m_pos;
+			if (line()[m_pos] == '\n') {
+				++m_end;
+			}
+			while (m_end < line().size() && line()[m_end] != '\n')
+				++m_end;
+
+			if (m_end < m_pos + width) {
+				m_len = m_end - m_pos;
+			} else {
+				size_t len = width;
+				while (len > 0 && !isBoundary(m_pos + len))
+					--len;
+				while (len > 0 && isWhitespace(line()[m_pos + len - 1]))
+					--len;
+
+				if (len > 0) {
+					m_len = len;
+				} else {
+					m_suffix = true;
+					m_len = width - 1;
+				}
+			}
+		}
+
+		auto indent() const -> size_t {
+			auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
+			return initial == std::string::npos ? m_column.m_indent : initial;
+		}
+
+		auto addIndentAndSuffix(std::string const &plain) const -> std::string {
+			return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain);
+		}
+
+	public:
+		using difference_type = std::ptrdiff_t;
+		using value_type = std::string;
+		using pointer = value_type * ;
+		using reference = value_type & ;
+		using iterator_category = std::forward_iterator_tag;
+
+		explicit iterator(Column const& column) : m_column(column) {
+			assert(m_column.m_width > m_column.m_indent);
+			assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent);
+			calcLength();
+			if (m_len == 0)
+				m_stringIndex++; // Empty string
+		}
+
+		auto operator *() const -> std::string {
+			assert(m_stringIndex < m_column.m_strings.size());
+			assert(m_pos <= m_end);
+			return addIndentAndSuffix(line().substr(m_pos, m_len));
+		}
+
+		auto operator ++() -> iterator& {
+			m_pos += m_len;
+			if (m_pos < line().size() && line()[m_pos] == '\n')
+				m_pos += 1;
+			else
+				while (m_pos < line().size() && isWhitespace(line()[m_pos]))
+					++m_pos;
+
+			if (m_pos == line().size()) {
+				m_pos = 0;
+				++m_stringIndex;
+			}
+			if (m_stringIndex < m_column.m_strings.size())
+				calcLength();
+			return *this;
+		}
+		auto operator ++(int) -> iterator {
+			iterator prev(*this);
+			operator++();
+			return prev;
+		}
+
+		auto operator ==(iterator const& other) const -> bool {
+			return
+				m_pos == other.m_pos &&
+				m_stringIndex == other.m_stringIndex &&
+				&m_column == &other.m_column;
+		}
+		auto operator !=(iterator const& other) const -> bool {
+			return !operator==(other);
+		}
+	};
+	using const_iterator = iterator;
+
+	explicit Column(std::string const& text) { m_strings.push_back(text); }
+
+	auto width(size_t newWidth) -> Column& {
+		assert(newWidth > 0);
+		m_width = newWidth;
+		return *this;
+	}
+	auto indent(size_t newIndent) -> Column& {
+		m_indent = newIndent;
+		return *this;
+	}
+	auto initialIndent(size_t newIndent) -> Column& {
+		m_initialIndent = newIndent;
+		return *this;
+	}
+
+	auto width() const -> size_t { return m_width; }
+	auto begin() const -> iterator { return iterator(*this); }
+	auto end() const -> iterator { return { *this, m_strings.size() }; }
+
+	inline friend std::ostream& operator << (std::ostream& os, Column const& col) {
+		bool first = true;
+		for (auto line : col) {
+			if (first)
+				first = false;
+			else
+				os << "\n";
+			os << line;
+		}
+		return os;
+	}
+
+	auto operator + (Column const& other)->Columns;
+
+	auto toString() const -> std::string {
+		std::ostringstream oss;
+		oss << *this;
+		return oss.str();
+	}
+};
+
+class Spacer : public Column {
+
+public:
+	explicit Spacer(size_t spaceWidth) : Column("") {
+		width(spaceWidth);
+	}
+};
+
+class Columns {
+	std::vector<Column> m_columns;
+
+public:
+
+	class iterator {
+		friend Columns;
+		struct EndTag {};
+
+		std::vector<Column> const& m_columns;
+		std::vector<Column::iterator> m_iterators;
+		size_t m_activeIterators;
+
+		iterator(Columns const& columns, EndTag)
+			: m_columns(columns.m_columns),
+			m_activeIterators(0) {
+			m_iterators.reserve(m_columns.size());
+
+			for (auto const& col : m_columns)
+				m_iterators.push_back(col.end());
+		}
+
+	public:
+		using difference_type = std::ptrdiff_t;
+		using value_type = std::string;
+		using pointer = value_type * ;
+		using reference = value_type & ;
+		using iterator_category = std::forward_iterator_tag;
+
+		explicit iterator(Columns const& columns)
+			: m_columns(columns.m_columns),
+			m_activeIterators(m_columns.size()) {
+			m_iterators.reserve(m_columns.size());
+
+			for (auto const& col : m_columns)
+				m_iterators.push_back(col.begin());
+		}
+
+		auto operator ==(iterator const& other) const -> bool {
+			return m_iterators == other.m_iterators;
+		}
+		auto operator !=(iterator const& other) const -> bool {
+			return m_iterators != other.m_iterators;
+		}
+		auto operator *() const -> std::string {
+			std::string row, padding;
+
+			for (size_t i = 0; i < m_columns.size(); ++i) {
+				auto width = m_columns[i].width();
+				if (m_iterators[i] != m_columns[i].end()) {
+					std::string col = *m_iterators[i];
+					row += padding + col;
+					if (col.size() < width)
+						padding = std::string(width - col.size(), ' ');
+					else
+						padding = "";
+				} else {
+					padding += std::string(width, ' ');
+				}
+			}
+			return row;
+		}
+		auto operator ++() -> iterator& {
+			for (size_t i = 0; i < m_columns.size(); ++i) {
+				if (m_iterators[i] != m_columns[i].end())
+					++m_iterators[i];
+			}
+			return *this;
+		}
+		auto operator ++(int) -> iterator {
+			iterator prev(*this);
+			operator++();
+			return prev;
+		}
+	};
+	using const_iterator = iterator;
+
+	auto begin() const -> iterator { return iterator(*this); }
+	auto end() const -> iterator { return { *this, iterator::EndTag() }; }
+
+	auto operator += (Column const& col) -> Columns& {
+		m_columns.push_back(col);
+		return *this;
+	}
+	auto operator + (Column const& col) -> Columns {
+		Columns combined = *this;
+		combined += col;
+		return combined;
+	}
+
+	inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) {
+
+		bool first = true;
+		for (auto line : cols) {
+			if (first)
+				first = false;
+			else
+				os << "\n";
+			os << line;
+		}
+		return os;
+	}
+
+	auto toString() const -> std::string {
+		std::ostringstream oss;
+		oss << *this;
+		return oss.str();
+	}
+};
+
+inline auto Column::operator + (Column const& other) -> Columns {
+	Columns cols;
+	cols += *this;
+	cols += other;
+	return cols;
+}
+}
+
+}
+}
+
+// ----------- end of #include from clara_textflow.hpp -----------
+// ........... back in clara.hpp
+
+#include <cctype>
+#include <string>
+#include <memory>
+#include <set>
+#include <algorithm>
+
+#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
+#define CATCH_PLATFORM_WINDOWS
+#endif
+
+namespace Catch { namespace clara {
+namespace detail {
+
+    // Traits for extracting arg and return type of lambdas (for single argument lambdas)
+    template<typename L>
+    struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {};
+
+    template<typename ClassT, typename ReturnT, typename... Args>
+    struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> {
+        static const bool isValid = false;
+    };
+
+    template<typename ClassT, typename ReturnT, typename ArgT>
+    struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
+        static const bool isValid = true;
+        using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;
+        using ReturnType = ReturnT;
+    };
+
+    class TokenStream;
+
+    // Transport for raw args (copied from main args, or supplied via init list for testing)
+    class Args {
+        friend TokenStream;
+        std::string m_exeName;
+        std::vector<std::string> m_args;
+
+    public:
+        Args( int argc, char const* const* argv )
+            : m_exeName(argv[0]),
+              m_args(argv + 1, argv + argc) {}
+
+        Args( std::initializer_list<std::string> args )
+        :   m_exeName( *args.begin() ),
+            m_args( args.begin()+1, args.end() )
+        {}
+
+        auto exeName() const -> std::string {
+            return m_exeName;
+        }
+    };
+
+    // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string
+    // may encode an option + its argument if the : or = form is used
+    enum class TokenType {
+        Option, Argument
+    };
+    struct Token {
+        TokenType type;
+        std::string token;
+    };
+
+    inline auto isOptPrefix( char c ) -> bool {
+        return c == '-'
+#ifdef CATCH_PLATFORM_WINDOWS
+            || c == '/'
+#endif
+        ;
+    }
+
+    // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled
+    class TokenStream {
+        using Iterator = std::vector<std::string>::const_iterator;
+        Iterator it;
+        Iterator itEnd;
+        std::vector<Token> m_tokenBuffer;
+
+        void loadBuffer() {
+            m_tokenBuffer.resize( 0 );
+
+            // Skip any empty strings
+            while( it != itEnd && it->empty() )
+                ++it;
+
+            if( it != itEnd ) {
+                auto const &next = *it;
+                if( isOptPrefix( next[0] ) ) {
+                    auto delimiterPos = next.find_first_of( " :=" );
+                    if( delimiterPos != std::string::npos ) {
+                        m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
+                        m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } );
+                    } else {
+                        if( next[1] != '-' && next.size() > 2 ) {
+                            std::string opt = "- ";
+                            for( size_t i = 1; i < next.size(); ++i ) {
+                                opt[1] = next[i];
+                                m_tokenBuffer.push_back( { TokenType::Option, opt } );
+                            }
+                        } else {
+                            m_tokenBuffer.push_back( { TokenType::Option, next } );
+                        }
+                    }
+                } else {
+                    m_tokenBuffer.push_back( { TokenType::Argument, next } );
+                }
+            }
+        }
+
+    public:
+        explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {}
+
+        TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) {
+            loadBuffer();
+        }
+
+        explicit operator bool() const {
+            return !m_tokenBuffer.empty() || it != itEnd;
+        }
+
+        auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); }
+
+        auto operator*() const -> Token {
+            assert( !m_tokenBuffer.empty() );
+            return m_tokenBuffer.front();
+        }
+
+        auto operator->() const -> Token const * {
+            assert( !m_tokenBuffer.empty() );
+            return &m_tokenBuffer.front();
+        }
+
+        auto operator++() -> TokenStream & {
+            if( m_tokenBuffer.size() >= 2 ) {
+                m_tokenBuffer.erase( m_tokenBuffer.begin() );
+            } else {
+                if( it != itEnd )
+                    ++it;
+                loadBuffer();
+            }
+            return *this;
+        }
+    };
+
+    class ResultBase {
+    public:
+        enum Type {
+            Ok, LogicError, RuntimeError
+        };
+
+    protected:
+        ResultBase( Type type ) : m_type( type ) {}
+        virtual ~ResultBase() = default;
+
+        virtual void enforceOk() const = 0;
+
+        Type m_type;
+    };
+
+    template<typename T>
+    class ResultValueBase : public ResultBase {
+    public:
+        auto value() const -> T const & {
+            enforceOk();
+            return m_value;
+        }
+
+    protected:
+        ResultValueBase( Type type ) : ResultBase( type ) {}
+
+        ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) {
+            if( m_type == ResultBase::Ok )
+                new( &m_value ) T( other.m_value );
+        }
+
+        ResultValueBase( Type, T const &value ) : ResultBase( Ok ) {
+            new( &m_value ) T( value );
+        }
+
+        auto operator=( ResultValueBase const &other ) -> ResultValueBase & {
+            if( m_type == ResultBase::Ok )
+                m_value.~T();
+            ResultBase::operator=(other);
+            if( m_type == ResultBase::Ok )
+                new( &m_value ) T( other.m_value );
+            return *this;
+        }
+
+        ~ResultValueBase() override {
+            if( m_type == Ok )
+                m_value.~T();
+        }
+
+        union {
+            T m_value;
+        };
+    };
+
+    template<>
+    class ResultValueBase<void> : public ResultBase {
+    protected:
+        using ResultBase::ResultBase;
+    };
+
+    template<typename T = void>
+    class BasicResult : public ResultValueBase<T> {
+    public:
+        template<typename U>
+        explicit BasicResult( BasicResult<U> const &other )
+        :   ResultValueBase<T>( other.type() ),
+            m_errorMessage( other.errorMessage() )
+        {
+            assert( type() != ResultBase::Ok );
+        }
+
+        template<typename U>
+        static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
+        static auto ok() -> BasicResult { return { ResultBase::Ok }; }
+        static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
+        static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
+
+        explicit operator bool() const { return m_type == ResultBase::Ok; }
+        auto type() const -> ResultBase::Type { return m_type; }
+        auto errorMessage() const -> std::string { return m_errorMessage; }
+
+    protected:
+        void enforceOk() const override {
+
+            // Errors shouldn't reach this point, but if they do
+            // the actual error message will be in m_errorMessage
+            assert( m_type != ResultBase::LogicError );
+            assert( m_type != ResultBase::RuntimeError );
+            if( m_type != ResultBase::Ok )
+                std::abort();
+        }
+
+        std::string m_errorMessage; // Only populated if resultType is an error
+
+        BasicResult( ResultBase::Type type, std::string const &message )
+        :   ResultValueBase<T>(type),
+            m_errorMessage(message)
+        {
+            assert( m_type != ResultBase::Ok );
+        }
+
+        using ResultValueBase<T>::ResultValueBase;
+        using ResultBase::m_type;
+    };
+
+    enum class ParseResultType {
+        Matched, NoMatch, ShortCircuitAll, ShortCircuitSame
+    };
+
+    class ParseState {
+    public:
+
+        ParseState( ParseResultType type, TokenStream const &remainingTokens )
+        : m_type(type),
+          m_remainingTokens( remainingTokens )
+        {}
+
+        auto type() const -> ParseResultType { return m_type; }
+        auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
+
+    private:
+        ParseResultType m_type;
+        TokenStream m_remainingTokens;
+    };
+
+    using Result = BasicResult<void>;
+    using ParserResult = BasicResult<ParseResultType>;
+    using InternalParseResult = BasicResult<ParseState>;
+
+    struct HelpColumns {
+        std::string left;
+        std::string right;
+    };
+
+    template<typename T>
+    inline auto convertInto( std::string const &source, T& target ) -> ParserResult {
+        std::stringstream ss;
+        ss << source;
+        ss >> target;
+        if( ss.fail() )
+            return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" );
+        else
+            return ParserResult::ok( ParseResultType::Matched );
+    }
+    inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult {
+        target = source;
+        return ParserResult::ok( ParseResultType::Matched );
+    }
+    inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
+        std::string srcLC = source;
+        std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned char c ) { return static_cast<char>( std::tolower(c) ); } );
+        if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")
+            target = true;
+        else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off")
+            target = false;
+        else
+            return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" );
+        return ParserResult::ok( ParseResultType::Matched );
+    }
+#ifdef CLARA_CONFIG_OPTIONAL_TYPE
+    template<typename T>
+    inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult {
+        T temp;
+        auto result = convertInto( source, temp );
+        if( result )
+            target = std::move(temp);
+        return result;
+    }
+#endif // CLARA_CONFIG_OPTIONAL_TYPE
+
+    struct NonCopyable {
+        NonCopyable() = default;
+        NonCopyable( NonCopyable const & ) = delete;
+        NonCopyable( NonCopyable && ) = delete;
+        NonCopyable &operator=( NonCopyable const & ) = delete;
+        NonCopyable &operator=( NonCopyable && ) = delete;
+    };
+
+    struct BoundRef : NonCopyable {
+        virtual ~BoundRef() = default;
+        virtual auto isContainer() const -> bool { return false; }
+        virtual auto isFlag() const -> bool { return false; }
+    };
+    struct BoundValueRefBase : BoundRef {
+        virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
+    };
+    struct BoundFlagRefBase : BoundRef {
+        virtual auto setFlag( bool flag ) -> ParserResult = 0;
+        virtual auto isFlag() const -> bool { return true; }
+    };
+
+    template<typename T>
+    struct BoundValueRef : BoundValueRefBase {
+        T &m_ref;
+
+        explicit BoundValueRef( T &ref ) : m_ref( ref ) {}
+
+        auto setValue( std::string const &arg ) -> ParserResult override {
+            return convertInto( arg, m_ref );
+        }
+    };
+
+    template<typename T>
+    struct BoundValueRef<std::vector<T>> : BoundValueRefBase {
+        std::vector<T> &m_ref;
+
+        explicit BoundValueRef( std::vector<T> &ref ) : m_ref( ref ) {}
+
+        auto isContainer() const -> bool override { return true; }
+
+        auto setValue( std::string const &arg ) -> ParserResult override {
+            T temp;
+            auto result = convertInto( arg, temp );
+            if( result )
+                m_ref.push_back( temp );
+            return result;
+        }
+    };
+
+    struct BoundFlagRef : BoundFlagRefBase {
+        bool &m_ref;
+
+        explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {}
+
+        auto setFlag( bool flag ) -> ParserResult override {
+            m_ref = flag;
+            return ParserResult::ok( ParseResultType::Matched );
+        }
+    };
+
+    template<typename ReturnType>
+    struct LambdaInvoker {
+        static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" );
+
+        template<typename L, typename ArgType>
+        static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
+            return lambda( arg );
+        }
+    };
+
+    template<>
+    struct LambdaInvoker<void> {
+        template<typename L, typename ArgType>
+        static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
+            lambda( arg );
+            return ParserResult::ok( ParseResultType::Matched );
+        }
+    };
+
+    template<typename ArgType, typename L>
+    inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult {
+        ArgType temp{};
+        auto result = convertInto( arg, temp );
+        return !result
+           ? result
+           : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp );
+    }
+
+    template<typename L>
+    struct BoundLambda : BoundValueRefBase {
+        L m_lambda;
+
+        static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
+        explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {}
+
+        auto setValue( std::string const &arg ) -> ParserResult override {
+            return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg );
+        }
+    };
+
+    template<typename L>
+    struct BoundFlagLambda : BoundFlagRefBase {
+        L m_lambda;
+
+        static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
+        static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType, bool>::value, "flags must be boolean" );
+
+        explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {}
+
+        auto setFlag( bool flag ) -> ParserResult override {
+            return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag );
+        }
+    };
+
+    enum class Optionality { Optional, Required };
+
+    struct Parser;
+
+    class ParserBase {
+    public:
+        virtual ~ParserBase() = default;
+        virtual auto validate() const -> Result { return Result::ok(); }
+        virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult  = 0;
+        virtual auto cardinality() const -> size_t { return 1; }
+
+        auto parse( Args const &args ) const -> InternalParseResult {
+            return parse( args.exeName(), TokenStream( args ) );
+        }
+    };
+
+    template<typename DerivedT>
+    class ComposableParserImpl : public ParserBase {
+    public:
+        template<typename T>
+        auto operator|( T const &other ) const -> Parser;
+
+		template<typename T>
+        auto operator+( T const &other ) const -> Parser;
+    };
+
+    // Common code and state for Args and Opts
+    template<typename DerivedT>
+    class ParserRefImpl : public ComposableParserImpl<DerivedT> {
+    protected:
+        Optionality m_optionality = Optionality::Optional;
+        std::shared_ptr<BoundRef> m_ref;
+        std::string m_hint;
+        std::string m_description;
+
+        explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {}
+
+    public:
+        template<typename T>
+        ParserRefImpl( T &ref, std::string const &hint )
+        :   m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
+            m_hint( hint )
+        {}
+
+        template<typename LambdaT>
+        ParserRefImpl( LambdaT const &ref, std::string const &hint )
+        :   m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
+            m_hint(hint)
+        {}
+
+        auto operator()( std::string const &description ) -> DerivedT & {
+            m_description = description;
+            return static_cast<DerivedT &>( *this );
+        }
+
+        auto optional() -> DerivedT & {
+            m_optionality = Optionality::Optional;
+            return static_cast<DerivedT &>( *this );
+        };
+
+        auto required() -> DerivedT & {
+            m_optionality = Optionality::Required;
+            return static_cast<DerivedT &>( *this );
+        };
+
+        auto isOptional() const -> bool {
+            return m_optionality == Optionality::Optional;
+        }
+
+        auto cardinality() const -> size_t override {
+            if( m_ref->isContainer() )
+                return 0;
+            else
+                return 1;
+        }
+
+        auto hint() const -> std::string { return m_hint; }
+    };
+
+    class ExeName : public ComposableParserImpl<ExeName> {
+        std::shared_ptr<std::string> m_name;
+        std::shared_ptr<BoundValueRefBase> m_ref;
+
+        template<typename LambdaT>
+        static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> {
+            return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
+        }
+
+    public:
+        ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}
+
+        explicit ExeName( std::string &ref ) : ExeName() {
+            m_ref = std::make_shared<BoundValueRef<std::string>>( ref );
+        }
+
+        template<typename LambdaT>
+        explicit ExeName( LambdaT const& lambda ) : ExeName() {
+            m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda );
+        }
+
+        // The exe name is not parsed out of the normal tokens, but is handled specially
+        auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
+            return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
+        }
+
+        auto name() const -> std::string { return *m_name; }
+        auto set( std::string const& newName ) -> ParserResult {
+
+            auto lastSlash = newName.find_last_of( "\\/" );
+            auto filename = ( lastSlash == std::string::npos )
+                    ? newName
+                    : newName.substr( lastSlash+1 );
+
+            *m_name = filename;
+            if( m_ref )
+                return m_ref->setValue( filename );
+            else
+                return ParserResult::ok( ParseResultType::Matched );
+        }
+    };
+
+    class Arg : public ParserRefImpl<Arg> {
+    public:
+        using ParserRefImpl::ParserRefImpl;
+
+        auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override {
+            auto validationResult = validate();
+            if( !validationResult )
+                return InternalParseResult( validationResult );
+
+            auto remainingTokens = tokens;
+            auto const &token = *remainingTokens;
+            if( token.type != TokenType::Argument )
+                return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
+
+            assert( !m_ref->isFlag() );
+            auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
+
+            auto result = valueRef->setValue( remainingTokens->token );
+            if( !result )
+                return InternalParseResult( result );
+            else
+                return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
+        }
+    };
+
+    inline auto normaliseOpt( std::string const &optName ) -> std::string {
+#ifdef CATCH_PLATFORM_WINDOWS
+        if( optName[0] == '/' )
+            return "-" + optName.substr( 1 );
+        else
+#endif
+            return optName;
+    }
+
+    class Opt : public ParserRefImpl<Opt> {
+    protected:
+        std::vector<std::string> m_optNames;
+
+    public:
+        template<typename LambdaT>
+        explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {}
+
+        explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {}
+
+        template<typename LambdaT>
+        Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
+
+        template<typename T>
+        Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
+
+        auto operator[]( std::string const &optName ) -> Opt & {
+            m_optNames.push_back( optName );
+            return *this;
+        }
+
+        auto getHelpColumns() const -> std::vector<HelpColumns> {
+            std::ostringstream oss;
+            bool first = true;
+            for( auto const &opt : m_optNames ) {
+                if (first)
+                    first = false;
+                else
+                    oss << ", ";
+                oss << opt;
+            }
+            if( !m_hint.empty() )
+                oss << " <" << m_hint << ">";
+            return { { oss.str(), m_description } };
+        }
+
+        auto isMatch( std::string const &optToken ) const -> bool {
+            auto normalisedToken = normaliseOpt( optToken );
+            for( auto const &name : m_optNames ) {
+                if( normaliseOpt( name ) == normalisedToken )
+                    return true;
+            }
+            return false;
+        }
+
+        using ParserBase::parse;
+
+        auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
+            auto validationResult = validate();
+            if( !validationResult )
+                return InternalParseResult( validationResult );
+
+            auto remainingTokens = tokens;
+            if( remainingTokens && remainingTokens->type == TokenType::Option ) {
+                auto const &token = *remainingTokens;
+                if( isMatch(token.token ) ) {
+                    if( m_ref->isFlag() ) {
+                        auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() );
+                        auto result = flagRef->setFlag( true );
+                        if( !result )
+                            return InternalParseResult( result );
+                        if( result.value() == ParseResultType::ShortCircuitAll )
+                            return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
+                    } else {
+                        auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
+                        ++remainingTokens;
+                        if( !remainingTokens )
+                            return InternalParseResult::runtimeError( "Expected argument following " + token.token );
+                        auto const &argToken = *remainingTokens;
+                        if( argToken.type != TokenType::Argument )
+                            return InternalParseResult::runtimeError( "Expected argument following " + token.token );
+                        auto result = valueRef->setValue( argToken.token );
+                        if( !result )
+                            return InternalParseResult( result );
+                        if( result.value() == ParseResultType::ShortCircuitAll )
+                            return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
+                    }
+                    return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
+                }
+            }
+            return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
+        }
+
+        auto validate() const -> Result override {
+            if( m_optNames.empty() )
+                return Result::logicError( "No options supplied to Opt" );
+            for( auto const &name : m_optNames ) {
+                if( name.empty() )
+                    return Result::logicError( "Option name cannot be empty" );
+#ifdef CATCH_PLATFORM_WINDOWS
+                if( name[0] != '-' && name[0] != '/' )
+                    return Result::logicError( "Option name must begin with '-' or '/'" );
+#else
+                if( name[0] != '-' )
+                    return Result::logicError( "Option name must begin with '-'" );
+#endif
+            }
+            return ParserRefImpl::validate();
+        }
+    };
+
+    struct Help : Opt {
+        Help( bool &showHelpFlag )
+        :   Opt([&]( bool flag ) {
+                showHelpFlag = flag;
+                return ParserResult::ok( ParseResultType::ShortCircuitAll );
+            })
+        {
+            static_cast<Opt &>( *this )
+                    ("display usage information")
+                    ["-?"]["-h"]["--help"]
+                    .optional();
+        }
+    };
+
+    struct Parser : ParserBase {
+
+        mutable ExeName m_exeName;
+        std::vector<Opt> m_options;
+        std::vector<Arg> m_args;
+
+        auto operator|=( ExeName const &exeName ) -> Parser & {
+            m_exeName = exeName;
+            return *this;
+        }
+
+        auto operator|=( Arg const &arg ) -> Parser & {
+            m_args.push_back(arg);
+            return *this;
+        }
+
+        auto operator|=( Opt const &opt ) -> Parser & {
+            m_options.push_back(opt);
+            return *this;
+        }
+
+        auto operator|=( Parser const &other ) -> Parser & {
+            m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
+            m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
+            return *this;
+        }
+
+        template<typename T>
+        auto operator|( T const &other ) const -> Parser {
+            return Parser( *this ) |= other;
+        }
+
+        // Forward deprecated interface with '+' instead of '|'
+        template<typename T>
+        auto operator+=( T const &other ) -> Parser & { return operator|=( other ); }
+        template<typename T>
+        auto operator+( T const &other ) const -> Parser { return operator|( other ); }
+
+        auto getHelpColumns() const -> std::vector<HelpColumns> {
+            std::vector<HelpColumns> cols;
+            for (auto const &o : m_options) {
+                auto childCols = o.getHelpColumns();
+                cols.insert( cols.end(), childCols.begin(), childCols.end() );
+            }
+            return cols;
+        }
+
+        void writeToStream( std::ostream &os ) const {
+            if (!m_exeName.name().empty()) {
+                os << "usage:\n" << "  " << m_exeName.name() << " ";
+                bool required = true, first = true;
+                for( auto const &arg : m_args ) {
+                    if (first)
+                        first = false;
+                    else
+                        os << " ";
+                    if( arg.isOptional() && required ) {
+                        os << "[";
+                        required = false;
+                    }
+                    os << "<" << arg.hint() << ">";
+                    if( arg.cardinality() == 0 )
+                        os << " ... ";
+                }
+                if( !required )
+                    os << "]";
+                if( !m_options.empty() )
+                    os << " options";
+                os << "\n\nwhere options are:" << std::endl;
+            }
+
+            auto rows = getHelpColumns();
+            size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH;
+            size_t optWidth = 0;
+            for( auto const &cols : rows )
+                optWidth = (std::max)(optWidth, cols.left.size() + 2);
+
+            optWidth = (std::min)(optWidth, consoleWidth/2);
+
+            for( auto const &cols : rows ) {
+                auto row =
+                        TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
+                        TextFlow::Spacer(4) +
+                        TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
+                os << row << std::endl;
+            }
+        }
+
+        friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& {
+            parser.writeToStream( os );
+            return os;
+        }
+
+        auto validate() const -> Result override {
+            for( auto const &opt : m_options ) {
+                auto result = opt.validate();
+                if( !result )
+                    return result;
+            }
+            for( auto const &arg : m_args ) {
+                auto result = arg.validate();
+                if( !result )
+                    return result;
+            }
+            return Result::ok();
+        }
+
+        using ParserBase::parse;
+
+        auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
+
+            struct ParserInfo {
+                ParserBase const* parser = nullptr;
+                size_t count = 0;
+            };
+            const size_t totalParsers = m_options.size() + m_args.size();
+            assert( totalParsers < 512 );
+            // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do
+            ParserInfo parseInfos[512];
+
+            {
+                size_t i = 0;
+                for (auto const &opt : m_options) parseInfos[i++].parser = &opt;
+                for (auto const &arg : m_args) parseInfos[i++].parser = &arg;
+            }
+
+            m_exeName.set( exeName );
+
+            auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
+            while( result.value().remainingTokens() ) {
+                bool tokenParsed = false;
+
+                for( size_t i = 0; i < totalParsers; ++i ) {
+                    auto&  parseInfo = parseInfos[i];
+                    if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
+                        result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
+                        if (!result)
+                            return result;
+                        if (result.value().type() != ParseResultType::NoMatch) {
+                            tokenParsed = true;
+                            ++parseInfo.count;
+                            break;
+                        }
+                    }
+                }
+
+                if( result.value().type() == ParseResultType::ShortCircuitAll )
+                    return result;
+                if( !tokenParsed )
+                    return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
+            }
+            // !TBD Check missing required options
+            return result;
+        }
+    };
+
+    template<typename DerivedT>
+    template<typename T>
+    auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
+        return Parser() | static_cast<DerivedT const &>( *this ) | other;
+    }
+} // namespace detail
+
+// A Combined parser
+using detail::Parser;
+
+// A parser for options
+using detail::Opt;
+
+// A parser for arguments
+using detail::Arg;
+
+// Wrapper for argc, argv from main()
+using detail::Args;
+
+// Specifies the name of the executable
+using detail::ExeName;
+
+// Convenience wrapper for option parser that specifies the help option
+using detail::Help;
+
+// enum of result types from a parse
+using detail::ParseResultType;
+
+// Result type for parser operation
+using detail::ParserResult;
+
+}} // namespace Catch::clara
+
+// end clara.hpp
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+// end catch_clara.h
+namespace Catch {
+
+    clara::Parser makeCommandLineParser( ConfigData& config );
+
+} // end namespace Catch
+
+// end catch_commandline.h
+#include <fstream>
+#include <ctime>
+
+namespace Catch {
+
+    clara::Parser makeCommandLineParser( ConfigData& config ) {
+
+        using namespace clara;
+
+        auto const setWarning = [&]( std::string const& warning ) {
+                auto warningSet = [&]() {
+                    if( warning == "NoAssertions" )
+                        return WarnAbout::NoAssertions;
+
+                    if ( warning == "NoTests" )
+                        return WarnAbout::NoTests;
+
+                    return WarnAbout::Nothing;
+                }();
+
+                if (warningSet == WarnAbout::Nothing)
+                    return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" );
+                config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet );
+                return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const loadTestNamesFromFile = [&]( std::string const& filename ) {
+                std::ifstream f( filename.c_str() );
+                if( !f.is_open() )
+                    return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" );
+
+                std::string line;
+                while( std::getline( f, line ) ) {
+                    line = trim(line);
+                    if( !line.empty() && !startsWith( line, '#' ) ) {
+                        if( !startsWith( line, '"' ) )
+                            line = '"' + line + '"';
+                        config.testsOrTags.push_back( line );
+                        config.testsOrTags.emplace_back( "," );
+                    }
+                }
+                //Remove comma in the end
+                if(!config.testsOrTags.empty())
+                    config.testsOrTags.erase( config.testsOrTags.end()-1 );
+
+                return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setTestOrder = [&]( std::string const& order ) {
+                if( startsWith( "declared", order ) )
+                    config.runOrder = RunTests::InDeclarationOrder;
+                else if( startsWith( "lexical", order ) )
+                    config.runOrder = RunTests::InLexicographicalOrder;
+                else if( startsWith( "random", order ) )
+                    config.runOrder = RunTests::InRandomOrder;
+                else
+                    return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" );
+                return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setRngSeed = [&]( std::string const& seed ) {
+                if( seed != "time" )
+                    return clara::detail::convertInto( seed, config.rngSeed );
+                config.rngSeed = static_cast<unsigned int>( std::time(nullptr) );
+                return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setColourUsage = [&]( std::string const& useColour ) {
+                    auto mode = toLower( useColour );
+
+                    if( mode == "yes" )
+                        config.useColour = UseColour::Yes;
+                    else if( mode == "no" )
+                        config.useColour = UseColour::No;
+                    else if( mode == "auto" )
+                        config.useColour = UseColour::Auto;
+                    else
+                        return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" );
+                return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setWaitForKeypress = [&]( std::string const& keypress ) {
+                auto keypressLc = toLower( keypress );
+                if (keypressLc == "never")
+                    config.waitForKeypress = WaitForKeypress::Never;
+                else if( keypressLc == "start" )
+                    config.waitForKeypress = WaitForKeypress::BeforeStart;
+                else if( keypressLc == "exit" )
+                    config.waitForKeypress = WaitForKeypress::BeforeExit;
+                else if( keypressLc == "both" )
+                    config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
+                else
+                    return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" );
+            return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setVerbosity = [&]( std::string const& verbosity ) {
+            auto lcVerbosity = toLower( verbosity );
+            if( lcVerbosity == "quiet" )
+                config.verbosity = Verbosity::Quiet;
+            else if( lcVerbosity == "normal" )
+                config.verbosity = Verbosity::Normal;
+            else if( lcVerbosity == "high" )
+                config.verbosity = Verbosity::High;
+            else
+                return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" );
+            return ParserResult::ok( ParseResultType::Matched );
+        };
+        auto const setReporter = [&]( std::string const& reporter ) {
+            IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+
+            auto lcReporter = toLower( reporter );
+            auto result = factories.find( lcReporter );
+
+            if( factories.end() != result )
+                config.reporterName = lcReporter;
+            else
+                return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" );
+            return ParserResult::ok( ParseResultType::Matched );
+        };
+
+        auto cli
+            = ExeName( config.processName )
+            | Help( config.showHelp )
+            | Opt( config.listTests )
+                ["-l"]["--list-tests"]
+                ( "list all/matching test cases" )
+            | Opt( config.listTags )
+                ["-t"]["--list-tags"]
+                ( "list all/matching tags" )
+            | Opt( config.showSuccessfulTests )
+                ["-s"]["--success"]
+                ( "include successful tests in output" )
+            | Opt( config.shouldDebugBreak )
+                ["-b"]["--break"]
+                ( "break into debugger on failure" )
+            | Opt( config.noThrow )
+                ["-e"]["--nothrow"]
+                ( "skip exception tests" )
+            | Opt( config.showInvisibles )
+                ["-i"]["--invisibles"]
+                ( "show invisibles (tabs, newlines)" )
+            | Opt( config.outputFilename, "filename" )
+                ["-o"]["--out"]
+                ( "output filename" )
+            | Opt( setReporter, "name" )
+                ["-r"]["--reporter"]
+                ( "reporter to use (defaults to console)" )
+            | Opt( config.name, "name" )
+                ["-n"]["--name"]
+                ( "suite name" )
+            | Opt( [&]( bool ){ config.abortAfter = 1; } )
+                ["-a"]["--abort"]
+                ( "abort at first failure" )
+            | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
+                ["-x"]["--abortx"]
+                ( "abort after x failures" )
+            | Opt( setWarning, "warning name" )
+                ["-w"]["--warn"]
+                ( "enable warnings" )
+            | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
+                ["-d"]["--durations"]
+                ( "show test durations" )
+            | Opt( config.minDuration, "seconds" )
+                ["-D"]["--min-duration"]
+                ( "show test durations for tests taking at least the given number of seconds" )
+            | Opt( loadTestNamesFromFile, "filename" )
+                ["-f"]["--input-file"]
+                ( "load test names to run from a file" )
+            | Opt( config.filenamesAsTags )
+                ["-#"]["--filenames-as-tags"]
+                ( "adds a tag for the filename" )
+            | Opt( config.sectionsToRun, "section name" )
+                ["-c"]["--section"]
+                ( "specify section to run" )
+            | Opt( setVerbosity, "quiet|normal|high" )
+                ["-v"]["--verbosity"]
+                ( "set output verbosity" )
+            | Opt( config.listTestNamesOnly )
+                ["--list-test-names-only"]
+                ( "list all/matching test cases names only" )
+            | Opt( config.listReporters )
+                ["--list-reporters"]
+                ( "list all reporters" )
+            | Opt( setTestOrder, "decl|lex|rand" )
+                ["--order"]
+                ( "test case order (defaults to decl)" )
+            | Opt( setRngSeed, "'time'|number" )
+                ["--rng-seed"]
+                ( "set a specific seed for random numbers" )
+            | Opt( setColourUsage, "yes|no" )
+                ["--use-colour"]
+                ( "should output be colourised" )
+            | Opt( config.libIdentify )
+                ["--libidentify"]
+                ( "report name and version according to libidentify standard" )
+            | Opt( setWaitForKeypress, "never|start|exit|both" )
+                ["--wait-for-keypress"]
+                ( "waits for a keypress before exiting" )
+            | Opt( config.benchmarkSamples, "samples" )
+                ["--benchmark-samples"]
+                ( "number of samples to collect (default: 100)" )
+            | Opt( config.benchmarkResamples, "resamples" )
+                ["--benchmark-resamples"]
+                ( "number of resamples for the bootstrap (default: 100000)" )
+            | Opt( config.benchmarkConfidenceInterval, "confidence interval" )
+                ["--benchmark-confidence-interval"]
+                ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" )
+            | Opt( config.benchmarkNoAnalysis )
+                ["--benchmark-no-analysis"]
+                ( "perform only measurements; do not perform any analysis" )
+            | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" )
+                ["--benchmark-warmup-time"]
+                ( "amount of time in milliseconds spent on warming up each test (default: 100)" )
+            | Arg( config.testsOrTags, "test name|pattern|tags" )
+                ( "which test or tests to use" );
+
+        return cli;
+    }
+
+} // end namespace Catch
+// end catch_commandline.cpp
+// start catch_common.cpp
+
+#include <cstring>
+#include <ostream>
+
+namespace Catch {
+
+    bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
+        return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+    }
+    bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept {
+        // We can assume that the same file will usually have the same pointer.
+        // Thus, if the pointers are the same, there is no point in calling the strcmp
+        return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0));
+    }
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+        os << info.file << '(' << info.line << ')';
+#else
+        os << info.file << ':' << info.line;
+#endif
+        return os;
+    }
+
+    std::string StreamEndStop::operator+() const {
+        return std::string();
+    }
+
+    NonCopyable::NonCopyable() = default;
+    NonCopyable::~NonCopyable() = default;
+
+}
+// end catch_common.cpp
+// start catch_config.cpp
+
+namespace Catch {
+
+    Config::Config( ConfigData const& data )
+    :   m_data( data ),
+        m_stream( openStream() )
+    {
+        // We need to trim filter specs to avoid trouble with superfluous
+        // whitespace (esp. important for bdd macros, as those are manually
+        // aligned with whitespace).
+
+        for (auto& elem : m_data.testsOrTags) {
+            elem = trim(elem);
+        }
+        for (auto& elem : m_data.sectionsToRun) {
+            elem = trim(elem);
+        }
+
+        TestSpecParser parser(ITagAliasRegistry::get());
+        if (!m_data.testsOrTags.empty()) {
+            m_hasTestFilters = true;
+            for (auto const& testOrTags : m_data.testsOrTags) {
+                parser.parse(testOrTags);
+            }
+        }
+        m_testSpec = parser.testSpec();
+    }
+
+    std::string const& Config::getFilename() const {
+        return m_data.outputFilename ;
+    }
+
+    bool Config::listTests() const          { return m_data.listTests; }
+    bool Config::listTestNamesOnly() const  { return m_data.listTestNamesOnly; }
+    bool Config::listTags() const           { return m_data.listTags; }
+    bool Config::listReporters() const      { return m_data.listReporters; }
+
+    std::string Config::getProcessName() const { return m_data.processName; }
+    std::string const& Config::getReporterName() const { return m_data.reporterName; }
+
+    std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
+    std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
+
+    TestSpec const& Config::testSpec() const { return m_testSpec; }
+    bool Config::hasTestFilters() const { return m_hasTestFilters; }
+
+    bool Config::showHelp() const { return m_data.showHelp; }
+
+    // IConfig interface
+    bool Config::allowThrows() const                   { return !m_data.noThrow; }
+    std::ostream& Config::stream() const               { return m_stream->stream(); }
+    std::string Config::name() const                   { return m_data.name.empty() ? m_data.processName : m_data.name; }
+    bool Config::includeSuccessfulResults() const      { return m_data.showSuccessfulTests; }
+    bool Config::warnAboutMissingAssertions() const    { return !!(m_data.warnings & WarnAbout::NoAssertions); }
+    bool Config::warnAboutNoTests() const              { return !!(m_data.warnings & WarnAbout::NoTests); }
+    ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; }
+    double Config::minDuration() const                 { return m_data.minDuration; }
+    RunTests::InWhatOrder Config::runOrder() const     { return m_data.runOrder; }
+    unsigned int Config::rngSeed() const               { return m_data.rngSeed; }
+    UseColour::YesOrNo Config::useColour() const       { return m_data.useColour; }
+    bool Config::shouldDebugBreak() const              { return m_data.shouldDebugBreak; }
+    int Config::abortAfter() const                     { return m_data.abortAfter; }
+    bool Config::showInvisibles() const                { return m_data.showInvisibles; }
+    Verbosity Config::verbosity() const                { return m_data.verbosity; }
+
+    bool Config::benchmarkNoAnalysis() const                      { return m_data.benchmarkNoAnalysis; }
+    int Config::benchmarkSamples() const                          { return m_data.benchmarkSamples; }
+    double Config::benchmarkConfidenceInterval() const            { return m_data.benchmarkConfidenceInterval; }
+    unsigned int Config::benchmarkResamples() const               { return m_data.benchmarkResamples; }
+    std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); }
+
+    IStream const* Config::openStream() {
+        return Catch::makeStream(m_data.outputFilename);
+    }
+
+} // end namespace Catch
+// end catch_config.cpp
+// start catch_console_colour.cpp
+
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+
+// start catch_errno_guard.h
+
+namespace Catch {
+
+    class ErrnoGuard {
+    public:
+        ErrnoGuard();
+        ~ErrnoGuard();
+    private:
+        int m_oldErrno;
+    };
+
+}
+
+// end catch_errno_guard.h
+// start catch_windows_h_proxy.h
+
+
+#if defined(CATCH_PLATFORM_WINDOWS)
+
+#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+#  define CATCH_DEFINED_NOMINMAX
+#  define NOMINMAX
+#endif
+#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+#  define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
+#  define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef CATCH_DEFINED_NOMINMAX
+#  undef NOMINMAX
+#endif
+#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
+#  undef WIN32_LEAN_AND_MEAN
+#endif
+
+#endif // defined(CATCH_PLATFORM_WINDOWS)
+
+// end catch_windows_h_proxy.h
+#include <sstream>
+
+namespace Catch {
+    namespace {
+
+        struct IColourImpl {
+            virtual ~IColourImpl() = default;
+            virtual void use( Colour::Code _colourCode ) = 0;
+        };
+
+        struct NoColourImpl : IColourImpl {
+            void use( Colour::Code ) override {}
+
+            static IColourImpl* instance() {
+                static NoColourImpl s_instance;
+                return &s_instance;
+            }
+        };
+
+    } // anon namespace
+} // namespace Catch
+
+#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
+#   ifdef CATCH_PLATFORM_WINDOWS
+#       define CATCH_CONFIG_COLOUR_WINDOWS
+#   else
+#       define CATCH_CONFIG_COLOUR_ANSI
+#   endif
+#endif
+
+#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+
+namespace Catch {
+namespace {
+
+    class Win32ColourImpl : public IColourImpl {
+    public:
+        Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+        {
+            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+            GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+            originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+            originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+        }
+
+        void use( Colour::Code _colourCode ) override {
+            switch( _colourCode ) {
+                case Colour::None:      return setTextAttribute( originalForegroundAttributes );
+                case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+                case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
+                case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
+                case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
+                case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+                case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+                case Colour::Grey:      return setTextAttribute( 0 );
+
+                case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
+                case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+                case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+                case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+                case Colour::BrightYellow:  return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN );
+
+                case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
+
+                default:
+                    CATCH_ERROR( "Unknown colour requested" );
+            }
+        }
+
+    private:
+        void setTextAttribute( WORD _textAttribute ) {
+            SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
+        }
+        HANDLE stdoutHandle;
+        WORD originalForegroundAttributes;
+        WORD originalBackgroundAttributes;
+    };
+
+    IColourImpl* platformColourInstance() {
+        static Win32ColourImpl s_instance;
+
+        IConfigPtr config = getCurrentContext().getConfig();
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = UseColour::Yes;
+        return colourMode == UseColour::Yes
+            ? &s_instance
+            : NoColourImpl::instance();
+    }
+
+} // end anon namespace
+} // end namespace Catch
+
+#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+    // use POSIX/ ANSI console terminal codes
+    // Thanks to Adam Strzelecki for original contribution
+    // (http://github.com/nanoant)
+    // https://github.com/philsquared/Catch/pull/131
+    class PosixColourImpl : public IColourImpl {
+    public:
+        void use( Colour::Code _colourCode ) override {
+            switch( _colourCode ) {
+                case Colour::None:
+                case Colour::White:     return setColour( "[0m" );
+                case Colour::Red:       return setColour( "[0;31m" );
+                case Colour::Green:     return setColour( "[0;32m" );
+                case Colour::Blue:      return setColour( "[0;34m" );
+                case Colour::Cyan:      return setColour( "[0;36m" );
+                case Colour::Yellow:    return setColour( "[0;33m" );
+                case Colour::Grey:      return setColour( "[1;30m" );
+
+                case Colour::LightGrey:     return setColour( "[0;37m" );
+                case Colour::BrightRed:     return setColour( "[1;31m" );
+                case Colour::BrightGreen:   return setColour( "[1;32m" );
+                case Colour::BrightWhite:   return setColour( "[1;37m" );
+                case Colour::BrightYellow:  return setColour( "[1;33m" );
+
+                case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
+                default: CATCH_INTERNAL_ERROR( "Unknown colour requested" );
+            }
+        }
+        static IColourImpl* instance() {
+            static PosixColourImpl s_instance;
+            return &s_instance;
+        }
+
+    private:
+        void setColour( const char* _escapeCode ) {
+            getCurrentContext().getConfig()->stream()
+                << '\033' << _escapeCode;
+        }
+    };
+
+    bool useColourOnPlatform() {
+        return
+#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+            !isDebuggerActive() &&
+#endif
+#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
+            isatty(STDOUT_FILENO)
+#else
+            false
+#endif
+            ;
+    }
+    IColourImpl* platformColourInstance() {
+        ErrnoGuard guard;
+        IConfigPtr config = getCurrentContext().getConfig();
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = useColourOnPlatform()
+                ? UseColour::Yes
+                : UseColour::No;
+        return colourMode == UseColour::Yes
+            ? PosixColourImpl::instance()
+            : NoColourImpl::instance();
+    }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else  // not Windows or ANSI ///////////////////////////////////////////////
+
+namespace Catch {
+
+    static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
+
+} // end namespace Catch
+
+#endif // Windows/ ANSI/ None
+
+namespace Catch {
+
+    Colour::Colour( Code _colourCode ) { use( _colourCode ); }
+    Colour::Colour( Colour&& other ) noexcept {
+        m_moved = other.m_moved;
+        other.m_moved = true;
+    }
+    Colour& Colour::operator=( Colour&& other ) noexcept {
+        m_moved = other.m_moved;
+        other.m_moved  = true;
+        return *this;
+    }
+
+    Colour::~Colour(){ if( !m_moved ) use( None ); }
+
+    void Colour::use( Code _colourCode ) {
+        static IColourImpl* impl = platformColourInstance();
+        // Strictly speaking, this cannot possibly happen.
+        // However, under some conditions it does happen (see #1626),
+        // and this change is small enough that we can let practicality
+        // triumph over purity in this case.
+        if (impl != nullptr) {
+            impl->use( _colourCode );
+        }
+    }
+
+    std::ostream& operator << ( std::ostream& os, Colour const& ) {
+        return os;
+    }
+
+} // end namespace Catch
+
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
+
+// end catch_console_colour.cpp
+// start catch_context.cpp
+
+namespace Catch {
+
+    class Context : public IMutableContext, NonCopyable {
+
+    public: // IContext
+        IResultCapture* getResultCapture() override {
+            return m_resultCapture;
+        }
+        IRunner* getRunner() override {
+            return m_runner;
+        }
+
+        IConfigPtr const& getConfig() const override {
+            return m_config;
+        }
+
+        ~Context() override;
+
+    public: // IMutableContext
+        void setResultCapture( IResultCapture* resultCapture ) override {
+            m_resultCapture = resultCapture;
+        }
+        void setRunner( IRunner* runner ) override {
+            m_runner = runner;
+        }
+        void setConfig( IConfigPtr const& config ) override {
+            m_config = config;
+        }
+
+        friend IMutableContext& getCurrentMutableContext();
+
+    private:
+        IConfigPtr m_config;
+        IRunner* m_runner = nullptr;
+        IResultCapture* m_resultCapture = nullptr;
+    };
+
+    IMutableContext *IMutableContext::currentContext = nullptr;
+
+    void IMutableContext::createContext()
+    {
+        currentContext = new Context();
+    }
+
+    void cleanUpContext() {
+        delete IMutableContext::currentContext;
+        IMutableContext::currentContext = nullptr;
+    }
+    IContext::~IContext() = default;
+    IMutableContext::~IMutableContext() = default;
+    Context::~Context() = default;
+
+    SimplePcg32& rng() {
+        static SimplePcg32 s_rng;
+        return s_rng;
+    }
+
+}
+// end catch_context.cpp
+// start catch_debug_console.cpp
+
+// start catch_debug_console.h
+
+#include <string>
+
+namespace Catch {
+    void writeToDebugConsole( std::string const& text );
+}
+
+// end catch_debug_console.h
+#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
+#include <android/log.h>
+
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
+        }
+    }
+
+#elif defined(CATCH_PLATFORM_WINDOWS)
+
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            ::OutputDebugStringA( text.c_str() );
+        }
+    }
+
+#else
+
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            // !TBD: Need a version for Mac/ XCode and other IDEs
+            Catch::cout() << text;
+        }
+    }
+
+#endif // Platform
+// end catch_debug_console.cpp
+// start catch_debugger.cpp
+
+#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+
+#  include <cassert>
+#  include <sys/types.h>
+#  include <unistd.h>
+#  include <cstddef>
+#  include <ostream>
+
+#ifdef __apple_build_version__
+    // These headers will only compile with AppleClang (XCode)
+    // For other compilers (Clang, GCC, ... ) we need to exclude them
+#  include <sys/sysctl.h>
+#endif
+
+    namespace Catch {
+        #ifdef __apple_build_version__
+        // The following function is taken directly from the following technical note:
+        // https://developer.apple.com/library/archive/qa/qa1361/_index.html
+
+        // Returns true if the current process is being debugged (either
+        // running under the debugger or has a debugger attached post facto).
+        bool isDebuggerActive(){
+            int                 mib[4];
+            struct kinfo_proc   info;
+            std::size_t         size;
+
+            // Initialize the flags so that, if sysctl fails for some bizarre
+            // reason, we get a predictable result.
+
+            info.kp_proc.p_flag = 0;
+
+            // Initialize mib, which tells sysctl the info we want, in this case
+            // we're looking for information about a specific process ID.
+
+            mib[0] = CTL_KERN;
+            mib[1] = KERN_PROC;
+            mib[2] = KERN_PROC_PID;
+            mib[3] = getpid();
+
+            // Call sysctl.
+
+            size = sizeof(info);
+            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) {
+                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+                return false;
+            }
+
+            // We're being debugged if the P_TRACED flag is set.
+
+            return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+        }
+        #else
+        bool isDebuggerActive() {
+            // We need to find another way to determine this for non-appleclang compilers on macOS
+            return false;
+        }
+        #endif
+    } // namespace Catch
+
+#elif defined(CATCH_PLATFORM_LINUX)
+    #include <fstream>
+    #include <string>
+
+    namespace Catch{
+        // The standard POSIX way of detecting a debugger is to attempt to
+        // ptrace() the process, but this needs to be done from a child and not
+        // this process itself to still allow attaching to this process later
+        // if wanted, so is rather heavy. Under Linux we have the PID of the
+        // "debugger" (which doesn't need to be gdb, of course, it could also
+        // be strace, for example) in /proc/$PID/status, so just get it from
+        // there instead.
+        bool isDebuggerActive(){
+            // Libstdc++ has a bug, where std::ifstream sets errno to 0
+            // This way our users can properly assert over errno values
+            ErrnoGuard guard;
+            std::ifstream in("/proc/self/status");
+            for( std::string line; std::getline(in, line); ) {
+                static const int PREFIX_LEN = 11;
+                if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
+                    // We're traced if the PID is not 0 and no other PID starts
+                    // with 0 digit, so it's enough to check for just a single
+                    // character.
+                    return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+                }
+            }
+
+            return false;
+        }
+    } // namespace Catch
+#elif defined(_MSC_VER)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#else
+    namespace Catch {
+       bool isDebuggerActive() { return false; }
+    }
+#endif // Platform
+// end catch_debugger.cpp
+// start catch_decomposer.cpp
+
+namespace Catch {
+
+    ITransientExpression::~ITransientExpression() = default;
+
+    void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) {
+        if( lhs.size() + rhs.size() < 40 &&
+                lhs.find('\n') == std::string::npos &&
+                rhs.find('\n') == std::string::npos )
+            os << lhs << " " << op << " " << rhs;
+        else
+            os << lhs << "\n" << op << "\n" << rhs;
+    }
+}
+// end catch_decomposer.cpp
+// start catch_enforce.cpp
+
+#include <stdexcept>
+
+namespace Catch {
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
+    [[noreturn]]
+    void throw_exception(std::exception const& e) {
+        Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n"
+                      << "The message was: " << e.what() << '\n';
+        std::terminate();
+    }
+#endif
+
+    [[noreturn]]
+    void throw_logic_error(std::string const& msg) {
+        throw_exception(std::logic_error(msg));
+    }
+
+    [[noreturn]]
+    void throw_domain_error(std::string const& msg) {
+        throw_exception(std::domain_error(msg));
+    }
+
+    [[noreturn]]
+    void throw_runtime_error(std::string const& msg) {
+        throw_exception(std::runtime_error(msg));
+    }
+
+} // namespace Catch;
+// end catch_enforce.cpp
+// start catch_enum_values_registry.cpp
+// start catch_enum_values_registry.h
+
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+    namespace Detail {
+
+        std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
+
+        class EnumValuesRegistry : public IMutableEnumValuesRegistry {
+
+            std::vector<std::unique_ptr<EnumInfo>> m_enumInfos;
+
+            EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
+        };
+
+        std::vector<StringRef> parseEnums( StringRef enums );
+
+    } // Detail
+
+} // Catch
+
+// end catch_enum_values_registry.h
+
+#include <map>
+#include <cassert>
+
+namespace Catch {
+
+    IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {}
+
+    namespace Detail {
+
+        namespace {
+            // Extracts the actual name part of an enum instance
+            // In other words, it returns the Blue part of Bikeshed::Colour::Blue
+            StringRef extractInstanceName(StringRef enumInstance) {
+                // Find last occurrence of ":"
+                size_t name_start = enumInstance.size();
+                while (name_start > 0 && enumInstance[name_start - 1] != ':') {
+                    --name_start;
+                }
+                return enumInstance.substr(name_start, enumInstance.size() - name_start);
+            }
+        }
+
+        std::vector<StringRef> parseEnums( StringRef enums ) {
+            auto enumValues = splitStringRef( enums, ',' );
+            std::vector<StringRef> parsed;
+            parsed.reserve( enumValues.size() );
+            for( auto const& enumValue : enumValues ) {
+                parsed.push_back(trim(extractInstanceName(enumValue)));
+            }
+            return parsed;
+        }
+
+        EnumInfo::~EnumInfo() {}
+
+        StringRef EnumInfo::lookup( int value ) const {
+            for( auto const& valueToName : m_values ) {
+                if( valueToName.first == value )
+                    return valueToName.second;
+            }
+            return "{** unexpected enum value **}"_sr;
+        }
+
+        std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
+            std::unique_ptr<EnumInfo> enumInfo( new EnumInfo );
+            enumInfo->m_name = enumName;
+            enumInfo->m_values.reserve( values.size() );
+
+            const auto valueNames = Catch::Detail::parseEnums( allValueNames );
+            assert( valueNames.size() == values.size() );
+            std::size_t i = 0;
+            for( auto value : values )
+                enumInfo->m_values.emplace_back(value, valueNames[i++]);
+
+            return enumInfo;
+        }
+
+        EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
+            m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
+            return *m_enumInfos.back();
+        }
+
+    } // Detail
+} // Catch
+
+// end catch_enum_values_registry.cpp
+// start catch_errno_guard.cpp
+
+#include <cerrno>
+
+namespace Catch {
+        ErrnoGuard::ErrnoGuard():m_oldErrno(errno){}
+        ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; }
+}
+// end catch_errno_guard.cpp
+// start catch_exception_translator_registry.cpp
+
+// start catch_exception_translator_registry.h
+
+#include <vector>
+#include <string>
+#include <memory>
+
+namespace Catch {
+
+    class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+    public:
+        ~ExceptionTranslatorRegistry();
+        virtual void registerTranslator( const IExceptionTranslator* translator );
+        std::string translateActiveException() const override;
+        std::string tryTranslators() const;
+
+    private:
+        std::vector<std::unique_ptr<IExceptionTranslator const>> m_translators;
+    };
+}
+
+// end catch_exception_translator_registry.h
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+    ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
+    }
+
+    void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) {
+        m_translators.push_back( std::unique_ptr<const IExceptionTranslator>( translator ) );
+    }
+
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+    std::string ExceptionTranslatorRegistry::translateActiveException() const {
+        try {
+#ifdef __OBJC__
+            // In Objective-C try objective-c exceptions first
+            @try {
+                return tryTranslators();
+            }
+            @catch (NSException *exception) {
+                return Catch::Detail::stringify( [exception description] );
+            }
+#else
+            // Compiling a mixed mode project with MSVC means that CLR
+            // exceptions will be caught in (...) as well. However, these
+            // do not fill-in std::current_exception and thus lead to crash
+            // when attempting rethrow.
+            // /EHa switch also causes structured exceptions to be caught
+            // here, but they fill-in current_exception properly, so
+            // at worst the output should be a little weird, instead of
+            // causing a crash.
+            if (std::current_exception() == nullptr) {
+                return "Non C++ exception. Possibly a CLR exception.";
+            }
+            return tryTranslators();
+#endif
+        }
+        catch( TestFailureException& ) {
+            std::rethrow_exception(std::current_exception());
+        }
+        catch( std::exception& ex ) {
+            return ex.what();
+        }
+        catch( std::string& msg ) {
+            return msg;
+        }
+        catch( const char* msg ) {
+            return msg;
+        }
+        catch(...) {
+            return "Unknown exception";
+        }
+    }
+
+    std::string ExceptionTranslatorRegistry::tryTranslators() const {
+        if (m_translators.empty()) {
+            std::rethrow_exception(std::current_exception());
+        } else {
+            return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end());
+        }
+    }
+
+#else // ^^ Exceptions are enabled // Exceptions are disabled vv
+    std::string ExceptionTranslatorRegistry::translateActiveException() const {
+        CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+    }
+
+    std::string ExceptionTranslatorRegistry::tryTranslators() const {
+        CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+    }
+#endif
+
+}
+// end catch_exception_translator_registry.cpp
+// start catch_fatal_condition.cpp
+
+#include <algorithm>
+
+#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+namespace Catch {
+
+    // If neither SEH nor signal handling is required, the handler impls
+    // do not have to do anything, and can be empty.
+    void FatalConditionHandler::engage_platform() {}
+    void FatalConditionHandler::disengage_platform() {}
+    FatalConditionHandler::FatalConditionHandler() = default;
+    FatalConditionHandler::~FatalConditionHandler() = default;
+
+} // end namespace Catch
+
+#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS
+
+#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
+#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
+#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS
+
+#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+namespace {
+    //! Signals fatal error message to the run context
+    void reportFatal( char const * const message ) {
+        Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
+    }
+
+    //! Minimal size Catch2 needs for its own fatal error handling.
+    //! Picked anecdotally, so it might not be sufficient on all
+    //! platforms, and for all configurations.
+    constexpr std::size_t minStackSizeForErrors = 32 * 1024;
+} // end unnamed namespace
+
+#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
+
+#if defined( CATCH_CONFIG_WINDOWS_SEH )
+
+namespace Catch {
+
+    struct SignalDefs { DWORD id; const char* name; };
+
+    // There is no 1-1 mapping between signals and windows exceptions.
+    // Windows can easily distinguish between SO and SigSegV,
+    // but SigInt, SigTerm, etc are handled differently.
+    static SignalDefs signalDefs[] = {
+        { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION),  "SIGILL - Illegal instruction signal" },
+        { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" },
+        { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" },
+        { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
+    };
+
+    static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+        for (auto const& def : signalDefs) {
+            if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
+                reportFatal(def.name);
+            }
+        }
+        // If its not an exception we care about, pass it along.
+        // This stops us from eating debugger breaks etc.
+        return EXCEPTION_CONTINUE_SEARCH;
+    }
+
+    // Since we do not support multiple instantiations, we put these
+    // into global variables and rely on cleaning them up in outlined
+    // constructors/destructors
+    static PVOID exceptionHandlerHandle = nullptr;
+
+    // For MSVC, we reserve part of the stack memory for handling
+    // memory overflow structured exception.
+    FatalConditionHandler::FatalConditionHandler() {
+        ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
+        if (!SetThreadStackGuarantee(&guaranteeSize)) {
+            // We do not want to fully error out, because needing
+            // the stack reserve should be rare enough anyway.
+            Catch::cerr()
+                << "Failed to reserve piece of stack."
+                << " Stack overflows will not be reported successfully.";
+        }
+    }
+
+    // We do not attempt to unset the stack guarantee, because
+    // Windows does not support lowering the stack size guarantee.
+    FatalConditionHandler::~FatalConditionHandler() = default;
+
+    void FatalConditionHandler::engage_platform() {
+        // Register as first handler in current chain
+        exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
+        if (!exceptionHandlerHandle) {
+            CATCH_RUNTIME_ERROR("Could not register vectored exception handler");
+        }
+    }
+
+    void FatalConditionHandler::disengage_platform() {
+        if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
+            CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
+        }
+        exceptionHandlerHandle = nullptr;
+    }
+
+} // end namespace Catch
+
+#endif // CATCH_CONFIG_WINDOWS_SEH
+
+#if defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+#include <signal.h>
+
+namespace Catch {
+
+    struct SignalDefs {
+        int id;
+        const char* name;
+    };
+
+    static SignalDefs signalDefs[] = {
+        { SIGINT,  "SIGINT - Terminal interrupt signal" },
+        { SIGILL,  "SIGILL - Illegal instruction signal" },
+        { SIGFPE,  "SIGFPE - Floating point error signal" },
+        { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
+        { SIGTERM, "SIGTERM - Termination request signal" },
+        { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+    };
+
+// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
+// which is zero initialization, but not explicit. We want to avoid
+// that.
+#if defined(__GNUC__)
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+    static char* altStackMem = nullptr;
+    static std::size_t altStackSize = 0;
+    static stack_t oldSigStack{};
+    static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
+
+    static void restorePreviousSignalHandlers() {
+        // We set signal handlers back to the previous ones. Hopefully
+        // nobody overwrote them in the meantime, and doesn't expect
+        // their signal handlers to live past ours given that they
+        // installed them after ours..
+        for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+            sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
+        }
+        // Return the old stack
+        sigaltstack(&oldSigStack, nullptr);
+    }
+
+    static void handleSignal( int sig ) {
+        char const * name = "<unknown signal>";
+        for (auto const& def : signalDefs) {
+            if (sig == def.id) {
+                name = def.name;
+                break;
+            }
+        }
+        // We need to restore previous signal handlers and let them do
+        // their thing, so that the users can have the debugger break
+        // when a signal is raised, and so on.
+        restorePreviousSignalHandlers();
+        reportFatal( name );
+        raise( sig );
+    }
+
+    FatalConditionHandler::FatalConditionHandler() {
+        assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
+        if (altStackSize == 0) {
+            altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
+        }
+        altStackMem = new char[altStackSize]();
+    }
+
+    FatalConditionHandler::~FatalConditionHandler() {
+        delete[] altStackMem;
+        // We signal that another instance can be constructed by zeroing
+        // out the pointer.
+        altStackMem = nullptr;
+    }
+
+    void FatalConditionHandler::engage_platform() {
+        stack_t sigStack;
+        sigStack.ss_sp = altStackMem;
+        sigStack.ss_size = altStackSize;
+        sigStack.ss_flags = 0;
+        sigaltstack(&sigStack, &oldSigStack);
+        struct sigaction sa = { };
+
+        sa.sa_handler = handleSignal;
+        sa.sa_flags = SA_ONSTACK;
+        for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
+            sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+        }
+    }
+
+#if defined(__GNUC__)
+#    pragma GCC diagnostic pop
+#endif
+
+    void FatalConditionHandler::disengage_platform() {
+        restorePreviousSignalHandlers();
+    }
+
+} // end namespace Catch
+
+#endif // CATCH_CONFIG_POSIX_SIGNALS
+// end catch_fatal_condition.cpp
+// start catch_generators.cpp
+
+#include <limits>
+#include <set>
+
+namespace Catch {
+
+IGeneratorTracker::~IGeneratorTracker() {}
+
+const char* GeneratorException::what() const noexcept {
+    return m_msg;
+}
+
+namespace Generators {
+
+    GeneratorUntypedBase::~GeneratorUntypedBase() {}
+
+    auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+        return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
+    }
+
+} // namespace Generators
+} // namespace Catch
+// end catch_generators.cpp
+// start catch_interfaces_capture.cpp
+
+namespace Catch {
+    IResultCapture::~IResultCapture() = default;
+}
+// end catch_interfaces_capture.cpp
+// start catch_interfaces_config.cpp
+
+namespace Catch {
+    IConfig::~IConfig() = default;
+}
+// end catch_interfaces_config.cpp
+// start catch_interfaces_exception.cpp
+
+namespace Catch {
+    IExceptionTranslator::~IExceptionTranslator() = default;
+    IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default;
+}
+// end catch_interfaces_exception.cpp
+// start catch_interfaces_registry_hub.cpp
+
+namespace Catch {
+    IRegistryHub::~IRegistryHub() = default;
+    IMutableRegistryHub::~IMutableRegistryHub() = default;
+}
+// end catch_interfaces_registry_hub.cpp
+// start catch_interfaces_reporter.cpp
+
+// start catch_reporter_listening.h
+
+namespace Catch {
+
+    class ListeningReporter : public IStreamingReporter {
+        using Reporters = std::vector<IStreamingReporterPtr>;
+        Reporters m_listeners;
+        IStreamingReporterPtr m_reporter = nullptr;
+        ReporterPreferences m_preferences;
+
+    public:
+        ListeningReporter();
+
+        void addListener( IStreamingReporterPtr&& listener );
+        void addReporter( IStreamingReporterPtr&& reporter );
+
+    public: // IStreamingReporter
+
+        ReporterPreferences getPreferences() const override;
+
+        void noMatchingTestCases( std::string const& spec ) override;
+
+        void reportInvalidArguments(std::string const&arg) override;
+
+        static std::set<Verbosity> getSupportedVerbosities();
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+        void benchmarkPreparing(std::string const& name) override;
+        void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
+        void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
+        void benchmarkFailed(std::string const&) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+        void testRunStarting( TestRunInfo const& testRunInfo ) override;
+        void testGroupStarting( GroupInfo const& groupInfo ) override;
+        void testCaseStarting( TestCaseInfo const& testInfo ) override;
+        void sectionStarting( SectionInfo const& sectionInfo ) override;
+        void assertionStarting( AssertionInfo const& assertionInfo ) override;
+
+        // The return value indicates if the messages buffer should be cleared:
+        bool assertionEnded( AssertionStats const& assertionStats ) override;
+        void sectionEnded( SectionStats const& sectionStats ) override;
+        void testCaseEnded( TestCaseStats const& testCaseStats ) override;
+        void testGroupEnded( TestGroupStats const& testGroupStats ) override;
+        void testRunEnded( TestRunStats const& testRunStats ) override;
+
+        void skipTest( TestCaseInfo const& testInfo ) override;
+        bool isMulti() const override;
+
+    };
+
+} // end namespace Catch
+
+// end catch_reporter_listening.h
+namespace Catch {
+
+    ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig )
+    :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+    ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream )
+    :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+    std::ostream& ReporterConfig::stream() const { return *m_stream; }
+    IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; }
+
+    TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {}
+
+    GroupInfo::GroupInfo(  std::string const& _name,
+                           std::size_t _groupIndex,
+                           std::size_t _groupsCount )
+    :   name( _name ),
+        groupIndex( _groupIndex ),
+        groupsCounts( _groupsCount )
+    {}
+
+     AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
+                                     std::vector<MessageInfo> const& _infoMessages,
+                                     Totals const& _totals )
+    :   assertionResult( _assertionResult ),
+        infoMessages( _infoMessages ),
+        totals( _totals )
+    {
+        assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression;
+
+        if( assertionResult.hasMessage() ) {
+            // Copy message into messages list.
+            // !TBD This should have been done earlier, somewhere
+            MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+            builder << assertionResult.getMessage();
+            builder.m_info.message = builder.m_stream.str();
+
+            infoMessages.push_back( builder.m_info );
+        }
+    }
+
+     AssertionStats::~AssertionStats() = default;
+
+    SectionStats::SectionStats(  SectionInfo const& _sectionInfo,
+                                 Counts const& _assertions,
+                                 double _durationInSeconds,
+                                 bool _missingAssertions )
+    :   sectionInfo( _sectionInfo ),
+        assertions( _assertions ),
+        durationInSeconds( _durationInSeconds ),
+        missingAssertions( _missingAssertions )
+    {}
+
+    SectionStats::~SectionStats() = default;
+
+    TestCaseStats::TestCaseStats(  TestCaseInfo const& _testInfo,
+                                   Totals const& _totals,
+                                   std::string const& _stdOut,
+                                   std::string const& _stdErr,
+                                   bool _aborting )
+    : testInfo( _testInfo ),
+        totals( _totals ),
+        stdOut( _stdOut ),
+        stdErr( _stdErr ),
+        aborting( _aborting )
+    {}
+
+    TestCaseStats::~TestCaseStats() = default;
+
+    TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo,
+                                    Totals const& _totals,
+                                    bool _aborting )
+    :   groupInfo( _groupInfo ),
+        totals( _totals ),
+        aborting( _aborting )
+    {}
+
+    TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo )
+    :   groupInfo( _groupInfo ),
+        aborting( false )
+    {}
+
+    TestGroupStats::~TestGroupStats() = default;
+
+    TestRunStats::TestRunStats(   TestRunInfo const& _runInfo,
+                    Totals const& _totals,
+                    bool _aborting )
+    :   runInfo( _runInfo ),
+        totals( _totals ),
+        aborting( _aborting )
+    {}
+
+    TestRunStats::~TestRunStats() = default;
+
+    void IStreamingReporter::fatalErrorEncountered( StringRef ) {}
+    bool IStreamingReporter::isMulti() const { return false; }
+
+    IReporterFactory::~IReporterFactory() = default;
+    IReporterRegistry::~IReporterRegistry() = default;
+
+} // end namespace Catch
+// end catch_interfaces_reporter.cpp
+// start catch_interfaces_runner.cpp
+
+namespace Catch {
+    IRunner::~IRunner() = default;
+}
+// end catch_interfaces_runner.cpp
+// start catch_interfaces_testcase.cpp
+
+namespace Catch {
+    ITestInvoker::~ITestInvoker() = default;
+    ITestCaseRegistry::~ITestCaseRegistry() = default;
+}
+// end catch_interfaces_testcase.cpp
+// start catch_leak_detector.cpp
+
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include <crtdbg.h>
+
+namespace Catch {
+
+    LeakDetector::LeakDetector() {
+        int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+        flag |= _CRTDBG_LEAK_CHECK_DF;
+        flag |= _CRTDBG_ALLOC_MEM_DF;
+        _CrtSetDbgFlag(flag);
+        _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+        _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+        // Change this to leaking allocation's number to break there
+        _CrtSetBreakAlloc(-1);
+    }
+}
+
+#else
+
+    Catch::LeakDetector::LeakDetector() {}
+
+#endif
+
+Catch::LeakDetector::~LeakDetector() {
+    Catch::cleanUp();
+}
+// end catch_leak_detector.cpp
+// start catch_list.cpp
+
+// start catch_list.h
+
+#include <set>
+
+namespace Catch {
+
+    std::size_t listTests( Config const& config );
+
+    std::size_t listTestsNamesOnly( Config const& config );
+
+    struct TagInfo {
+        void add( std::string const& spelling );
+        std::string all() const;
+
+        std::set<std::string> spellings;
+        std::size_t count = 0;
+    };
+
+    std::size_t listTags( Config const& config );
+
+    std::size_t listReporters();
+
+    Option<std::size_t> list( std::shared_ptr<Config> const& config );
+
+} // end namespace Catch
+
+// end catch_list.h
+// start catch_text.h
+
+namespace Catch {
+    using namespace clara::TextFlow;
+}
+
+// end catch_text.h
+#include <limits>
+#include <algorithm>
+#include <iomanip>
+
+namespace Catch {
+
+    std::size_t listTests( Config const& config ) {
+        TestSpec const& testSpec = config.testSpec();
+        if( config.hasTestFilters() )
+            Catch::cout() << "Matching test cases:\n";
+        else {
+            Catch::cout() << "All available test cases:\n";
+        }
+
+        auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( auto const& testCaseInfo : matchedTestCases ) {
+            Colour::Code colour = testCaseInfo.isHidden()
+                ? Colour::SecondaryText
+                : Colour::None;
+            Colour colourGuard( colour );
+
+            Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n";
+            if( config.verbosity() >= Verbosity::High ) {
+                Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl;
+                std::string description = testCaseInfo.description;
+                if( description.empty() )
+                    description = "(NO DESCRIPTION)";
+                Catch::cout() << Column( description ).indent(4) << std::endl;
+            }
+            if( !testCaseInfo.tags.empty() )
+                Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n";
+        }
+
+        if( !config.hasTestFilters() )
+            Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl;
+        else
+            Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl;
+        return matchedTestCases.size();
+    }
+
+    std::size_t listTestsNamesOnly( Config const& config ) {
+        TestSpec const& testSpec = config.testSpec();
+        std::size_t matchedTests = 0;
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( auto const& testCaseInfo : matchedTestCases ) {
+            matchedTests++;
+            if( startsWith( testCaseInfo.name, '#' ) )
+               Catch::cout() << '"' << testCaseInfo.name << '"';
+            else
+               Catch::cout() << testCaseInfo.name;
+            if ( config.verbosity() >= Verbosity::High )
+                Catch::cout() << "\t@" << testCaseInfo.lineInfo;
+            Catch::cout() << std::endl;
+        }
+        return matchedTests;
+    }
+
+    void TagInfo::add( std::string const& spelling ) {
+        ++count;
+        spellings.insert( spelling );
+    }
+
+    std::string TagInfo::all() const {
+        size_t size = 0;
+        for (auto const& spelling : spellings) {
+            // Add 2 for the brackes
+            size += spelling.size() + 2;
+        }
+
+        std::string out; out.reserve(size);
+        for (auto const& spelling : spellings) {
+            out += '[';
+            out += spelling;
+            out += ']';
+        }
+        return out;
+    }
+
+    std::size_t listTags( Config const& config ) {
+        TestSpec const& testSpec = config.testSpec();
+        if( config.hasTestFilters() )
+            Catch::cout() << "Tags for matching test cases:\n";
+        else {
+            Catch::cout() << "All available tags:\n";
+        }
+
+        std::map<std::string, TagInfo> tagCounts;
+
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( auto const& testCase : matchedTestCases ) {
+            for( auto const& tagName : testCase.getTestCaseInfo().tags ) {
+                std::string lcaseTagName = toLower( tagName );
+                auto countIt = tagCounts.find( lcaseTagName );
+                if( countIt == tagCounts.end() )
+                    countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+                countIt->second.add( tagName );
+            }
+        }
+
+        for( auto const& tagCount : tagCounts ) {
+            ReusableStringStream rss;
+            rss << "  " << std::setw(2) << tagCount.second.count << "  ";
+            auto str = rss.str();
+            auto wrapper = Column( tagCount.second.all() )
+                                                    .initialIndent( 0 )
+                                                    .indent( str.size() )
+                                                    .width( CATCH_CONFIG_CONSOLE_WIDTH-10 );
+            Catch::cout() << str << wrapper << '\n';
+        }
+        Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
+        return tagCounts.size();
+    }
+
+    std::size_t listReporters() {
+        Catch::cout() << "Available reporters:\n";
+        IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+        std::size_t maxNameLen = 0;
+        for( auto const& factoryKvp : factories )
+            maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() );
+
+        for( auto const& factoryKvp : factories ) {
+            Catch::cout()
+                    << Column( factoryKvp.first + ":" )
+                            .indent(2)
+                            .width( 5+maxNameLen )
+                    +  Column( factoryKvp.second->getDescription() )
+                            .initialIndent(0)
+                            .indent(2)
+                            .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 )
+                    << "\n";
+        }
+        Catch::cout() << std::endl;
+        return factories.size();
+    }
+
+    Option<std::size_t> list( std::shared_ptr<Config> const& config ) {
+        Option<std::size_t> listedCount;
+        getCurrentMutableContext().setConfig( config );
+        if( config->listTests() )
+            listedCount = listedCount.valueOr(0) + listTests( *config );
+        if( config->listTestNamesOnly() )
+            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config );
+        if( config->listTags() )
+            listedCount = listedCount.valueOr(0) + listTags( *config );
+        if( config->listReporters() )
+            listedCount = listedCount.valueOr(0) + listReporters();
+        return listedCount;
+    }
+
+} // end namespace Catch
+// end catch_list.cpp
+// start catch_matchers.cpp
+
+namespace Catch {
+namespace Matchers {
+    namespace Impl {
+
+        std::string MatcherUntypedBase::toString() const {
+            if( m_cachedToString.empty() )
+                m_cachedToString = describe();
+            return m_cachedToString;
+        }
+
+        MatcherUntypedBase::~MatcherUntypedBase() = default;
+
+    } // namespace Impl
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // namespace Catch
+// end catch_matchers.cpp
+// start catch_matchers_exception.cpp
+
+namespace Catch {
+namespace Matchers {
+namespace Exception {
+
+bool ExceptionMessageMatcher::match(std::exception const& ex) const {
+    return ex.what() == m_message;
+}
+
+std::string ExceptionMessageMatcher::describe() const {
+    return "exception message matches \"" + m_message + "\"";
+}
+
+}
+Exception::ExceptionMessageMatcher Message(std::string const& message) {
+    return Exception::ExceptionMessageMatcher(message);
+}
+
+// namespace Exception
+} // namespace Matchers
+} // namespace Catch
+// end catch_matchers_exception.cpp
+// start catch_matchers_floating.cpp
+
+// start catch_polyfills.hpp
+
+namespace Catch {
+    bool isnan(float f);
+    bool isnan(double d);
+}
+
+// end catch_polyfills.hpp
+// start catch_to_string.hpp
+
+#include <string>
+
+namespace Catch {
+    template <typename T>
+    std::string to_string(T const& t) {
+#if defined(CATCH_CONFIG_CPP11_TO_STRING)
+        return std::to_string(t);
+#else
+        ReusableStringStream rss;
+        rss << t;
+        return rss.str();
+#endif
+    }
+} // end namespace Catch
+
+// end catch_to_string.hpp
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <cstdint>
+#include <cstring>
+#include <sstream>
+#include <type_traits>
+#include <iomanip>
+#include <limits>
+
+namespace Catch {
+namespace {
+
+    int32_t convert(float f) {
+        static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
+        int32_t i;
+        std::memcpy(&i, &f, sizeof(f));
+        return i;
+    }
+
+    int64_t convert(double d) {
+        static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
+        int64_t i;
+        std::memcpy(&i, &d, sizeof(d));
+        return i;
+    }
+
+    template <typename FP>
+    bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
+        // Comparison with NaN should always be false.
+        // This way we can rule it out before getting into the ugly details
+        if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
+            return false;
+        }
+
+        auto lc = convert(lhs);
+        auto rc = convert(rhs);
+
+        if ((lc < 0) != (rc < 0)) {
+            // Potentially we can have +0 and -0
+            return lhs == rhs;
+        }
+
+        // static cast as a workaround for IBM XLC
+        auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
+        return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
+    }
+
+#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+
+    float nextafter(float x, float y) {
+        return ::nextafterf(x, y);
+    }
+
+    double nextafter(double x, double y) {
+        return ::nextafter(x, y);
+    }
+
+#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
+
+template <typename FP>
+FP step(FP start, FP direction, uint64_t steps) {
+    for (uint64_t i = 0; i < steps; ++i) {
+#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+        start = Catch::nextafter(start, direction);
+#else
+        start = std::nextafter(start, direction);
+#endif
+    }
+    return start;
+}
+
+// Performs equivalent check of std::fabs(lhs - rhs) <= margin
+// But without the subtraction to allow for INFINITY in comparison
+bool marginComparison(double lhs, double rhs, double margin) {
+    return (lhs + margin >= rhs) && (rhs + margin >= lhs);
+}
+
+template <typename FloatingPoint>
+void write(std::ostream& out, FloatingPoint num) {
+    out << std::scientific
+        << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)
+        << num;
+}
+
+} // end anonymous namespace
+
+namespace Matchers {
+namespace Floating {
+
+    enum class FloatingPointKind : uint8_t {
+        Float,
+        Double
+    };
+
+    WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
+        :m_target{ target }, m_margin{ margin } {
+        CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
+            << " Margin has to be non-negative.");
+    }
+
+    // Performs equivalent check of std::fabs(lhs - rhs) <= margin
+    // But without the subtraction to allow for INFINITY in comparison
+    bool WithinAbsMatcher::match(double const& matchee) const {
+        return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee);
+    }
+
+    std::string WithinAbsMatcher::describe() const {
+        return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
+    }
+
+    WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
+        :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
+        CATCH_ENFORCE(m_type == FloatingPointKind::Double
+                   || m_ulps < (std::numeric_limits<uint32_t>::max)(),
+            "Provided ULP is impossibly large for a float comparison.");
+    }
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+// Clang <3.5 reports on the default branch in the switch below
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+
+    bool WithinUlpsMatcher::match(double const& matchee) const {
+        switch (m_type) {
+        case FloatingPointKind::Float:
+            return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);
+        case FloatingPointKind::Double:
+            return almostEqualUlps<double>(matchee, m_target, m_ulps);
+        default:
+            CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" );
+        }
+    }
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+    std::string WithinUlpsMatcher::describe() const {
+        std::stringstream ret;
+
+        ret << "is within " << m_ulps << " ULPs of ";
+
+        if (m_type == FloatingPointKind::Float) {
+            write(ret, static_cast<float>(m_target));
+            ret << 'f';
+        } else {
+            write(ret, m_target);
+        }
+
+        ret << " ([";
+        if (m_type == FloatingPointKind::Double) {
+            write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
+            ret << ", ";
+            write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
+        } else {
+            // We have to cast INFINITY to float because of MinGW, see #1782
+            write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps));
+            ret << ", ";
+            write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));
+        }
+        ret << "])";
+
+        return ret.str();
+    }
+
+    WithinRelMatcher::WithinRelMatcher(double target, double epsilon):
+        m_target(target),
+        m_epsilon(epsilon){
+        CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon <  0 does not make sense.");
+        CATCH_ENFORCE(m_epsilon  < 1., "Relative comparison with epsilon >= 1 does not make sense.");
+    }
+
+    bool WithinRelMatcher::match(double const& matchee) const {
+        const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target));
+        return marginComparison(matchee, m_target,
+                                std::isinf(relMargin)? 0 : relMargin);
+    }
+
+    std::string WithinRelMatcher::describe() const {
+        Catch::ReusableStringStream sstr;
+        sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other";
+        return sstr.str();
+    }
+
+}// namespace Floating
+
+Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
+    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
+}
+
+Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
+    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
+}
+
+Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
+    return Floating::WithinAbsMatcher(target, margin);
+}
+
+Floating::WithinRelMatcher WithinRel(double target, double eps) {
+    return Floating::WithinRelMatcher(target, eps);
+}
+
+Floating::WithinRelMatcher WithinRel(double target) {
+    return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
+}
+
+Floating::WithinRelMatcher WithinRel(float target, float eps) {
+    return Floating::WithinRelMatcher(target, eps);
+}
+
+Floating::WithinRelMatcher WithinRel(float target) {
+    return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
+}
+
+} // namespace Matchers
+} // namespace Catch
+// end catch_matchers_floating.cpp
+// start catch_matchers_generic.cpp
+
+std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) {
+    if (desc.empty()) {
+        return "matches undescribed predicate";
+    } else {
+        return "matches predicate: \"" + desc + '"';
+    }
+}
+// end catch_matchers_generic.cpp
+// start catch_matchers_string.cpp
+
+#include <regex>
+
+namespace Catch {
+namespace Matchers {
+
+    namespace StdString {
+
+        CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+        :   m_caseSensitivity( caseSensitivity ),
+            m_str( adjustString( str ) )
+        {}
+        std::string CasedString::adjustString( std::string const& str ) const {
+            return m_caseSensitivity == CaseSensitive::No
+                   ? toLower( str )
+                   : str;
+        }
+        std::string CasedString::caseSensitivitySuffix() const {
+            return m_caseSensitivity == CaseSensitive::No
+                   ? " (case insensitive)"
+                   : std::string();
+        }
+
+        StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
+        : m_comparator( comparator ),
+          m_operation( operation ) {
+        }
+
+        std::string StringMatcherBase::describe() const {
+            std::string description;
+            description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+                                        m_comparator.caseSensitivitySuffix().size());
+            description += m_operation;
+            description += ": \"";
+            description += m_comparator.m_str;
+            description += "\"";
+            description += m_comparator.caseSensitivitySuffix();
+            return description;
+        }
+
+        EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+
+        bool EqualsMatcher::match( std::string const& source ) const {
+            return m_comparator.adjustString( source ) == m_comparator.m_str;
+        }
+
+        ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+
+        bool ContainsMatcher::match( std::string const& source ) const {
+            return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+
+        bool StartsWithMatcher::match( std::string const& source ) const {
+            return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+
+        bool EndsWithMatcher::match( std::string const& source ) const {
+            return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {}
+
+        bool RegexMatcher::match(std::string const& matchee) const {
+            auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway
+            if (m_caseSensitivity == CaseSensitive::Choice::No) {
+                flags |= std::regex::icase;
+            }
+            auto reg = std::regex(m_regex, flags);
+            return std::regex_match(matchee, reg);
+        }
+
+        std::string RegexMatcher::describe() const {
+            return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively");
+        }
+
+    } // namespace StdString
+
+    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+
+    StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) {
+        return StdString::RegexMatcher(regex, caseSensitivity);
+    }
+
+} // namespace Matchers
+} // namespace Catch
+// end catch_matchers_string.cpp
+// start catch_message.cpp
+
+// start catch_uncaught_exceptions.h
+
+namespace Catch {
+    bool uncaught_exceptions();
+} // end namespace Catch
+
+// end catch_uncaught_exceptions.h
+#include <cassert>
+#include <stack>
+
+namespace Catch {
+
+    MessageInfo::MessageInfo(   StringRef const& _macroName,
+                                SourceLineInfo const& _lineInfo,
+                                ResultWas::OfType _type )
+    :   macroName( _macroName ),
+        lineInfo( _lineInfo ),
+        type( _type ),
+        sequence( ++globalCount )
+    {}
+
+    bool MessageInfo::operator==( MessageInfo const& other ) const {
+        return sequence == other.sequence;
+    }
+
+    bool MessageInfo::operator<( MessageInfo const& other ) const {
+        return sequence < other.sequence;
+    }
+
+    // This may need protecting if threading support is added
+    unsigned int MessageInfo::globalCount = 0;
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    Catch::MessageBuilder::MessageBuilder( StringRef const& macroName,
+                                           SourceLineInfo const& lineInfo,
+                                           ResultWas::OfType type )
+        :m_info(macroName, lineInfo, type) {}
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+    : m_info( builder.m_info ), m_moved()
+    {
+        m_info.message = builder.m_stream.str();
+        getResultCapture().pushScopedMessage( m_info );
+    }
+
+    ScopedMessage::ScopedMessage( ScopedMessage&& old )
+    : m_info( old.m_info ), m_moved()
+    {
+        old.m_moved = true;
+    }
+
+    ScopedMessage::~ScopedMessage() {
+        if ( !uncaught_exceptions() && !m_moved ){
+            getResultCapture().popScopedMessage(m_info);
+        }
+    }
+
+    Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
+        auto trimmed = [&] (size_t start, size_t end) {
+            while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
+                ++start;
+            }
+            while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) {
+                --end;
+            }
+            return names.substr(start, end - start + 1);
+        };
+        auto skipq = [&] (size_t start, char quote) {
+            for (auto i = start + 1; i < names.size() ; ++i) {
+                if (names[i] == quote)
+                    return i;
+                if (names[i] == '\\')
+                    ++i;
+            }
+            CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote");
+        };
+
+        size_t start = 0;
+        std::stack<char> openings;
+        for (size_t pos = 0; pos < names.size(); ++pos) {
+            char c = names[pos];
+            switch (c) {
+            case '[':
+            case '{':
+            case '(':
+            // It is basically impossible to disambiguate between
+            // comparison and start of template args in this context
+//            case '<':
+                openings.push(c);
+                break;
+            case ']':
+            case '}':
+            case ')':
+//           case '>':
+                openings.pop();
+                break;
+            case '"':
+            case '\'':
+                pos = skipq(pos, c);
+                break;
+            case ',':
+                if (start != pos && openings.empty()) {
+                    m_messages.emplace_back(macroName, lineInfo, resultType);
+                    m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
+                    m_messages.back().message += " := ";
+                    start = pos;
+                }
+            }
+        }
+        assert(openings.empty() && "Mismatched openings");
+        m_messages.emplace_back(macroName, lineInfo, resultType);
+        m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
+        m_messages.back().message += " := ";
+    }
+    Capturer::~Capturer() {
+        if ( !uncaught_exceptions() ){
+            assert( m_captured == m_messages.size() );
+            for( size_t i = 0; i < m_captured; ++i  )
+                m_resultCapture.popScopedMessage( m_messages[i] );
+        }
+    }
+
+    void Capturer::captureValue( size_t index, std::string const& value ) {
+        assert( index < m_messages.size() );
+        m_messages[index].message += value;
+        m_resultCapture.pushScopedMessage( m_messages[index] );
+        m_captured++;
+    }
+
+} // end namespace Catch
+// end catch_message.cpp
+// start catch_output_redirect.cpp
+
+// start catch_output_redirect.h
+#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
+#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
+
+#include <cstdio>
+#include <iosfwd>
+#include <string>
+
+namespace Catch {
+
+    class RedirectedStream {
+        std::ostream& m_originalStream;
+        std::ostream& m_redirectionStream;
+        std::streambuf* m_prevBuf;
+
+    public:
+        RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream );
+        ~RedirectedStream();
+    };
+
+    class RedirectedStdOut {
+        ReusableStringStream m_rss;
+        RedirectedStream m_cout;
+    public:
+        RedirectedStdOut();
+        auto str() const -> std::string;
+    };
+
+    // StdErr has two constituent streams in C++, std::cerr and std::clog
+    // This means that we need to redirect 2 streams into 1 to keep proper
+    // order of writes
+    class RedirectedStdErr {
+        ReusableStringStream m_rss;
+        RedirectedStream m_cerr;
+        RedirectedStream m_clog;
+    public:
+        RedirectedStdErr();
+        auto str() const -> std::string;
+    };
+
+    class RedirectedStreams {
+    public:
+        RedirectedStreams(RedirectedStreams const&) = delete;
+        RedirectedStreams& operator=(RedirectedStreams const&) = delete;
+        RedirectedStreams(RedirectedStreams&&) = delete;
+        RedirectedStreams& operator=(RedirectedStreams&&) = delete;
+
+        RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr);
+        ~RedirectedStreams();
+    private:
+        std::string& m_redirectedCout;
+        std::string& m_redirectedCerr;
+        RedirectedStdOut m_redirectedStdOut;
+        RedirectedStdErr m_redirectedStdErr;
+    };
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+
+    // Windows's implementation of std::tmpfile is terrible (it tries
+    // to create a file inside system folder, thus requiring elevated
+    // privileges for the binary), so we have to use tmpnam(_s) and
+    // create the file ourselves there.
+    class TempFile {
+    public:
+        TempFile(TempFile const&) = delete;
+        TempFile& operator=(TempFile const&) = delete;
+        TempFile(TempFile&&) = delete;
+        TempFile& operator=(TempFile&&) = delete;
+
+        TempFile();
+        ~TempFile();
+
+        std::FILE* getFile();
+        std::string getContents();
+
+    private:
+        std::FILE* m_file = nullptr;
+    #if defined(_MSC_VER)
+        char m_buffer[L_tmpnam] = { 0 };
+    #endif
+    };
+
+    class OutputRedirect {
+    public:
+        OutputRedirect(OutputRedirect const&) = delete;
+        OutputRedirect& operator=(OutputRedirect const&) = delete;
+        OutputRedirect(OutputRedirect&&) = delete;
+        OutputRedirect& operator=(OutputRedirect&&) = delete;
+
+        OutputRedirect(std::string& stdout_dest, std::string& stderr_dest);
+        ~OutputRedirect();
+
+    private:
+        int m_originalStdout = -1;
+        int m_originalStderr = -1;
+        TempFile m_stdoutFile;
+        TempFile m_stderrFile;
+        std::string& m_stdoutDest;
+        std::string& m_stderrDest;
+    };
+
+#endif
+
+} // end namespace Catch
+
+#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
+// end catch_output_redirect.h
+#include <cstdio>
+#include <cstring>
+#include <fstream>
+#include <sstream>
+#include <stdexcept>
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+    #if defined(_MSC_VER)
+    #include <io.h>      //_dup and _dup2
+    #define dup _dup
+    #define dup2 _dup2
+    #define fileno _fileno
+    #else
+    #include <unistd.h>  // dup and dup2
+    #endif
+#endif
+
+namespace Catch {
+
+    RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
+    :   m_originalStream( originalStream ),
+        m_redirectionStream( redirectionStream ),
+        m_prevBuf( m_originalStream.rdbuf() )
+    {
+        m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
+    }
+
+    RedirectedStream::~RedirectedStream() {
+        m_originalStream.rdbuf( m_prevBuf );
+    }
+
+    RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
+    auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); }
+
+    RedirectedStdErr::RedirectedStdErr()
+    :   m_cerr( Catch::cerr(), m_rss.get() ),
+        m_clog( Catch::clog(), m_rss.get() )
+    {}
+    auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
+
+    RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
+    :   m_redirectedCout(redirectedCout),
+        m_redirectedCerr(redirectedCerr)
+    {}
+
+    RedirectedStreams::~RedirectedStreams() {
+        m_redirectedCout += m_redirectedStdOut.str();
+        m_redirectedCerr += m_redirectedStdErr.str();
+    }
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+
+#if defined(_MSC_VER)
+    TempFile::TempFile() {
+        if (tmpnam_s(m_buffer)) {
+            CATCH_RUNTIME_ERROR("Could not get a temp filename");
+        }
+        if (fopen_s(&m_file, m_buffer, "w+")) {
+            char buffer[100];
+            if (strerror_s(buffer, errno)) {
+                CATCH_RUNTIME_ERROR("Could not translate errno to a string");
+            }
+            CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
+        }
+    }
+#else
+    TempFile::TempFile() {
+        m_file = std::tmpfile();
+        if (!m_file) {
+            CATCH_RUNTIME_ERROR("Could not create a temp file.");
+        }
+    }
+
+#endif
+
+    TempFile::~TempFile() {
+         // TBD: What to do about errors here?
+         std::fclose(m_file);
+         // We manually create the file on Windows only, on Linux
+         // it will be autodeleted
+#if defined(_MSC_VER)
+         std::remove(m_buffer);
+#endif
+    }
+
+    FILE* TempFile::getFile() {
+        return m_file;
+    }
+
+    std::string TempFile::getContents() {
+        std::stringstream sstr;
+        char buffer[100] = {};
+        std::rewind(m_file);
+        while (std::fgets(buffer, sizeof(buffer), m_file)) {
+            sstr << buffer;
+        }
+        return sstr.str();
+    }
+
+    OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) :
+        m_originalStdout(dup(1)),
+        m_originalStderr(dup(2)),
+        m_stdoutDest(stdout_dest),
+        m_stderrDest(stderr_dest) {
+        dup2(fileno(m_stdoutFile.getFile()), 1);
+        dup2(fileno(m_stderrFile.getFile()), 2);
+    }
+
+    OutputRedirect::~OutputRedirect() {
+        Catch::cout() << std::flush;
+        fflush(stdout);
+        // Since we support overriding these streams, we flush cerr
+        // even though std::cerr is unbuffered
+        Catch::cerr() << std::flush;
+        Catch::clog() << std::flush;
+        fflush(stderr);
+
+        dup2(m_originalStdout, 1);
+        dup2(m_originalStderr, 2);
+
+        m_stdoutDest += m_stdoutFile.getContents();
+        m_stderrDest += m_stderrFile.getContents();
+    }
+
+#endif // CATCH_CONFIG_NEW_CAPTURE
+
+} // namespace Catch
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+    #if defined(_MSC_VER)
+    #undef dup
+    #undef dup2
+    #undef fileno
+    #endif
+#endif
+// end catch_output_redirect.cpp
+// start catch_polyfills.cpp
+
+#include <cmath>
+
+namespace Catch {
+
+#if !defined(CATCH_CONFIG_POLYFILL_ISNAN)
+    bool isnan(float f) {
+        return std::isnan(f);
+    }
+    bool isnan(double d) {
+        return std::isnan(d);
+    }
+#else
+    // For now we only use this for embarcadero
+    bool isnan(float f) {
+        return std::_isnan(f);
+    }
+    bool isnan(double d) {
+        return std::_isnan(d);
+    }
+#endif
+
+} // end namespace Catch
+// end catch_polyfills.cpp
+// start catch_random_number_generator.cpp
+
+namespace Catch {
+
+namespace {
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4146) // we negate uint32 during the rotate
+#endif
+        // Safe rotr implementation thanks to John Regehr
+        uint32_t rotate_right(uint32_t val, uint32_t count) {
+            const uint32_t mask = 31;
+            count &= mask;
+            return (val >> count) | (val << (-count & mask));
+        }
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+}
+
+    SimplePcg32::SimplePcg32(result_type seed_) {
+        seed(seed_);
+    }
+
+    void SimplePcg32::seed(result_type seed_) {
+        m_state = 0;
+        (*this)();
+        m_state += seed_;
+        (*this)();
+    }
+
+    void SimplePcg32::discard(uint64_t skip) {
+        // We could implement this to run in O(log n) steps, but this
+        // should suffice for our use case.
+        for (uint64_t s = 0; s < skip; ++s) {
+            static_cast<void>((*this)());
+        }
+    }
+
+    SimplePcg32::result_type SimplePcg32::operator()() {
+        // prepare the output value
+        const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u);
+        const auto output = rotate_right(xorshifted, m_state >> 59u);
+
+        // advance state
+        m_state = m_state * 6364136223846793005ULL + s_inc;
+
+        return output;
+    }
+
+    bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
+        return lhs.m_state == rhs.m_state;
+    }
+
+    bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
+        return lhs.m_state != rhs.m_state;
+    }
+}
+// end catch_random_number_generator.cpp
+// start catch_registry_hub.cpp
+
+// start catch_test_case_registry_impl.h
+
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <ios>
+
+namespace Catch {
+
+    class TestCase;
+    struct IConfig;
+
+    std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases );
+
+    bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+
+    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions );
+
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+    class TestRegistry : public ITestCaseRegistry {
+    public:
+        virtual ~TestRegistry() = default;
+
+        virtual void registerTest( TestCase const& testCase );
+
+        std::vector<TestCase> const& getAllTests() const override;
+        std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const override;
+
+    private:
+        std::vector<TestCase> m_functions;
+        mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder;
+        mutable std::vector<TestCase> m_sortedFunctions;
+        std::size_t m_unnamedCount = 0;
+        std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class TestInvokerAsFunction : public ITestInvoker {
+        void(*m_testAsFunction)();
+    public:
+        TestInvokerAsFunction( void(*testAsFunction)() ) noexcept;
+
+        void invoke() const override;
+    };
+
+    std::string extractClassName( StringRef const& classOrQualifiedMethodName );
+
+    ///////////////////////////////////////////////////////////////////////////
+
+} // end namespace Catch
+
+// end catch_test_case_registry_impl.h
+// start catch_reporter_registry.h
+
+#include <map>
+
+namespace Catch {
+
+    class ReporterRegistry : public IReporterRegistry {
+
+    public:
+
+        ~ReporterRegistry() override;
+
+        IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override;
+
+        void registerReporter( std::string const& name, IReporterFactoryPtr const& factory );
+        void registerListener( IReporterFactoryPtr const& factory );
+
+        FactoryMap const& getFactories() const override;
+        Listeners const& getListeners() const override;
+
+    private:
+        FactoryMap m_factories;
+        Listeners m_listeners;
+    };
+}
+
+// end catch_reporter_registry.h
+// start catch_tag_alias_registry.h
+
+// start catch_tag_alias.h
+
+#include <string>
+
+namespace Catch {
+
+    struct TagAlias {
+        TagAlias(std::string const& _tag, SourceLineInfo _lineInfo);
+
+        std::string tag;
+        SourceLineInfo lineInfo;
+    };
+
+} // end namespace Catch
+
+// end catch_tag_alias.h
+#include <map>
+
+namespace Catch {
+
+    class TagAliasRegistry : public ITagAliasRegistry {
+    public:
+        ~TagAliasRegistry() override;
+        TagAlias const* find( std::string const& alias ) const override;
+        std::string expandAliases( std::string const& unexpandedTestSpec ) const override;
+        void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
+
+    private:
+        std::map<std::string, TagAlias> m_registry;
+    };
+
+} // end namespace Catch
+
+// end catch_tag_alias_registry.h
+// start catch_startup_exception_registry.h
+
+#include <vector>
+#include <exception>
+
+namespace Catch {
+
+    class StartupExceptionRegistry {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+    public:
+        void add(std::exception_ptr const& exception) noexcept;
+        std::vector<std::exception_ptr> const& getExceptions() const noexcept;
+    private:
+        std::vector<std::exception_ptr> m_exceptions;
+#endif
+    };
+
+} // end namespace Catch
+
+// end catch_startup_exception_registry.h
+// start catch_singletons.hpp
+
+namespace Catch {
+
+    struct ISingleton {
+        virtual ~ISingleton();
+    };
+
+    void addSingleton( ISingleton* singleton );
+    void cleanupSingletons();
+
+    template<typename SingletonImplT, typename InterfaceT = SingletonImplT, typename MutableInterfaceT = InterfaceT>
+    class Singleton : SingletonImplT, public ISingleton {
+
+        static auto getInternal() -> Singleton* {
+            static Singleton* s_instance = nullptr;
+            if( !s_instance ) {
+                s_instance = new Singleton;
+                addSingleton( s_instance );
+            }
+            return s_instance;
+        }
+
+    public:
+        static auto get() -> InterfaceT const& {
+            return *getInternal();
+        }
+        static auto getMutable() -> MutableInterfaceT& {
+            return *getInternal();
+        }
+    };
+
+} // namespace Catch
+
+// end catch_singletons.hpp
+namespace Catch {
+
+    namespace {
+
+        class RegistryHub : public IRegistryHub, public IMutableRegistryHub,
+                            private NonCopyable {
+
+        public: // IRegistryHub
+            RegistryHub() = default;
+            IReporterRegistry const& getReporterRegistry() const override {
+                return m_reporterRegistry;
+            }
+            ITestCaseRegistry const& getTestCaseRegistry() const override {
+                return m_testCaseRegistry;
+            }
+            IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override {
+                return m_exceptionTranslatorRegistry;
+            }
+            ITagAliasRegistry const& getTagAliasRegistry() const override {
+                return m_tagAliasRegistry;
+            }
+            StartupExceptionRegistry const& getStartupExceptionRegistry() const override {
+                return m_exceptionRegistry;
+            }
+
+        public: // IMutableRegistryHub
+            void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override {
+                m_reporterRegistry.registerReporter( name, factory );
+            }
+            void registerListener( IReporterFactoryPtr const& factory ) override {
+                m_reporterRegistry.registerListener( factory );
+            }
+            void registerTest( TestCase const& testInfo ) override {
+                m_testCaseRegistry.registerTest( testInfo );
+            }
+            void registerTranslator( const IExceptionTranslator* translator ) override {
+                m_exceptionTranslatorRegistry.registerTranslator( translator );
+            }
+            void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override {
+                m_tagAliasRegistry.add( alias, tag, lineInfo );
+            }
+            void registerStartupException() noexcept override {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+                m_exceptionRegistry.add(std::current_exception());
+#else
+                CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+#endif
+            }
+            IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
+                return m_enumValuesRegistry;
+            }
+
+        private:
+            TestRegistry m_testCaseRegistry;
+            ReporterRegistry m_reporterRegistry;
+            ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+            TagAliasRegistry m_tagAliasRegistry;
+            StartupExceptionRegistry m_exceptionRegistry;
+            Detail::EnumValuesRegistry m_enumValuesRegistry;
+        };
+    }
+
+    using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>;
+
+    IRegistryHub const& getRegistryHub() {
+        return RegistryHubSingleton::get();
+    }
+    IMutableRegistryHub& getMutableRegistryHub() {
+        return RegistryHubSingleton::getMutable();
+    }
+    void cleanUp() {
+        cleanupSingletons();
+        cleanUpContext();
+    }
+    std::string translateActiveException() {
+        return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+    }
+
+} // end namespace Catch
+// end catch_registry_hub.cpp
+// start catch_reporter_registry.cpp
+
+namespace Catch {
+
+    ReporterRegistry::~ReporterRegistry() = default;
+
+    IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const {
+        auto it =  m_factories.find( name );
+        if( it == m_factories.end() )
+            return nullptr;
+        return it->second->create( ReporterConfig( config ) );
+    }
+
+    void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) {
+        m_factories.emplace(name, factory);
+    }
+    void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) {
+        m_listeners.push_back( factory );
+    }
+
+    IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const {
+        return m_factories;
+    }
+    IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const {
+        return m_listeners;
+    }
+
+}
+// end catch_reporter_registry.cpp
+// start catch_result_type.cpp
+
+namespace Catch {
+
+    bool isOk( ResultWas::OfType resultType ) {
+        return ( resultType & ResultWas::FailureBit ) == 0;
+    }
+    bool isJustInfo( int flags ) {
+        return flags == ResultWas::Info;
+    }
+
+    ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+        return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+    }
+
+    bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+    bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+// end catch_result_type.cpp
+// start catch_run_context.cpp
+
+#include <cassert>
+#include <algorithm>
+#include <sstream>
+
+namespace Catch {
+
+    namespace Generators {
+        struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
+            GeneratorBasePtr m_generator;
+
+            GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+            :   TrackerBase( nameAndLocation, ctx, parent )
+            {}
+            ~GeneratorTracker();
+
+            static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) {
+                std::shared_ptr<GeneratorTracker> tracker;
+
+                ITracker& currentTracker = ctx.currentTracker();
+                // Under specific circumstances, the generator we want
+                // to acquire is also the current tracker. If this is
+                // the case, we have to avoid looking through current
+                // tracker's children, and instead return the current
+                // tracker.
+                // A case where this check is important is e.g.
+                //     for (int i = 0; i < 5; ++i) {
+                //         int n = GENERATE(1, 2);
+                //     }
+                //
+                // without it, the code above creates 5 nested generators.
+                if (currentTracker.nameAndLocation() == nameAndLocation) {
+                    auto thisTracker = currentTracker.parent().findChild(nameAndLocation);
+                    assert(thisTracker);
+                    assert(thisTracker->isGeneratorTracker());
+                    tracker = std::static_pointer_cast<GeneratorTracker>(thisTracker);
+                } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+                    assert( childTracker );
+                    assert( childTracker->isGeneratorTracker() );
+                    tracker = std::static_pointer_cast<GeneratorTracker>( childTracker );
+                } else {
+                    tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, &currentTracker );
+                    currentTracker.addChild( tracker );
+                }
+
+                if( !tracker->isComplete() ) {
+                    tracker->open();
+                }
+
+                return *tracker;
+            }
+
+            // TrackerBase interface
+            bool isGeneratorTracker() const override { return true; }
+            auto hasGenerator() const -> bool override {
+                return !!m_generator;
+            }
+            void close() override {
+                TrackerBase::close();
+                // If a generator has a child (it is followed by a section)
+                // and none of its children have started, then we must wait
+                // until later to start consuming its values.
+                // This catches cases where `GENERATE` is placed between two
+                // `SECTION`s.
+                // **The check for m_children.empty cannot be removed**.
+                // doing so would break `GENERATE` _not_ followed by `SECTION`s.
+                const bool should_wait_for_child = [&]() {
+                    // No children -> nobody to wait for
+                    if ( m_children.empty() ) {
+                        return false;
+                    }
+                    // If at least one child started executing, don't wait
+                    if ( std::find_if(
+                             m_children.begin(),
+                             m_children.end(),
+                             []( TestCaseTracking::ITrackerPtr tracker ) {
+                                 return tracker->hasStarted();
+                             } ) != m_children.end() ) {
+                        return false;
+                    }
+
+                    // No children have started. We need to check if they _can_
+                    // start, and thus we should wait for them, or they cannot
+                    // start (due to filters), and we shouldn't wait for them
+                    auto* parent = m_parent;
+                    // This is safe: there is always at least one section
+                    // tracker in a test case tracking tree
+                    while ( !parent->isSectionTracker() ) {
+                        parent = &( parent->parent() );
+                    }
+                    assert( parent &&
+                            "Missing root (test case) level section" );
+
+                    auto const& parentSection =
+                        static_cast<SectionTracker&>( *parent );
+                    auto const& filters = parentSection.getFilters();
+                    // No filters -> no restrictions on running sections
+                    if ( filters.empty() ) {
+                        return true;
+                    }
+
+                    for ( auto const& child : m_children ) {
+                        if ( child->isSectionTracker() &&
+                             std::find( filters.begin(),
+                                        filters.end(),
+                                        static_cast<SectionTracker&>( *child )
+                                            .trimmedName() ) !=
+                                 filters.end() ) {
+                            return true;
+                        }
+                    }
+                    return false;
+                }();
+
+                // This check is a bit tricky, because m_generator->next()
+                // has a side-effect, where it consumes generator's current
+                // value, but we do not want to invoke the side-effect if
+                // this generator is still waiting for any child to start.
+                if ( should_wait_for_child ||
+                     ( m_runState == CompletedSuccessfully &&
+                       m_generator->next() ) ) {
+                    m_children.clear();
+                    m_runState = Executing;
+                }
+            }
+
+            // IGeneratorTracker interface
+            auto getGenerator() const -> GeneratorBasePtr const& override {
+                return m_generator;
+            }
+            void setGenerator( GeneratorBasePtr&& generator ) override {
+                m_generator = std::move( generator );
+            }
+        };
+        GeneratorTracker::~GeneratorTracker() {}
+    }
+
+    RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter)
+    :   m_runInfo(_config->name()),
+        m_context(getCurrentMutableContext()),
+        m_config(_config),
+        m_reporter(std::move(reporter)),
+        m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
+        m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
+    {
+        m_context.setRunner(this);
+        m_context.setConfig(m_config);
+        m_context.setResultCapture(this);
+        m_reporter->testRunStarting(m_runInfo);
+    }
+
+    RunContext::~RunContext() {
+        m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
+    }
+
+    void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) {
+        m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount));
+    }
+
+    void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) {
+        m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting()));
+    }
+
+    Totals RunContext::runTest(TestCase const& testCase) {
+        Totals prevTotals = m_totals;
+
+        std::string redirectedCout;
+        std::string redirectedCerr;
+
+        auto const& testInfo = testCase.getTestCaseInfo();
+
+        m_reporter->testCaseStarting(testInfo);
+
+        m_activeTestCase = &testCase;
+
+        ITracker& rootTracker = m_trackerContext.startRun();
+        assert(rootTracker.isSectionTracker());
+        static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun());
+        do {
+            m_trackerContext.startCycle();
+            m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo));
+            runCurrentTest(redirectedCout, redirectedCerr);
+        } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
+
+        Totals deltaTotals = m_totals.delta(prevTotals);
+        if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) {
+            deltaTotals.assertions.failed++;
+            deltaTotals.testCases.passed--;
+            deltaTotals.testCases.failed++;
+        }
+        m_totals.testCases += deltaTotals.testCases;
+        m_reporter->testCaseEnded(TestCaseStats(testInfo,
+                                  deltaTotals,
+                                  redirectedCout,
+                                  redirectedCerr,
+                                  aborting()));
+
+        m_activeTestCase = nullptr;
+        m_testCaseTracker = nullptr;
+
+        return deltaTotals;
+    }
+
+    IConfigPtr RunContext::config() const {
+        return m_config;
+    }
+
+    IStreamingReporter& RunContext::reporter() const {
+        return *m_reporter;
+    }
+
+    void RunContext::assertionEnded(AssertionResult const & result) {
+        if (result.getResultType() == ResultWas::Ok) {
+            m_totals.assertions.passed++;
+            m_lastAssertionPassed = true;
+        } else if (!result.isOk()) {
+            m_lastAssertionPassed = false;
+            if( m_activeTestCase->getTestCaseInfo().okToFail() )
+                m_totals.assertions.failedButOk++;
+            else
+                m_totals.assertions.failed++;
+        }
+        else {
+            m_lastAssertionPassed = true;
+        }
+
+        // We have no use for the return value (whether messages should be cleared), because messages were made scoped
+        // and should be let to clear themselves out.
+        static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+
+        if (result.getResultType() != ResultWas::Warning)
+            m_messageScopes.clear();
+
+        // Reset working state
+        resetAssertionInfo();
+        m_lastResult = result;
+    }
+    void RunContext::resetAssertionInfo() {
+        m_lastAssertionInfo.macroName = StringRef();
+        m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
+    }
+
+    bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) {
+        ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo));
+        if (!sectionTracker.isOpen())
+            return false;
+        m_activeSections.push_back(&sectionTracker);
+
+        m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+        m_reporter->sectionStarting(sectionInfo);
+
+        assertions = m_totals.assertions;
+
+        return true;
+    }
+    auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+        using namespace Generators;
+        GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext,
+                                                              TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) );
+        m_lastAssertionInfo.lineInfo = lineInfo;
+        return tracker;
+    }
+
+    bool RunContext::testForMissingAssertions(Counts& assertions) {
+        if (assertions.total() != 0)
+            return false;
+        if (!m_config->warnAboutMissingAssertions())
+            return false;
+        if (m_trackerContext.currentTracker().hasChildren())
+            return false;
+        m_totals.assertions.failed++;
+        assertions.failed++;
+        return true;
+    }
+
+    void RunContext::sectionEnded(SectionEndInfo const & endInfo) {
+        Counts assertions = m_totals.assertions - endInfo.prevAssertions;
+        bool missingAssertions = testForMissingAssertions(assertions);
+
+        if (!m_activeSections.empty()) {
+            m_activeSections.back()->close();
+            m_activeSections.pop_back();
+        }
+
+        m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
+        m_messages.clear();
+        m_messageScopes.clear();
+    }
+
+    void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
+        if (m_unfinishedSections.empty())
+            m_activeSections.back()->fail();
+        else
+            m_activeSections.back()->close();
+        m_activeSections.pop_back();
+
+        m_unfinishedSections.push_back(endInfo);
+    }
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+    void RunContext::benchmarkPreparing(std::string const& name) {
+        m_reporter->benchmarkPreparing(name);
+    }
+    void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
+        m_reporter->benchmarkStarting( info );
+    }
+    void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
+        m_reporter->benchmarkEnded( stats );
+    }
+    void RunContext::benchmarkFailed(std::string const & error) {
+        m_reporter->benchmarkFailed(error);
+    }
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+    void RunContext::pushScopedMessage(MessageInfo const & message) {
+        m_messages.push_back(message);
+    }
+
+    void RunContext::popScopedMessage(MessageInfo const & message) {
+        m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
+    }
+
+    void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
+        m_messageScopes.emplace_back( builder );
+    }
+
+    std::string RunContext::getCurrentTestName() const {
+        return m_activeTestCase
+            ? m_activeTestCase->getTestCaseInfo().name
+            : std::string();
+    }
+
+    const AssertionResult * RunContext::getLastResult() const {
+        return &(*m_lastResult);
+    }
+
+    void RunContext::exceptionEarlyReported() {
+        m_shouldReportUnexpected = false;
+    }
+
+    void RunContext::handleFatalErrorCondition( StringRef message ) {
+        // First notify reporter that bad things happened
+        m_reporter->fatalErrorEncountered(message);
+
+        // Don't rebuild the result -- the stringification itself can cause more fatal errors
+        // Instead, fake a result data.
+        AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
+        tempResult.message = static_cast<std::string>(message);
+        AssertionResult result(m_lastAssertionInfo, tempResult);
+
+        assertionEnded(result);
+
+        handleUnfinishedSections();
+
+        // Recreate section for test case (as we will lose the one that was in scope)
+        auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
+
+        Counts assertions;
+        assertions.failed = 1;
+        SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false);
+        m_reporter->sectionEnded(testCaseSectionStats);
+
+        auto const& testInfo = m_activeTestCase->getTestCaseInfo();
+
+        Totals deltaTotals;
+        deltaTotals.testCases.failed = 1;
+        deltaTotals.assertions.failed = 1;
+        m_reporter->testCaseEnded(TestCaseStats(testInfo,
+                                  deltaTotals,
+                                  std::string(),
+                                  std::string(),
+                                  false));
+        m_totals.testCases.failed++;
+        testGroupEnded(std::string(), m_totals, 1, 1);
+        m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
+    }
+
+    bool RunContext::lastAssertionPassed() {
+         return m_lastAssertionPassed;
+    }
+
+    void RunContext::assertionPassed() {
+        m_lastAssertionPassed = true;
+        ++m_totals.assertions.passed;
+        resetAssertionInfo();
+        m_messageScopes.clear();
+    }
+
+    bool RunContext::aborting() const {
+        return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter());
+    }
+
+    void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
+        auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
+        m_reporter->sectionStarting(testCaseSection);
+        Counts prevAssertions = m_totals.assertions;
+        double duration = 0;
+        m_shouldReportUnexpected = true;
+        m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal };
+
+        seedRng(*m_config);
+
+        Timer timer;
+        CATCH_TRY {
+            if (m_reporter->getPreferences().shouldRedirectStdOut) {
+#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+                RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
+
+                timer.start();
+                invokeActiveTestCase();
+#else
+                OutputRedirect r(redirectedCout, redirectedCerr);
+                timer.start();
+                invokeActiveTestCase();
+#endif
+            } else {
+                timer.start();
+                invokeActiveTestCase();
+            }
+            duration = timer.getElapsedSeconds();
+        } CATCH_CATCH_ANON (TestFailureException&) {
+            // This just means the test was aborted due to failure
+        } CATCH_CATCH_ALL {
+            // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
+            // are reported without translation at the point of origin.
+            if( m_shouldReportUnexpected ) {
+                AssertionReaction dummyReaction;
+                handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );
+            }
+        }
+        Counts assertions = m_totals.assertions - prevAssertions;
+        bool missingAssertions = testForMissingAssertions(assertions);
+
+        m_testCaseTracker->close();
+        handleUnfinishedSections();
+        m_messages.clear();
+        m_messageScopes.clear();
+
+        SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
+        m_reporter->sectionEnded(testCaseSectionStats);
+    }
+
+    void RunContext::invokeActiveTestCase() {
+        FatalConditionHandlerGuard _(&m_fatalConditionhandler);
+        m_activeTestCase->invoke();
+    }
+
+    void RunContext::handleUnfinishedSections() {
+        // If sections ended prematurely due to an exception we stored their
+        // infos here so we can tear them down outside the unwind process.
+        for (auto it = m_unfinishedSections.rbegin(),
+             itEnd = m_unfinishedSections.rend();
+             it != itEnd;
+             ++it)
+            sectionEnded(*it);
+        m_unfinishedSections.clear();
+    }
+
+    void RunContext::handleExpr(
+        AssertionInfo const& info,
+        ITransientExpression const& expr,
+        AssertionReaction& reaction
+    ) {
+        m_reporter->assertionStarting( info );
+
+        bool negated = isFalseTest( info.resultDisposition );
+        bool result = expr.getResult() != negated;
+
+        if( result ) {
+            if (!m_includeSuccessfulResults) {
+                assertionPassed();
+            }
+            else {
+                reportExpr(info, ResultWas::Ok, &expr, negated);
+            }
+        }
+        else {
+            reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
+            populateReaction( reaction );
+        }
+    }
+    void RunContext::reportExpr(
+            AssertionInfo const &info,
+            ResultWas::OfType resultType,
+            ITransientExpression const *expr,
+            bool negated ) {
+
+        m_lastAssertionInfo = info;
+        AssertionResultData data( resultType, LazyExpression( negated ) );
+
+        AssertionResult assertionResult{ info, data };
+        assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
+
+        assertionEnded( assertionResult );
+    }
+
+    void RunContext::handleMessage(
+            AssertionInfo const& info,
+            ResultWas::OfType resultType,
+            StringRef const& message,
+            AssertionReaction& reaction
+    ) {
+        m_reporter->assertionStarting( info );
+
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( resultType, LazyExpression( false ) );
+        data.message = static_cast<std::string>(message);
+        AssertionResult assertionResult{ m_lastAssertionInfo, data };
+        assertionEnded( assertionResult );
+        if( !assertionResult.isOk() )
+            populateReaction( reaction );
+    }
+    void RunContext::handleUnexpectedExceptionNotThrown(
+            AssertionInfo const& info,
+            AssertionReaction& reaction
+    ) {
+        handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction);
+    }
+
+    void RunContext::handleUnexpectedInflightException(
+            AssertionInfo const& info,
+            std::string const& message,
+            AssertionReaction& reaction
+    ) {
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
+        data.message = message;
+        AssertionResult assertionResult{ info, data };
+        assertionEnded( assertionResult );
+        populateReaction( reaction );
+    }
+
+    void RunContext::populateReaction( AssertionReaction& reaction ) {
+        reaction.shouldDebugBreak = m_config->shouldDebugBreak();
+        reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal);
+    }
+
+    void RunContext::handleIncomplete(
+            AssertionInfo const& info
+    ) {
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
+        data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
+        AssertionResult assertionResult{ info, data };
+        assertionEnded( assertionResult );
+    }
+    void RunContext::handleNonExpr(
+            AssertionInfo const &info,
+            ResultWas::OfType resultType,
+            AssertionReaction &reaction
+    ) {
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( resultType, LazyExpression( false ) );
+        AssertionResult assertionResult{ info, data };
+        assertionEnded( assertionResult );
+
+        if( !assertionResult.isOk() )
+            populateReaction( reaction );
+    }
+
+    IResultCapture& getResultCapture() {
+        if (auto* capture = getCurrentContext().getResultCapture())
+            return *capture;
+        else
+            CATCH_INTERNAL_ERROR("No result capture instance");
+    }
+
+    void seedRng(IConfig const& config) {
+        if (config.rngSeed() != 0) {
+            std::srand(config.rngSeed());
+            rng().seed(config.rngSeed());
+        }
+    }
+
+    unsigned int rngSeed() {
+        return getCurrentContext().getConfig()->rngSeed();
+    }
+
+}
+// end catch_run_context.cpp
+// start catch_section.cpp
+
+namespace Catch {
+
+    Section::Section( SectionInfo const& info )
+    :   m_info( info ),
+        m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+    {
+        m_timer.start();
+    }
+
+    Section::~Section() {
+        if( m_sectionIncluded ) {
+            SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() };
+            if( uncaught_exceptions() )
+                getResultCapture().sectionEndedEarly( endInfo );
+            else
+                getResultCapture().sectionEnded( endInfo );
+        }
+    }
+
+    // This indicates whether the section should be executed or not
+    Section::operator bool() const {
+        return m_sectionIncluded;
+    }
+
+} // end namespace Catch
+// end catch_section.cpp
+// start catch_section_info.cpp
+
+namespace Catch {
+
+    SectionInfo::SectionInfo
+        (   SourceLineInfo const& _lineInfo,
+            std::string const& _name )
+    :   name( _name ),
+        lineInfo( _lineInfo )
+    {}
+
+} // end namespace Catch
+// end catch_section_info.cpp
+// start catch_session.cpp
+
+// start catch_session.h
+
+#include <memory>
+
+namespace Catch {
+
+    class Session : NonCopyable {
+    public:
+
+        Session();
+        ~Session() override;
+
+        void showHelp() const;
+        void libIdentify();
+
+        int applyCommandLine( int argc, char const * const * argv );
+    #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
+        int applyCommandLine( int argc, wchar_t const * const * argv );
+    #endif
+
+        void useConfigData( ConfigData const& configData );
+
+        template<typename CharT>
+        int run(int argc, CharT const * const argv[]) {
+            if (m_startupExceptions)
+                return 1;
+            int returnCode = applyCommandLine(argc, argv);
+            if (returnCode == 0)
+                returnCode = run();
+            return returnCode;
+        }
+
+        int run();
+
+        clara::Parser const& cli() const;
+        void cli( clara::Parser const& newParser );
+        ConfigData& configData();
+        Config& config();
+    private:
+        int runInternal();
+
+        clara::Parser m_cli;
+        ConfigData m_configData;
+        std::shared_ptr<Config> m_config;
+        bool m_startupExceptions = false;
+    };
+
+} // end namespace Catch
+
+// end catch_session.h
+// start catch_version.h
+
+#include <iosfwd>
+
+namespace Catch {
+
+    // Versioning information
+    struct Version {
+        Version( Version const& ) = delete;
+        Version& operator=( Version const& ) = delete;
+        Version(    unsigned int _majorVersion,
+                    unsigned int _minorVersion,
+                    unsigned int _patchNumber,
+                    char const * const _branchName,
+                    unsigned int _buildNumber );
+
+        unsigned int const majorVersion;
+        unsigned int const minorVersion;
+        unsigned int const patchNumber;
+
+        // buildNumber is only used if branchName is not null
+        char const * const branchName;
+        unsigned int const buildNumber;
+
+        friend std::ostream& operator << ( std::ostream& os, Version const& version );
+    };
+
+    Version const& libraryVersion();
+}
+
+// end catch_version.h
+#include <cstdlib>
+#include <iomanip>
+#include <set>
+#include <iterator>
+
+namespace Catch {
+
+    namespace {
+        const int MaxExitCode = 255;
+
+        IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
+            auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
+            CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
+
+            return reporter;
+        }
+
+        IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
+            if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) {
+                return createReporter(config->getReporterName(), config);
+            }
+
+            // On older platforms, returning std::unique_ptr<ListeningReporter>
+            // when the return type is std::unique_ptr<IStreamingReporter>
+            // doesn't compile without a std::move call. However, this causes
+            // a warning on newer platforms. Thus, we have to work around
+            // it a bit and downcast the pointer manually.
+            auto ret = std::unique_ptr<IStreamingReporter>(new ListeningReporter);
+            auto& multi = static_cast<ListeningReporter&>(*ret);
+            auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
+            for (auto const& listener : listeners) {
+                multi.addListener(listener->create(Catch::ReporterConfig(config)));
+            }
+            multi.addReporter(createReporter(config->getReporterName(), config));
+            return ret;
+        }
+
+        class TestGroup {
+        public:
+            explicit TestGroup(std::shared_ptr<Config> const& config)
+            : m_config{config}
+            , m_context{config, makeReporter(config)}
+            {
+                auto const& allTestCases = getAllTestCasesSorted(*m_config);
+                m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
+                auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
+
+                if (m_matches.empty() && invalidArgs.empty()) {
+                    for (auto const& test : allTestCases)
+                        if (!test.isHidden())
+                            m_tests.emplace(&test);
+                } else {
+                    for (auto const& match : m_matches)
+                        m_tests.insert(match.tests.begin(), match.tests.end());
+                }
+            }
+
+            Totals execute() {
+                auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
+                Totals totals;
+                m_context.testGroupStarting(m_config->name(), 1, 1);
+                for (auto const& testCase : m_tests) {
+                    if (!m_context.aborting())
+                        totals += m_context.runTest(*testCase);
+                    else
+                        m_context.reporter().skipTest(*testCase);
+                }
+
+                for (auto const& match : m_matches) {
+                    if (match.tests.empty()) {
+                        m_context.reporter().noMatchingTestCases(match.name);
+                        totals.error = -1;
+                    }
+                }
+
+                if (!invalidArgs.empty()) {
+                    for (auto const& invalidArg: invalidArgs)
+                         m_context.reporter().reportInvalidArguments(invalidArg);
+                }
+
+                m_context.testGroupEnded(m_config->name(), totals, 1, 1);
+                return totals;
+            }
+
+        private:
+            using Tests = std::set<TestCase const*>;
+
+            std::shared_ptr<Config> m_config;
+            RunContext m_context;
+            Tests m_tests;
+            TestSpec::Matches m_matches;
+        };
+
+        void applyFilenamesAsTags(Catch::IConfig const& config) {
+            auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
+            for (auto& testCase : tests) {
+                auto tags = testCase.tags;
+
+                std::string filename = testCase.lineInfo.file;
+                auto lastSlash = filename.find_last_of("\\/");
+                if (lastSlash != std::string::npos) {
+                    filename.erase(0, lastSlash);
+                    filename[0] = '#';
+                }
+
+                auto lastDot = filename.find_last_of('.');
+                if (lastDot != std::string::npos) {
+                    filename.erase(lastDot);
+                }
+
+                tags.push_back(std::move(filename));
+                setTags(testCase, tags);
+            }
+        }
+
+    } // anon namespace
+
+    Session::Session() {
+        static bool alreadyInstantiated = false;
+        if( alreadyInstantiated ) {
+            CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
+            CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); }
+        }
+
+        // There cannot be exceptions at startup in no-exception mode.
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+        const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
+        if ( !exceptions.empty() ) {
+            config();
+            getCurrentMutableContext().setConfig(m_config);
+
+            m_startupExceptions = true;
+            Colour colourGuard( Colour::Red );
+            Catch::cerr() << "Errors occurred during startup!" << '\n';
+            // iterate over all exceptions and notify user
+            for ( const auto& ex_ptr : exceptions ) {
+                try {
+                    std::rethrow_exception(ex_ptr);
+                } catch ( std::exception const& ex ) {
+                    Catch::cerr() << Column( ex.what() ).indent(2) << '\n';
+                }
+            }
+        }
+#endif
+
+        alreadyInstantiated = true;
+        m_cli = makeCommandLineParser( m_configData );
+    }
+    Session::~Session() {
+        Catch::cleanUp();
+    }
+
+    void Session::showHelp() const {
+        Catch::cout()
+                << "\nCatch v" << libraryVersion() << "\n"
+                << m_cli << std::endl
+                << "For more detailed usage please see the project docs\n" << std::endl;
+    }
+    void Session::libIdentify() {
+        Catch::cout()
+                << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
+                << std::left << std::setw(16) << "category: " << "testframework\n"
+                << std::left << std::setw(16) << "framework: " << "Catch Test\n"
+                << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
+    }
+
+    int Session::applyCommandLine( int argc, char const * const * argv ) {
+        if( m_startupExceptions )
+            return 1;
+
+        auto result = m_cli.parse( clara::Args( argc, argv ) );
+        if( !result ) {
+            config();
+            getCurrentMutableContext().setConfig(m_config);
+            Catch::cerr()
+                << Colour( Colour::Red )
+                << "\nError(s) in input:\n"
+                << Column( result.errorMessage() ).indent( 2 )
+                << "\n\n";
+            Catch::cerr() << "Run with -? for usage\n" << std::endl;
+            return MaxExitCode;
+        }
+
+        if( m_configData.showHelp )
+            showHelp();
+        if( m_configData.libIdentify )
+            libIdentify();
+        m_config.reset();
+        return 0;
+    }
+
+#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
+    int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
+
+        char **utf8Argv = new char *[ argc ];
+
+        for ( int i = 0; i < argc; ++i ) {
+            int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr );
+
+            utf8Argv[ i ] = new char[ bufSize ];
+
+            WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr );
+        }
+
+        int returnCode = applyCommandLine( argc, utf8Argv );
+
+        for ( int i = 0; i < argc; ++i )
+            delete [] utf8Argv[ i ];
+
+        delete [] utf8Argv;
+
+        return returnCode;
+    }
+#endif
+
+    void Session::useConfigData( ConfigData const& configData ) {
+        m_configData = configData;
+        m_config.reset();
+    }
+
+    int Session::run() {
+        if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
+            Catch::cout() << "...waiting for enter/ return before starting" << std::endl;
+            static_cast<void>(std::getchar());
+        }
+        int exitCode = runInternal();
+        if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
+            Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl;
+            static_cast<void>(std::getchar());
+        }
+        return exitCode;
+    }
+
+    clara::Parser const& Session::cli() const {
+        return m_cli;
+    }
+    void Session::cli( clara::Parser const& newParser ) {
+        m_cli = newParser;
+    }
+    ConfigData& Session::configData() {
+        return m_configData;
+    }
+    Config& Session::config() {
+        if( !m_config )
+            m_config = std::make_shared<Config>( m_configData );
+        return *m_config;
+    }
+
+    int Session::runInternal() {
+        if( m_startupExceptions )
+            return 1;
+
+        if (m_configData.showHelp || m_configData.libIdentify) {
+            return 0;
+        }
+
+        CATCH_TRY {
+            config(); // Force config to be constructed
+
+            seedRng( *m_config );
+
+            if( m_configData.filenamesAsTags )
+                applyFilenamesAsTags( *m_config );
+
+            // Handle list request
+            if( Option<std::size_t> listed = list( m_config ) )
+                return static_cast<int>( *listed );
+
+            TestGroup tests { m_config };
+            auto const totals = tests.execute();
+
+            if( m_config->warnAboutNoTests() && totals.error == -1 )
+                return 2;
+
+            // Note that on unices only the lower 8 bits are usually used, clamping
+            // the return value to 255 prevents false negative when some multiple
+            // of 256 tests has failed
+            return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed)));
+        }
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+        catch( std::exception& ex ) {
+            Catch::cerr() << ex.what() << std::endl;
+            return MaxExitCode;
+        }
+#endif
+    }
+
+} // end namespace Catch
+// end catch_session.cpp
+// start catch_singletons.cpp
+
+#include <vector>
+
+namespace Catch {
+
+    namespace {
+        static auto getSingletons() -> std::vector<ISingleton*>*& {
+            static std::vector<ISingleton*>* g_singletons = nullptr;
+            if( !g_singletons )
+                g_singletons = new std::vector<ISingleton*>();
+            return g_singletons;
+        }
+    }
+
+    ISingleton::~ISingleton() {}
+
+    void addSingleton(ISingleton* singleton ) {
+        getSingletons()->push_back( singleton );
+    }
+    void cleanupSingletons() {
+        auto& singletons = getSingletons();
+        for( auto singleton : *singletons )
+            delete singleton;
+        delete singletons;
+        singletons = nullptr;
+    }
+
+} // namespace Catch
+// end catch_singletons.cpp
+// start catch_startup_exception_registry.cpp
+
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+namespace Catch {
+void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept {
+        CATCH_TRY {
+            m_exceptions.push_back(exception);
+        } CATCH_CATCH_ALL {
+            // If we run out of memory during start-up there's really not a lot more we can do about it
+            std::terminate();
+        }
+    }
+
+    std::vector<std::exception_ptr> const& StartupExceptionRegistry::getExceptions() const noexcept {
+        return m_exceptions;
+    }
+
+} // end namespace Catch
+#endif
+// end catch_startup_exception_registry.cpp
+// start catch_stream.cpp
+
+#include <cstdio>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+    Catch::IStream::~IStream() = default;
+
+    namespace Detail { namespace {
+        template<typename WriterF, std::size_t bufferSize=256>
+        class StreamBufImpl : public std::streambuf {
+            char data[bufferSize];
+            WriterF m_writer;
+
+        public:
+            StreamBufImpl() {
+                setp( data, data + sizeof(data) );
+            }
+
+            ~StreamBufImpl() noexcept {
+                StreamBufImpl::sync();
+            }
+
+        private:
+            int overflow( int c ) override {
+                sync();
+
+                if( c != EOF ) {
+                    if( pbase() == epptr() )
+                        m_writer( std::string( 1, static_cast<char>( c ) ) );
+                    else
+                        sputc( static_cast<char>( c ) );
+                }
+                return 0;
+            }
+
+            int sync() override {
+                if( pbase() != pptr() ) {
+                    m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+                    setp( pbase(), epptr() );
+                }
+                return 0;
+            }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        struct OutputDebugWriter {
+
+            void operator()( std::string const&str ) {
+                writeToDebugConsole( str );
+            }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        class FileStream : public IStream {
+            mutable std::ofstream m_ofs;
+        public:
+            FileStream( StringRef filename ) {
+                m_ofs.open( filename.c_str() );
+                CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" );
+            }
+            ~FileStream() override = default;
+        public: // IStream
+            std::ostream& stream() const override {
+                return m_ofs;
+            }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        class CoutStream : public IStream {
+            mutable std::ostream m_os;
+        public:
+            // Store the streambuf from cout up-front because
+            // cout may get redirected when running tests
+            CoutStream() : m_os( Catch::cout().rdbuf() ) {}
+            ~CoutStream() override = default;
+
+        public: // IStream
+            std::ostream& stream() const override { return m_os; }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        class DebugOutStream : public IStream {
+            std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
+            mutable std::ostream m_os;
+        public:
+            DebugOutStream()
+            :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+                m_os( m_streamBuf.get() )
+            {}
+
+            ~DebugOutStream() override = default;
+
+        public: // IStream
+            std::ostream& stream() const override { return m_os; }
+        };
+
+    }} // namespace anon::detail
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    auto makeStream( StringRef const &filename ) -> IStream const* {
+        if( filename.empty() )
+            return new Detail::CoutStream();
+        else if( filename[0] == '%' ) {
+            if( filename == "%debug" )
+                return new Detail::DebugOutStream();
+            else
+                CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
+        }
+        else
+            return new Detail::FileStream( filename );
+    }
+
+    // This class encapsulates the idea of a pool of ostringstreams that can be reused.
+    struct StringStreams {
+        std::vector<std::unique_ptr<std::ostringstream>> m_streams;
+        std::vector<std::size_t> m_unused;
+        std::ostringstream m_referenceStream; // Used for copy state/ flags from
+
+        auto add() -> std::size_t {
+            if( m_unused.empty() ) {
+                m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) );
+                return m_streams.size()-1;
+            }
+            else {
+                auto index = m_unused.back();
+                m_unused.pop_back();
+                return index;
+            }
+        }
+
+        void release( std::size_t index ) {
+            m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
+            m_unused.push_back(index);
+        }
+    };
+
+    ReusableStringStream::ReusableStringStream()
+    :   m_index( Singleton<StringStreams>::getMutable().add() ),
+        m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
+    {}
+
+    ReusableStringStream::~ReusableStringStream() {
+        static_cast<std::ostringstream*>( m_oss )->str("");
+        m_oss->clear();
+        Singleton<StringStreams>::getMutable().release( m_index );
+    }
+
+    auto ReusableStringStream::str() const -> std::string {
+        return static_cast<std::ostringstream*>( m_oss )->str();
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
+    std::ostream& cout() { return std::cout; }
+    std::ostream& cerr() { return std::cerr; }
+    std::ostream& clog() { return std::clog; }
+#endif
+}
+// end catch_stream.cpp
+// start catch_string_manip.cpp
+
+#include <algorithm>
+#include <ostream>
+#include <cstring>
+#include <cctype>
+#include <vector>
+
+namespace Catch {
+
+    namespace {
+        char toLowerCh(char c) {
+            return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) );
+        }
+    }
+
+    bool startsWith( std::string const& s, std::string const& prefix ) {
+        return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
+    }
+    bool startsWith( std::string const& s, char prefix ) {
+        return !s.empty() && s[0] == prefix;
+    }
+    bool endsWith( std::string const& s, std::string const& suffix ) {
+        return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
+    }
+    bool endsWith( std::string const& s, char suffix ) {
+        return !s.empty() && s[s.size()-1] == suffix;
+    }
+    bool contains( std::string const& s, std::string const& infix ) {
+        return s.find( infix ) != std::string::npos;
+    }
+    void toLowerInPlace( std::string& s ) {
+        std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
+    }
+    std::string toLower( std::string const& s ) {
+        std::string lc = s;
+        toLowerInPlace( lc );
+        return lc;
+    }
+    std::string trim( std::string const& str ) {
+        static char const* whitespaceChars = "\n\r\t ";
+        std::string::size_type start = str.find_first_not_of( whitespaceChars );
+        std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+        return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
+    }
+
+    StringRef trim(StringRef ref) {
+        const auto is_ws = [](char c) {
+            return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+        };
+        size_t real_begin = 0;
+        while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; }
+        size_t real_end = ref.size();
+        while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; }
+
+        return ref.substr(real_begin, real_end - real_begin);
+    }
+
+    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+        bool replaced = false;
+        std::size_t i = str.find( replaceThis );
+        while( i != std::string::npos ) {
+            replaced = true;
+            str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+            if( i < str.size()-withThis.size() )
+                i = str.find( replaceThis, i+withThis.size() );
+            else
+                i = std::string::npos;
+        }
+        return replaced;
+    }
+
+    std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) {
+        std::vector<StringRef> subStrings;
+        std::size_t start = 0;
+        for(std::size_t pos = 0; pos < str.size(); ++pos ) {
+            if( str[pos] == delimiter ) {
+                if( pos - start > 1 )
+                    subStrings.push_back( str.substr( start, pos-start ) );
+                start = pos+1;
+            }
+        }
+        if( start < str.size() )
+            subStrings.push_back( str.substr( start, str.size()-start ) );
+        return subStrings;
+    }
+
+    pluralise::pluralise( std::size_t count, std::string const& label )
+    :   m_count( count ),
+        m_label( label )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+        os << pluraliser.m_count << ' ' << pluraliser.m_label;
+        if( pluraliser.m_count != 1 )
+            os << 's';
+        return os;
+    }
+
+}
+// end catch_string_manip.cpp
+// start catch_stringref.cpp
+
+#include <algorithm>
+#include <ostream>
+#include <cstring>
+#include <cstdint>
+
+namespace Catch {
+    StringRef::StringRef( char const* rawChars ) noexcept
+    : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
+    {}
+
+    auto StringRef::c_str() const -> char const* {
+        CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
+        return m_start;
+    }
+    auto StringRef::data() const noexcept -> char const* {
+        return m_start;
+    }
+
+    auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
+        if (start < m_size) {
+            return StringRef(m_start + start, (std::min)(m_size - start, size));
+        } else {
+            return StringRef();
+        }
+    }
+    auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
+        return m_size == other.m_size
+            && (std::memcmp( m_start, other.m_start, m_size ) == 0);
+    }
+
+    auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
+        return os.write(str.data(), str.size());
+    }
+
+    auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
+        lhs.append(rhs.data(), rhs.size());
+        return lhs;
+    }
+
+} // namespace Catch
+// end catch_stringref.cpp
+// start catch_tag_alias.cpp
+
+namespace Catch {
+    TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {}
+}
+// end catch_tag_alias.cpp
+// start catch_tag_alias_autoregistrar.cpp
+
+namespace Catch {
+
+    RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) {
+        CATCH_TRY {
+            getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo);
+        } CATCH_CATCH_ALL {
+            // Do not throw when constructing global objects, instead register the exception to be processed later
+            getMutableRegistryHub().registerStartupException();
+        }
+    }
+
+}
+// end catch_tag_alias_autoregistrar.cpp
+// start catch_tag_alias_registry.cpp
+
+#include <sstream>
+
+namespace Catch {
+
+    TagAliasRegistry::~TagAliasRegistry() {}
+
+    TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
+        auto it = m_registry.find( alias );
+        if( it != m_registry.end() )
+            return &(it->second);
+        else
+            return nullptr;
+    }
+
+    std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+        std::string expandedTestSpec = unexpandedTestSpec;
+        for( auto const& registryKvp : m_registry ) {
+            std::size_t pos = expandedTestSpec.find( registryKvp.first );
+            if( pos != std::string::npos ) {
+                expandedTestSpec =  expandedTestSpec.substr( 0, pos ) +
+                                    registryKvp.second.tag +
+                                    expandedTestSpec.substr( pos + registryKvp.first.size() );
+            }
+        }
+        return expandedTestSpec;
+    }
+
+    void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
+        CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'),
+                      "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo );
+
+        CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second,
+                      "error: tag alias, '" << alias << "' already registered.\n"
+                      << "\tFirst seen at: " << find(alias)->lineInfo << "\n"
+                      << "\tRedefined at: " << lineInfo );
+    }
+
+    ITagAliasRegistry::~ITagAliasRegistry() {}
+
+    ITagAliasRegistry const& ITagAliasRegistry::get() {
+        return getRegistryHub().getTagAliasRegistry();
+    }
+
+} // end namespace Catch
+// end catch_tag_alias_registry.cpp
+// start catch_test_case_info.cpp
+
+#include <cctype>
+#include <exception>
+#include <algorithm>
+#include <sstream>
+
+namespace Catch {
+
+    namespace {
+        TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+            if( startsWith( tag, '.' ) ||
+                tag == "!hide" )
+                return TestCaseInfo::IsHidden;
+            else if( tag == "!throws" )
+                return TestCaseInfo::Throws;
+            else if( tag == "!shouldfail" )
+                return TestCaseInfo::ShouldFail;
+            else if( tag == "!mayfail" )
+                return TestCaseInfo::MayFail;
+            else if( tag == "!nonportable" )
+                return TestCaseInfo::NonPortable;
+            else if( tag == "!benchmark" )
+                return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden );
+            else
+                return TestCaseInfo::None;
+        }
+        bool isReservedTag( std::string const& tag ) {
+            return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast<unsigned char>(tag[0]) );
+        }
+        void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+            CATCH_ENFORCE( !isReservedTag(tag),
+                          "Tag name: [" << tag << "] is not allowed.\n"
+                          << "Tag names starting with non alphanumeric characters are reserved\n"
+                          << _lineInfo );
+        }
+    }
+
+    TestCase makeTestCase(  ITestInvoker* _testCase,
+                            std::string const& _className,
+                            NameAndTags const& nameAndTags,
+                            SourceLineInfo const& _lineInfo )
+    {
+        bool isHidden = false;
+
+        // Parse out tags
+        std::vector<std::string> tags;
+        std::string desc, tag;
+        bool inTag = false;
+        for (char c : nameAndTags.tags) {
+            if( !inTag ) {
+                if( c == '[' )
+                    inTag = true;
+                else
+                    desc += c;
+            }
+            else {
+                if( c == ']' ) {
+                    TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
+                    if( ( prop & TestCaseInfo::IsHidden ) != 0 )
+                        isHidden = true;
+                    else if( prop == TestCaseInfo::None )
+                        enforceNotReservedTag( tag, _lineInfo );
+
+                    // Merged hide tags like `[.approvals]` should be added as
+                    // `[.][approvals]`. The `[.]` is added at later point, so
+                    // we only strip the prefix
+                    if (startsWith(tag, '.') && tag.size() > 1) {
+                        tag.erase(0, 1);
+                    }
+                    tags.push_back( tag );
+                    tag.clear();
+                    inTag = false;
+                }
+                else
+                    tag += c;
+            }
+        }
+        if( isHidden ) {
+            // Add all "hidden" tags to make them behave identically
+            tags.insert( tags.end(), { ".", "!hide" } );
+        }
+
+        TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo );
+        return TestCase( _testCase, std::move(info) );
+    }
+
+    void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) {
+        std::sort(begin(tags), end(tags));
+        tags.erase(std::unique(begin(tags), end(tags)), end(tags));
+        testCaseInfo.lcaseTags.clear();
+
+        for( auto const& tag : tags ) {
+            std::string lcaseTag = toLower( tag );
+            testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
+            testCaseInfo.lcaseTags.push_back( lcaseTag );
+        }
+        testCaseInfo.tags = std::move(tags);
+    }
+
+    TestCaseInfo::TestCaseInfo( std::string const& _name,
+                                std::string const& _className,
+                                std::string const& _description,
+                                std::vector<std::string> const& _tags,
+                                SourceLineInfo const& _lineInfo )
+    :   name( _name ),
+        className( _className ),
+        description( _description ),
+        lineInfo( _lineInfo ),
+        properties( None )
+    {
+        setTags( *this, _tags );
+    }
+
+    bool TestCaseInfo::isHidden() const {
+        return ( properties & IsHidden ) != 0;
+    }
+    bool TestCaseInfo::throws() const {
+        return ( properties & Throws ) != 0;
+    }
+    bool TestCaseInfo::okToFail() const {
+        return ( properties & (ShouldFail | MayFail ) ) != 0;
+    }
+    bool TestCaseInfo::expectedToFail() const {
+        return ( properties & (ShouldFail ) ) != 0;
+    }
+
+    std::string TestCaseInfo::tagsAsString() const {
+        std::string ret;
+        // '[' and ']' per tag
+        std::size_t full_size = 2 * tags.size();
+        for (const auto& tag : tags) {
+            full_size += tag.size();
+        }
+        ret.reserve(full_size);
+        for (const auto& tag : tags) {
+            ret.push_back('[');
+            ret.append(tag);
+            ret.push_back(']');
+        }
+
+        return ret;
+    }
+
+    TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {}
+
+    TestCase TestCase::withName( std::string const& _newName ) const {
+        TestCase other( *this );
+        other.name = _newName;
+        return other;
+    }
+
+    void TestCase::invoke() const {
+        test->invoke();
+    }
+
+    bool TestCase::operator == ( TestCase const& other ) const {
+        return  test.get() == other.test.get() &&
+                name == other.name &&
+                className == other.className;
+    }
+
+    bool TestCase::operator < ( TestCase const& other ) const {
+        return name < other.name;
+    }
+
+    TestCaseInfo const& TestCase::getTestCaseInfo() const
+    {
+        return *this;
+    }
+
+} // end namespace Catch
+// end catch_test_case_info.cpp
+// start catch_test_case_registry_impl.cpp
+
+#include <algorithm>
+#include <sstream>
+
+namespace Catch {
+
+    namespace {
+        struct TestHasher {
+            using hash_t = uint64_t;
+
+            explicit TestHasher( hash_t hashSuffix ):
+                m_hashSuffix{ hashSuffix } {}
+
+            uint32_t operator()( TestCase const& t ) const {
+                // FNV-1a hash with multiplication fold.
+                const hash_t prime = 1099511628211u;
+                hash_t hash = 14695981039346656037u;
+                for ( const char c : t.name ) {
+                    hash ^= c;
+                    hash *= prime;
+                }
+                hash ^= m_hashSuffix;
+                hash *= prime;
+                const uint32_t low{ static_cast<uint32_t>( hash ) };
+                const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) };
+                return low * high;
+            }
+
+        private:
+            hash_t m_hashSuffix;
+        };
+    } // end unnamed namespace
+
+    std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+        switch( config.runOrder() ) {
+            case RunTests::InDeclarationOrder:
+                // already in declaration order
+                break;
+
+            case RunTests::InLexicographicalOrder: {
+                std::vector<TestCase> sorted = unsortedTestCases;
+                std::sort( sorted.begin(), sorted.end() );
+                return sorted;
+            }
+
+            case RunTests::InRandomOrder: {
+                seedRng( config );
+                TestHasher h{ config.rngSeed() };
+
+                using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>;
+                std::vector<hashedTest> indexed_tests;
+                indexed_tests.reserve( unsortedTestCases.size() );
+
+                for (auto const& testCase : unsortedTestCases) {
+                    indexed_tests.emplace_back(h(testCase), &testCase);
+                }
+
+                std::sort(indexed_tests.begin(), indexed_tests.end(),
+                          [](hashedTest const& lhs, hashedTest const& rhs) {
+                          if (lhs.first == rhs.first) {
+                              return lhs.second->name < rhs.second->name;
+                          }
+                          return lhs.first < rhs.first;
+                });
+
+                std::vector<TestCase> sorted;
+                sorted.reserve( indexed_tests.size() );
+
+                for (auto const& hashed : indexed_tests) {
+                    sorted.emplace_back(*hashed.second);
+                }
+
+                return sorted;
+            }
+        }
+        return unsortedTestCases;
+    }
+
+    bool isThrowSafe( TestCase const& testCase, IConfig const& config ) {
+        return !testCase.throws() || config.allowThrows();
+    }
+
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+        return testSpec.matches( testCase ) && isThrowSafe( testCase, config );
+    }
+
+    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+        std::set<TestCase> seenFunctions;
+        for( auto const& function : functions ) {
+            auto prev = seenFunctions.insert( function );
+            CATCH_ENFORCE( prev.second,
+                    "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n"
+                    << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
+                    << "\tRedefined at " << function.getTestCaseInfo().lineInfo );
+        }
+    }
+
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+        std::vector<TestCase> filtered;
+        filtered.reserve( testCases.size() );
+        for (auto const& testCase : testCases) {
+            if ((!testSpec.hasFilters() && !testCase.isHidden()) ||
+                (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
+                filtered.push_back(testCase);
+            }
+        }
+        return filtered;
+    }
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+        return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+    }
+
+    void TestRegistry::registerTest( TestCase const& testCase ) {
+        std::string name = testCase.getTestCaseInfo().name;
+        if( name.empty() ) {
+            ReusableStringStream rss;
+            rss << "Anonymous test case " << ++m_unnamedCount;
+            return registerTest( testCase.withName( rss.str() ) );
+        }
+        m_functions.push_back( testCase );
+    }
+
+    std::vector<TestCase> const& TestRegistry::getAllTests() const {
+        return m_functions;
+    }
+    std::vector<TestCase> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const {
+        if( m_sortedFunctions.empty() )
+            enforceNoDuplicateTestCases( m_functions );
+
+        if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+            m_sortedFunctions = sortTests( config, m_functions );
+            m_currentSortOrder = config.runOrder();
+        }
+        return m_sortedFunctions;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {}
+
+    void TestInvokerAsFunction::invoke() const {
+        m_testAsFunction();
+    }
+
+    std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
+        std::string className(classOrQualifiedMethodName);
+        if( startsWith( className, '&' ) )
+        {
+            std::size_t lastColons = className.rfind( "::" );
+            std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+            if( penultimateColons == std::string::npos )
+                penultimateColons = 1;
+            className = className.substr( penultimateColons, lastColons-penultimateColons );
+        }
+        return className;
+    }
+
+} // end namespace Catch
+// end catch_test_case_registry_impl.cpp
+// start catch_test_case_tracker.cpp
+
+#include <algorithm>
+#include <cassert>
+#include <stdexcept>
+#include <memory>
+#include <sstream>
+
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+
+namespace Catch {
+namespace TestCaseTracking {
+
+    NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
+    :   name( _name ),
+        location( _location )
+    {}
+
+    ITracker::~ITracker() = default;
+
+    ITracker& TrackerContext::startRun() {
+        m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
+        m_currentTracker = nullptr;
+        m_runState = Executing;
+        return *m_rootTracker;
+    }
+
+    void TrackerContext::endRun() {
+        m_rootTracker.reset();
+        m_currentTracker = nullptr;
+        m_runState = NotStarted;
+    }
+
+    void TrackerContext::startCycle() {
+        m_currentTracker = m_rootTracker.get();
+        m_runState = Executing;
+    }
+    void TrackerContext::completeCycle() {
+        m_runState = CompletedCycle;
+    }
+
+    bool TrackerContext::completedCycle() const {
+        return m_runState == CompletedCycle;
+    }
+    ITracker& TrackerContext::currentTracker() {
+        return *m_currentTracker;
+    }
+    void TrackerContext::setCurrentTracker( ITracker* tracker ) {
+        m_currentTracker = tracker;
+    }
+
+    TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
+        ITracker(nameAndLocation),
+        m_ctx( ctx ),
+        m_parent( parent )
+    {}
+
+    bool TrackerBase::isComplete() const {
+        return m_runState == CompletedSuccessfully || m_runState == Failed;
+    }
+    bool TrackerBase::isSuccessfullyCompleted() const {
+        return m_runState == CompletedSuccessfully;
+    }
+    bool TrackerBase::isOpen() const {
+        return m_runState != NotStarted && !isComplete();
+    }
+    bool TrackerBase::hasChildren() const {
+        return !m_children.empty();
+    }
+
+    void TrackerBase::addChild( ITrackerPtr const& child ) {
+        m_children.push_back( child );
+    }
+
+    ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) {
+        auto it = std::find_if( m_children.begin(), m_children.end(),
+            [&nameAndLocation]( ITrackerPtr const& tracker ){
+                return
+                    tracker->nameAndLocation().location == nameAndLocation.location &&
+                    tracker->nameAndLocation().name == nameAndLocation.name;
+            } );
+        return( it != m_children.end() )
+            ? *it
+            : nullptr;
+    }
+    ITracker& TrackerBase::parent() {
+        assert( m_parent ); // Should always be non-null except for root
+        return *m_parent;
+    }
+
+    void TrackerBase::openChild() {
+        if( m_runState != ExecutingChildren ) {
+            m_runState = ExecutingChildren;
+            if( m_parent )
+                m_parent->openChild();
+        }
+    }
+
+    bool TrackerBase::isSectionTracker() const { return false; }
+    bool TrackerBase::isGeneratorTracker() const { return false; }
+
+    void TrackerBase::open() {
+        m_runState = Executing;
+        moveToThis();
+        if( m_parent )
+            m_parent->openChild();
+    }
+
+    void TrackerBase::close() {
+
+        // Close any still open children (e.g. generators)
+        while( &m_ctx.currentTracker() != this )
+            m_ctx.currentTracker().close();
+
+        switch( m_runState ) {
+            case NeedsAnotherRun:
+                break;
+
+            case Executing:
+                m_runState = CompletedSuccessfully;
+                break;
+            case ExecutingChildren:
+                if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
+                    m_runState = CompletedSuccessfully;
+                break;
+
+            case NotStarted:
+            case CompletedSuccessfully:
+            case Failed:
+                CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
+
+            default:
+                CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
+        }
+        moveToParent();
+        m_ctx.completeCycle();
+    }
+    void TrackerBase::fail() {
+        m_runState = Failed;
+        if( m_parent )
+            m_parent->markAsNeedingAnotherRun();
+        moveToParent();
+        m_ctx.completeCycle();
+    }
+    void TrackerBase::markAsNeedingAnotherRun() {
+        m_runState = NeedsAnotherRun;
+    }
+
+    void TrackerBase::moveToParent() {
+        assert( m_parent );
+        m_ctx.setCurrentTracker( m_parent );
+    }
+    void TrackerBase::moveToThis() {
+        m_ctx.setCurrentTracker( this );
+    }
+
+    SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+    :   TrackerBase( nameAndLocation, ctx, parent ),
+        m_trimmed_name(trim(nameAndLocation.name))
+    {
+        if( parent ) {
+            while( !parent->isSectionTracker() )
+                parent = &parent->parent();
+
+            SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
+            addNextFilters( parentSection.m_filters );
+        }
+    }
+
+    bool SectionTracker::isComplete() const {
+        bool complete = true;
+
+        if (m_filters.empty()
+            || m_filters[0] == ""
+            || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
+            complete = TrackerBase::isComplete();
+        }
+        return complete;
+    }
+
+    bool SectionTracker::isSectionTracker() const { return true; }
+
+    SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
+        std::shared_ptr<SectionTracker> section;
+
+        ITracker& currentTracker = ctx.currentTracker();
+        if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+            assert( childTracker );
+            assert( childTracker->isSectionTracker() );
+            section = std::static_pointer_cast<SectionTracker>( childTracker );
+        }
+        else {
+            section = std::make_shared<SectionTracker>( nameAndLocation, ctx, &currentTracker );
+            currentTracker.addChild( section );
+        }
+        if( !ctx.completedCycle() )
+            section->tryOpen();
+        return *section;
+    }
+
+    void SectionTracker::tryOpen() {
+        if( !isComplete() )
+            open();
+    }
+
+    void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
+        if( !filters.empty() ) {
+            m_filters.reserve( m_filters.size() + filters.size() + 2 );
+            m_filters.emplace_back(""); // Root - should never be consulted
+            m_filters.emplace_back(""); // Test Case - not a section filter
+            m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
+        }
+    }
+    void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
+        if( filters.size() > 1 )
+            m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
+    }
+
+    std::vector<std::string> const& SectionTracker::getFilters() const {
+        return m_filters;
+    }
+
+    std::string const& SectionTracker::trimmedName() const {
+        return m_trimmed_name;
+    }
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+
+} // namespace Catch
+
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
+// end catch_test_case_tracker.cpp
+// start catch_test_registry.cpp
+
+namespace Catch {
+
+    auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* {
+        return new(std::nothrow) TestInvokerAsFunction( testAsFunction );
+    }
+
+    NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {}
+
+    AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept {
+        CATCH_TRY {
+            getMutableRegistryHub()
+                    .registerTest(
+                        makeTestCase(
+                            invoker,
+                            extractClassName( classOrMethod ),
+                            nameAndTags,
+                            lineInfo));
+        } CATCH_CATCH_ALL {
+            // Do not throw when constructing global objects, instead register the exception to be processed later
+            getMutableRegistryHub().registerStartupException();
+        }
+    }
+
+    AutoReg::~AutoReg() = default;
+}
+// end catch_test_registry.cpp
+// start catch_test_spec.cpp
+
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+    TestSpec::Pattern::Pattern( std::string const& name )
+    : m_name( name )
+    {}
+
+    TestSpec::Pattern::~Pattern() = default;
+
+    std::string const& TestSpec::Pattern::name() const {
+        return m_name;
+    }
+
+    TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString )
+    : Pattern( filterString )
+    , m_wildcardPattern( toLower( name ), CaseSensitive::No )
+    {}
+
+    bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
+        return m_wildcardPattern.matches( testCase.name );
+    }
+
+    TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
+    : Pattern( filterString )
+    , m_tag( toLower( tag ) )
+    {}
+
+    bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
+        return std::find(begin(testCase.lcaseTags),
+                         end(testCase.lcaseTags),
+                         m_tag) != end(testCase.lcaseTags);
+    }
+
+    TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern )
+    : Pattern( underlyingPattern->name() )
+    , m_underlyingPattern( underlyingPattern )
+    {}
+
+    bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const {
+        return !m_underlyingPattern->matches( testCase );
+    }
+
+    bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
+        return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } );
+    }
+
+    std::string TestSpec::Filter::name() const {
+        std::string name;
+        for( auto const& p : m_patterns )
+            name += p->name();
+        return name;
+    }
+
+    bool TestSpec::hasFilters() const {
+        return !m_filters.empty();
+    }
+
+    bool TestSpec::matches( TestCaseInfo const& testCase ) const {
+        return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
+    }
+
+    TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const
+    {
+        Matches matches( m_filters.size() );
+        std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
+            std::vector<TestCase const*> currentMatches;
+            for( auto const& test : testCases )
+                if( isThrowSafe( test, config ) && filter.matches( test ) )
+                    currentMatches.emplace_back( &test );
+            return FilterMatch{ filter.name(), currentMatches };
+        } );
+        return matches;
+    }
+
+    const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{
+        return  (m_invalidArgs);
+    }
+
+}
+// end catch_test_spec.cpp
+// start catch_test_spec_parser.cpp
+
+namespace Catch {
+
+    TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+    TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
+        m_mode = None;
+        m_exclusion = false;
+        m_arg = m_tagAliases->expandAliases( arg );
+        m_escapeChars.clear();
+        m_substring.reserve(m_arg.size());
+        m_patternName.reserve(m_arg.size());
+        m_realPatternPos = 0;
+
+        for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+          //if visitChar fails
+           if( !visitChar( m_arg[m_pos] ) ){
+               m_testSpec.m_invalidArgs.push_back(arg);
+               break;
+           }
+        endMode();
+        return *this;
+    }
+    TestSpec TestSpecParser::testSpec() {
+        addFilter();
+        return m_testSpec;
+    }
+    bool TestSpecParser::visitChar( char c ) {
+        if( (m_mode != EscapedName) && (c == '\\') ) {
+            escape();
+            addCharToPattern(c);
+            return true;
+        }else if((m_mode != EscapedName) && (c == ',') )  {
+            return separate();
+        }
+
+        switch( m_mode ) {
+        case None:
+            if( processNoneChar( c ) )
+                return true;
+            break;
+        case Name:
+            processNameChar( c );
+            break;
+        case EscapedName:
+            endMode();
+            addCharToPattern(c);
+            return true;
+        default:
+        case Tag:
+        case QuotedName:
+            if( processOtherChar( c ) )
+                return true;
+            break;
+        }
+
+        m_substring += c;
+        if( !isControlChar( c ) ) {
+            m_patternName += c;
+            m_realPatternPos++;
+        }
+        return true;
+    }
+    // Two of the processing methods return true to signal the caller to return
+    // without adding the given character to the current pattern strings
+    bool TestSpecParser::processNoneChar( char c ) {
+        switch( c ) {
+        case ' ':
+            return true;
+        case '~':
+            m_exclusion = true;
+            return false;
+        case '[':
+            startNewMode( Tag );
+            return false;
+        case '"':
+            startNewMode( QuotedName );
+            return false;
+        default:
+            startNewMode( Name );
+            return false;
+        }
+    }
+    void TestSpecParser::processNameChar( char c ) {
+        if( c == '[' ) {
+            if( m_substring == "exclude:" )
+                m_exclusion = true;
+            else
+                endMode();
+            startNewMode( Tag );
+        }
+    }
+    bool TestSpecParser::processOtherChar( char c ) {
+        if( !isControlChar( c ) )
+            return false;
+        m_substring += c;
+        endMode();
+        return true;
+    }
+    void TestSpecParser::startNewMode( Mode mode ) {
+        m_mode = mode;
+    }
+    void TestSpecParser::endMode() {
+        switch( m_mode ) {
+        case Name:
+        case QuotedName:
+            return addNamePattern();
+        case Tag:
+            return addTagPattern();
+        case EscapedName:
+            revertBackToLastMode();
+            return;
+        case None:
+        default:
+            return startNewMode( None );
+        }
+    }
+    void TestSpecParser::escape() {
+        saveLastMode();
+        m_mode = EscapedName;
+        m_escapeChars.push_back(m_realPatternPos);
+    }
+    bool TestSpecParser::isControlChar( char c ) const {
+        switch( m_mode ) {
+            default:
+                return false;
+            case None:
+                return c == '~';
+            case Name:
+                return c == '[';
+            case EscapedName:
+                return true;
+            case QuotedName:
+                return c == '"';
+            case Tag:
+                return c == '[' || c == ']';
+        }
+    }
+
+    void TestSpecParser::addFilter() {
+        if( !m_currentFilter.m_patterns.empty() ) {
+            m_testSpec.m_filters.push_back( m_currentFilter );
+            m_currentFilter = TestSpec::Filter();
+        }
+    }
+
+    void TestSpecParser::saveLastMode() {
+      lastMode = m_mode;
+    }
+
+    void TestSpecParser::revertBackToLastMode() {
+      m_mode = lastMode;
+    }
+
+    bool TestSpecParser::separate() {
+      if( (m_mode==QuotedName) || (m_mode==Tag) ){
+         //invalid argument, signal failure to previous scope.
+         m_mode = None;
+         m_pos = m_arg.size();
+         m_substring.clear();
+         m_patternName.clear();
+         m_realPatternPos = 0;
+         return false;
+      }
+      endMode();
+      addFilter();
+      return true; //success
+    }
+
+    std::string TestSpecParser::preprocessPattern() {
+        std::string token = m_patternName;
+        for (std::size_t i = 0; i < m_escapeChars.size(); ++i)
+            token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1);
+        m_escapeChars.clear();
+        if (startsWith(token, "exclude:")) {
+            m_exclusion = true;
+            token = token.substr(8);
+        }
+
+        m_patternName.clear();
+        m_realPatternPos = 0;
+
+        return token;
+    }
+
+    void TestSpecParser::addNamePattern() {
+        auto token = preprocessPattern();
+
+        if (!token.empty()) {
+            TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring);
+            if (m_exclusion)
+                pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
+            m_currentFilter.m_patterns.push_back(pattern);
+        }
+        m_substring.clear();
+        m_exclusion = false;
+        m_mode = None;
+    }
+
+    void TestSpecParser::addTagPattern() {
+        auto token = preprocessPattern();
+
+        if (!token.empty()) {
+            // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo])
+            // we have to create a separate hide tag and shorten the real one
+            if (token.size() > 1 && token[0] == '.') {
+                token.erase(token.begin());
+                TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring);
+                if (m_exclusion) {
+                    pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
+                }
+                m_currentFilter.m_patterns.push_back(pattern);
+            }
+
+            TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring);
+
+            if (m_exclusion) {
+                pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
+            }
+            m_currentFilter.m_patterns.push_back(pattern);
+        }
+        m_substring.clear();
+        m_exclusion = false;
+        m_mode = None;
+    }
+
+    TestSpec parseTestSpec( std::string const& arg ) {
+        return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+    }
+
+} // namespace Catch
+// end catch_test_spec_parser.cpp
+// start catch_timer.cpp
+
+#include <chrono>
+
+static const uint64_t nanosecondsInSecond = 1000000000;
+
+namespace Catch {
+
+    auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
+        return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count();
+    }
+
+    namespace {
+        auto estimateClockResolution() -> uint64_t {
+            uint64_t sum = 0;
+            static const uint64_t iterations = 1000000;
+
+            auto startTime = getCurrentNanosecondsSinceEpoch();
+
+            for( std::size_t i = 0; i < iterations; ++i ) {
+
+                uint64_t ticks;
+                uint64_t baseTicks = getCurrentNanosecondsSinceEpoch();
+                do {
+                    ticks = getCurrentNanosecondsSinceEpoch();
+                } while( ticks == baseTicks );
+
+                auto delta = ticks - baseTicks;
+                sum += delta;
+
+                // If we have been calibrating for over 3 seconds -- the clock
+                // is terrible and we should move on.
+                // TBD: How to signal that the measured resolution is probably wrong?
+                if (ticks > startTime + 3 * nanosecondsInSecond) {
+                    return sum / ( i + 1u );
+                }
+            }
+
+            // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers
+            // - and potentially do more iterations if there's a high variance.
+            return sum/iterations;
+        }
+    }
+    auto getEstimatedClockResolution() -> uint64_t {
+        static auto s_resolution = estimateClockResolution();
+        return s_resolution;
+    }
+
+    void Timer::start() {
+       m_nanoseconds = getCurrentNanosecondsSinceEpoch();
+    }
+    auto Timer::getElapsedNanoseconds() const -> uint64_t {
+        return getCurrentNanosecondsSinceEpoch() - m_nanoseconds;
+    }
+    auto Timer::getElapsedMicroseconds() const -> uint64_t {
+        return getElapsedNanoseconds()/1000;
+    }
+    auto Timer::getElapsedMilliseconds() const -> unsigned int {
+        return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
+    }
+    auto Timer::getElapsedSeconds() const -> double {
+        return getElapsedMicroseconds()/1000000.0;
+    }
+
+} // namespace Catch
+// end catch_timer.cpp
+// start catch_tostring.cpp
+
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wexit-time-destructors"
+#    pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+
+// Enable specific decls locally
+#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#endif
+
+#include <cmath>
+#include <iomanip>
+
+namespace Catch {
+
+namespace Detail {
+
+    const std::string unprintableString = "{?}";
+
+    namespace {
+        const int hexThreshold = 255;
+
+        struct Endianness {
+            enum Arch { Big, Little };
+
+            static Arch which() {
+                int one = 1;
+                // If the lowest byte we read is non-zero, we can assume
+                // that little endian format is used.
+                auto value = *reinterpret_cast<char*>(&one);
+                return value ? Little : Big;
+            }
+        };
+    }
+
+    std::string rawMemoryToString( const void *object, std::size_t size ) {
+        // Reverse order for little endian architectures
+        int i = 0, end = static_cast<int>( size ), inc = 1;
+        if( Endianness::which() == Endianness::Little ) {
+            i = end-1;
+            end = inc = -1;
+        }
+
+        unsigned char const *bytes = static_cast<unsigned char const *>(object);
+        ReusableStringStream rss;
+        rss << "0x" << std::setfill('0') << std::hex;
+        for( ; i != end; i += inc )
+             rss << std::setw(2) << static_cast<unsigned>(bytes[i]);
+       return rss.str();
+    }
+}
+
+template<typename T>
+std::string fpToString( T value, int precision ) {
+    if (Catch::isnan(value)) {
+        return "nan";
+    }
+
+    ReusableStringStream rss;
+    rss << std::setprecision( precision )
+        << std::fixed
+        << value;
+    std::string d = rss.str();
+    std::size_t i = d.find_last_not_of( '0' );
+    if( i != std::string::npos && i != d.size()-1 ) {
+        if( d[i] == '.' )
+            i++;
+        d = d.substr( 0, i+1 );
+    }
+    return d;
+}
+
+//// ======================================================= ////
+//
+//   Out-of-line defs for full specialization of StringMaker
+//
+//// ======================================================= ////
+
+std::string StringMaker<std::string>::convert(const std::string& str) {
+    if (!getCurrentContext().getConfig()->showInvisibles()) {
+        return '"' + str + '"';
+    }
+
+    std::string s("\"");
+    for (char c : str) {
+        switch (c) {
+        case '\n':
+            s.append("\\n");
+            break;
+        case '\t':
+            s.append("\\t");
+            break;
+        default:
+            s.push_back(c);
+            break;
+        }
+    }
+    s.append("\"");
+    return s;
+}
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+std::string StringMaker<std::string_view>::convert(std::string_view str) {
+    return ::Catch::Detail::stringify(std::string{ str });
+}
+#endif
+
+std::string StringMaker<char const*>::convert(char const* str) {
+    if (str) {
+        return ::Catch::Detail::stringify(std::string{ str });
+    } else {
+        return{ "{null string}" };
+    }
+}
+std::string StringMaker<char*>::convert(char* str) {
+    if (str) {
+        return ::Catch::Detail::stringify(std::string{ str });
+    } else {
+        return{ "{null string}" };
+    }
+}
+
+#ifdef CATCH_CONFIG_WCHAR
+std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) {
+    std::string s;
+    s.reserve(wstr.size());
+    for (auto c : wstr) {
+        s += (c <= 0xff) ? static_cast<char>(c) : '?';
+    }
+    return ::Catch::Detail::stringify(s);
+}
+
+# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) {
+    return StringMaker<std::wstring>::convert(std::wstring(str));
+}
+# endif
+
+std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) {
+    if (str) {
+        return ::Catch::Detail::stringify(std::wstring{ str });
+    } else {
+        return{ "{null string}" };
+    }
+}
+std::string StringMaker<wchar_t *>::convert(wchar_t * str) {
+    if (str) {
+        return ::Catch::Detail::stringify(std::wstring{ str });
+    } else {
+        return{ "{null string}" };
+    }
+}
+#endif
+
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+#include <cstddef>
+std::string StringMaker<std::byte>::convert(std::byte value) {
+    return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value));
+}
+#endif // defined(CATCH_CONFIG_CPP17_BYTE)
+
+std::string StringMaker<int>::convert(int value) {
+    return ::Catch::Detail::stringify(static_cast<long long>(value));
+}
+std::string StringMaker<long>::convert(long value) {
+    return ::Catch::Detail::stringify(static_cast<long long>(value));
+}
+std::string StringMaker<long long>::convert(long long value) {
+    ReusableStringStream rss;
+    rss << value;
+    if (value > Detail::hexThreshold) {
+        rss << " (0x" << std::hex << value << ')';
+    }
+    return rss.str();
+}
+
+std::string StringMaker<unsigned int>::convert(unsigned int value) {
+    return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
+}
+std::string StringMaker<unsigned long>::convert(unsigned long value) {
+    return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
+}
+std::string StringMaker<unsigned long long>::convert(unsigned long long value) {
+    ReusableStringStream rss;
+    rss << value;
+    if (value > Detail::hexThreshold) {
+        rss << " (0x" << std::hex << value << ')';
+    }
+    return rss.str();
+}
+
+std::string StringMaker<bool>::convert(bool b) {
+    return b ? "true" : "false";
+}
+
+std::string StringMaker<signed char>::convert(signed char value) {
+    if (value == '\r') {
+        return "'\\r'";
+    } else if (value == '\f') {
+        return "'\\f'";
+    } else if (value == '\n') {
+        return "'\\n'";
+    } else if (value == '\t') {
+        return "'\\t'";
+    } else if ('\0' <= value && value < ' ') {
+        return ::Catch::Detail::stringify(static_cast<unsigned int>(value));
+    } else {
+        char chstr[] = "' '";
+        chstr[1] = value;
+        return chstr;
+    }
+}
+std::string StringMaker<char>::convert(char c) {
+    return ::Catch::Detail::stringify(static_cast<signed char>(c));
+}
+std::string StringMaker<unsigned char>::convert(unsigned char c) {
+    return ::Catch::Detail::stringify(static_cast<char>(c));
+}
+
+std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) {
+    return "nullptr";
+}
+
+int StringMaker<float>::precision = 5;
+
+std::string StringMaker<float>::convert(float value) {
+    return fpToString(value, precision) + 'f';
+}
+
+int StringMaker<double>::precision = 10;
+
+std::string StringMaker<double>::convert(double value) {
+    return fpToString(value, precision);
+}
+
+std::string ratio_string<std::atto>::symbol() { return "a"; }
+std::string ratio_string<std::femto>::symbol() { return "f"; }
+std::string ratio_string<std::pico>::symbol() { return "p"; }
+std::string ratio_string<std::nano>::symbol() { return "n"; }
+std::string ratio_string<std::micro>::symbol() { return "u"; }
+std::string ratio_string<std::milli>::symbol() { return "m"; }
+
+} // end namespace Catch
+
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
+
+// end catch_tostring.cpp
+// start catch_totals.cpp
+
+namespace Catch {
+
+    Counts Counts::operator - ( Counts const& other ) const {
+        Counts diff;
+        diff.passed = passed - other.passed;
+        diff.failed = failed - other.failed;
+        diff.failedButOk = failedButOk - other.failedButOk;
+        return diff;
+    }
+
+    Counts& Counts::operator += ( Counts const& other ) {
+        passed += other.passed;
+        failed += other.failed;
+        failedButOk += other.failedButOk;
+        return *this;
+    }
+
+    std::size_t Counts::total() const {
+        return passed + failed + failedButOk;
+    }
+    bool Counts::allPassed() const {
+        return failed == 0 && failedButOk == 0;
+    }
+    bool Counts::allOk() const {
+        return failed == 0;
+    }
+
+    Totals Totals::operator - ( Totals const& other ) const {
+        Totals diff;
+        diff.assertions = assertions - other.assertions;
+        diff.testCases = testCases - other.testCases;
+        return diff;
+    }
+
+    Totals& Totals::operator += ( Totals const& other ) {
+        assertions += other.assertions;
+        testCases += other.testCases;
+        return *this;
+    }
+
+    Totals Totals::delta( Totals const& prevTotals ) const {
+        Totals diff = *this - prevTotals;
+        if( diff.assertions.failed > 0 )
+            ++diff.testCases.failed;
+        else if( diff.assertions.failedButOk > 0 )
+            ++diff.testCases.failedButOk;
+        else
+            ++diff.testCases.passed;
+        return diff;
+    }
+
+}
+// end catch_totals.cpp
+// start catch_uncaught_exceptions.cpp
+
+// start catch_config_uncaught_exceptions.hpp
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+
+#if defined(_MSC_VER)
+#  if _MSC_VER >= 1900 // Visual Studio 2015 or newer
+#    define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#  endif
+#endif
+
+#include <exception>
+
+#if defined(__cpp_lib_uncaught_exceptions) \
+    && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+
+#  define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif // __cpp_lib_uncaught_exceptions
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \
+    && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \
+    && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+
+#  define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif
+
+#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+// end catch_config_uncaught_exceptions.hpp
+#include <exception>
+
+namespace Catch {
+    bool uncaught_exceptions() {
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+        return false;
+#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+        return std::uncaught_exceptions() > 0;
+#else
+        return std::uncaught_exception();
+#endif
+  }
+} // end namespace Catch
+// end catch_uncaught_exceptions.cpp
+// start catch_version.cpp
+
+#include <ostream>
+
+namespace Catch {
+
+    Version::Version
+        (   unsigned int _majorVersion,
+            unsigned int _minorVersion,
+            unsigned int _patchNumber,
+            char const * const _branchName,
+            unsigned int _buildNumber )
+    :   majorVersion( _majorVersion ),
+        minorVersion( _minorVersion ),
+        patchNumber( _patchNumber ),
+        branchName( _branchName ),
+        buildNumber( _buildNumber )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, Version const& version ) {
+        os  << version.majorVersion << '.'
+            << version.minorVersion << '.'
+            << version.patchNumber;
+        // branchName is never null -> 0th char is \0 if it is empty
+        if (version.branchName[0]) {
+            os << '-' << version.branchName
+               << '.' << version.buildNumber;
+        }
+        return os;
+    }
+
+    Version const& libraryVersion() {
+        static Version version( 2, 13, 6, "", 0 );
+        return version;
+    }
+
+}
+// end catch_version.cpp
+// start catch_wildcard_pattern.cpp
+
+namespace Catch {
+
+    WildcardPattern::WildcardPattern( std::string const& pattern,
+                                      CaseSensitive::Choice caseSensitivity )
+    :   m_caseSensitivity( caseSensitivity ),
+        m_pattern( normaliseString( pattern ) )
+    {
+        if( startsWith( m_pattern, '*' ) ) {
+            m_pattern = m_pattern.substr( 1 );
+            m_wildcard = WildcardAtStart;
+        }
+        if( endsWith( m_pattern, '*' ) ) {
+            m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+            m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+        }
+    }
+
+    bool WildcardPattern::matches( std::string const& str ) const {
+        switch( m_wildcard ) {
+            case NoWildcard:
+                return m_pattern == normaliseString( str );
+            case WildcardAtStart:
+                return endsWith( normaliseString( str ), m_pattern );
+            case WildcardAtEnd:
+                return startsWith( normaliseString( str ), m_pattern );
+            case WildcardAtBothEnds:
+                return contains( normaliseString( str ), m_pattern );
+            default:
+                CATCH_INTERNAL_ERROR( "Unknown enum" );
+        }
+    }
+
+    std::string WildcardPattern::normaliseString( std::string const& str ) const {
+        return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str );
+    }
+}
+// end catch_wildcard_pattern.cpp
+// start catch_xmlwriter.cpp
+
+#include <iomanip>
+#include <type_traits>
+
+namespace Catch {
+
+namespace {
+
+    size_t trailingBytes(unsigned char c) {
+        if ((c & 0xE0) == 0xC0) {
+            return 2;
+        }
+        if ((c & 0xF0) == 0xE0) {
+            return 3;
+        }
+        if ((c & 0xF8) == 0xF0) {
+            return 4;
+        }
+        CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
+    }
+
+    uint32_t headerValue(unsigned char c) {
+        if ((c & 0xE0) == 0xC0) {
+            return c & 0x1F;
+        }
+        if ((c & 0xF0) == 0xE0) {
+            return c & 0x0F;
+        }
+        if ((c & 0xF8) == 0xF0) {
+            return c & 0x07;
+        }
+        CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
+    }
+
+    void hexEscapeChar(std::ostream& os, unsigned char c) {
+        std::ios_base::fmtflags f(os.flags());
+        os << "\\x"
+            << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+            << static_cast<int>(c);
+        os.flags(f);
+    }
+
+    bool shouldNewline(XmlFormatting fmt) {
+        return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline));
+    }
+
+    bool shouldIndent(XmlFormatting fmt) {
+        return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent));
+    }
+
+} // anonymous namespace
+
+    XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) {
+        return static_cast<XmlFormatting>(
+            static_cast<std::underlying_type<XmlFormatting>::type>(lhs) |
+            static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
+        );
+    }
+
+    XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) {
+        return static_cast<XmlFormatting>(
+            static_cast<std::underlying_type<XmlFormatting>::type>(lhs) &
+            static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
+        );
+    }
+
+    XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
+    :   m_str( str ),
+        m_forWhat( forWhat )
+    {}
+
+    void XmlEncode::encodeTo( std::ostream& os ) const {
+        // Apostrophe escaping not necessary if we always use " to write attributes
+        // (see: http://www.w3.org/TR/xml/#syntax)
+
+        for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
+            unsigned char c = m_str[idx];
+            switch (c) {
+            case '<':   os << "&lt;"; break;
+            case '&':   os << "&amp;"; break;
+
+            case '>':
+                // See: http://www.w3.org/TR/xml/#syntax
+                if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
+                    os << "&gt;";
+                else
+                    os << c;
+                break;
+
+            case '\"':
+                if (m_forWhat == ForAttributes)
+                    os << "&quot;";
+                else
+                    os << c;
+                break;
+
+            default:
+                // Check for control characters and invalid utf-8
+
+                // Escape control characters in standard ascii
+                // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+                if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+
+                // Plain ASCII: Write it to stream
+                if (c < 0x7F) {
+                    os << c;
+                    break;
+                }
+
+                // UTF-8 territory
+                // Check if the encoding is valid and if it is not, hex escape bytes.
+                // Important: We do not check the exact decoded values for validity, only the encoding format
+                // First check that this bytes is a valid lead byte:
+                // This means that it is not encoded as 1111 1XXX
+                // Or as 10XX XXXX
+                if (c <  0xC0 ||
+                    c >= 0xF8) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+
+                auto encBytes = trailingBytes(c);
+                // Are there enough bytes left to avoid accessing out-of-bounds memory?
+                if (idx + encBytes - 1 >= m_str.size()) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+                // The header is valid, check data
+                // The next encBytes bytes must together be a valid utf-8
+                // This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
+                bool valid = true;
+                uint32_t value = headerValue(c);
+                for (std::size_t n = 1; n < encBytes; ++n) {
+                    unsigned char nc = m_str[idx + n];
+                    valid &= ((nc & 0xC0) == 0x80);
+                    value = (value << 6) | (nc & 0x3F);
+                }
+
+                if (
+                    // Wrong bit pattern of following bytes
+                    (!valid) ||
+                    // Overlong encodings
+                    (value < 0x80) ||
+                    (0x80 <= value && value < 0x800   && encBytes > 2) ||
+                    (0x800 < value && value < 0x10000 && encBytes > 3) ||
+                    // Encoded value out of range
+                    (value >= 0x110000)
+                    ) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+
+                // If we got here, this is in fact a valid(ish) utf-8 sequence
+                for (std::size_t n = 0; n < encBytes; ++n) {
+                    os << m_str[idx + n];
+                }
+                idx += encBytes - 1;
+                break;
+            }
+        }
+    }
+
+    std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+        xmlEncode.encodeTo( os );
+        return os;
+    }
+
+    XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt )
+    :   m_writer( writer ),
+        m_fmt(fmt)
+    {}
+
+    XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
+    :   m_writer( other.m_writer ),
+        m_fmt(other.m_fmt)
+    {
+        other.m_writer = nullptr;
+        other.m_fmt = XmlFormatting::None;
+    }
+    XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
+        if ( m_writer ) {
+            m_writer->endElement();
+        }
+        m_writer = other.m_writer;
+        other.m_writer = nullptr;
+        m_fmt = other.m_fmt;
+        other.m_fmt = XmlFormatting::None;
+        return *this;
+    }
+
+    XmlWriter::ScopedElement::~ScopedElement() {
+        if (m_writer) {
+            m_writer->endElement(m_fmt);
+        }
+    }
+
+    XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) {
+        m_writer->writeText( text, fmt );
+        return *this;
+    }
+
+    XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
+    {
+        writeDeclaration();
+    }
+
+    XmlWriter::~XmlWriter() {
+        while (!m_tags.empty()) {
+            endElement();
+        }
+        newlineIfNecessary();
+    }
+
+    XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) {
+        ensureTagClosed();
+        newlineIfNecessary();
+        if (shouldIndent(fmt)) {
+            m_os << m_indent;
+            m_indent += "  ";
+        }
+        m_os << '<' << name;
+        m_tags.push_back( name );
+        m_tagIsOpen = true;
+        applyFormatting(fmt);
+        return *this;
+    }
+
+    XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) {
+        ScopedElement scoped( this, fmt );
+        startElement( name, fmt );
+        return scoped;
+    }
+
+    XmlWriter& XmlWriter::endElement(XmlFormatting fmt) {
+        m_indent = m_indent.substr(0, m_indent.size() - 2);
+
+        if( m_tagIsOpen ) {
+            m_os << "/>";
+            m_tagIsOpen = false;
+        } else {
+            newlineIfNecessary();
+            if (shouldIndent(fmt)) {
+                m_os << m_indent;
+            }
+            m_os << "</" << m_tags.back() << ">";
+        }
+        m_os << std::flush;
+        applyFormatting(fmt);
+        m_tags.pop_back();
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) {
+        if( !name.empty() && !attribute.empty() )
+            m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) {
+        m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {
+        if( !text.empty() ){
+            bool tagWasOpen = m_tagIsOpen;
+            ensureTagClosed();
+            if (tagWasOpen && shouldIndent(fmt)) {
+                m_os << m_indent;
+            }
+            m_os << XmlEncode( text );
+            applyFormatting(fmt);
+        }
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {
+        ensureTagClosed();
+        if (shouldIndent(fmt)) {
+            m_os << m_indent;
+        }
+        m_os << "<!--" << text << "-->";
+        applyFormatting(fmt);
+        return *this;
+    }
+
+    void XmlWriter::writeStylesheetRef( std::string const& url ) {
+        m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+    }
+
+    XmlWriter& XmlWriter::writeBlankLine() {
+        ensureTagClosed();
+        m_os << '\n';
+        return *this;
+    }
+
+    void XmlWriter::ensureTagClosed() {
+        if( m_tagIsOpen ) {
+            m_os << '>' << std::flush;
+            newlineIfNecessary();
+            m_tagIsOpen = false;
+        }
+    }
+
+    void XmlWriter::applyFormatting(XmlFormatting fmt) {
+        m_needsNewline = shouldNewline(fmt);
+    }
+
+    void XmlWriter::writeDeclaration() {
+        m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+    }
+
+    void XmlWriter::newlineIfNecessary() {
+        if( m_needsNewline ) {
+            m_os << std::endl;
+            m_needsNewline = false;
+        }
+    }
+}
+// end catch_xmlwriter.cpp
+// start catch_reporter_bases.cpp
+
+#include <cstring>
+#include <cfloat>
+#include <cstdio>
+#include <cassert>
+#include <memory>
+
+namespace Catch {
+    void prepareExpandedExpression(AssertionResult& result) {
+        result.getExpandedExpression();
+    }
+
+    // Because formatting using c++ streams is stateful, drop down to C is required
+    // Alternatively we could use stringstream, but its performance is... not good.
+    std::string getFormattedDuration( double duration ) {
+        // Max exponent + 1 is required to represent the whole part
+        // + 1 for decimal point
+        // + 3 for the 3 decimal places
+        // + 1 for null terminator
+        const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+        char buffer[maxDoubleSize];
+
+        // Save previous errno, to prevent sprintf from overwriting it
+        ErrnoGuard guard;
+#ifdef _MSC_VER
+        sprintf_s(buffer, "%.3f", duration);
+#else
+        std::sprintf(buffer, "%.3f", duration);
+#endif
+        return std::string(buffer);
+    }
+
+    bool shouldShowDuration( IConfig const& config, double duration ) {
+        if ( config.showDurations() == ShowDurations::Always ) {
+            return true;
+        }
+        if ( config.showDurations() == ShowDurations::Never ) {
+            return false;
+        }
+        const double min = config.minDuration();
+        return min >= 0 && duration >= min;
+    }
+
+    std::string serializeFilters( std::vector<std::string> const& container ) {
+        ReusableStringStream oss;
+        bool first = true;
+        for (auto&& filter : container)
+        {
+            if (!first)
+                oss << ' ';
+            else
+                first = false;
+
+            oss << filter;
+        }
+        return oss.str();
+    }
+
+    TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config)
+        :StreamingReporterBase(_config) {}
+
+    std::set<Verbosity> TestEventListenerBase::getSupportedVerbosities() {
+        return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High };
+    }
+
+    void TestEventListenerBase::assertionStarting(AssertionInfo const &) {}
+
+    bool TestEventListenerBase::assertionEnded(AssertionStats const &) {
+        return false;
+    }
+
+} // end namespace Catch
+// end catch_reporter_bases.cpp
+// start catch_reporter_compact.cpp
+
+namespace {
+
+#ifdef CATCH_PLATFORM_MAC
+    const char* failedString() { return "FAILED"; }
+    const char* passedString() { return "PASSED"; }
+#else
+    const char* failedString() { return "failed"; }
+    const char* passedString() { return "passed"; }
+#endif
+
+    // Colour::LightGrey
+    Catch::Colour::Code dimColour() { return Catch::Colour::FileName; }
+
+    std::string bothOrAll( std::size_t count ) {
+        return count == 1 ? std::string() :
+               count == 2 ? "both " : "all " ;
+    }
+
+} // anon namespace
+
+namespace Catch {
+namespace {
+// Colour, message variants:
+// - white: No tests ran.
+// -   red: Failed [both/all] N test cases, failed [both/all] M assertions.
+// - white: Passed [both/all] N test cases (no assertions).
+// -   red: Failed N tests cases, failed M assertions.
+// - green: Passed [both/all] N tests cases with M assertions.
+void printTotals(std::ostream& out, const Totals& totals) {
+    if (totals.testCases.total() == 0) {
+        out << "No tests ran.";
+    } else if (totals.testCases.failed == totals.testCases.total()) {
+        Colour colour(Colour::ResultError);
+        const std::string qualify_assertions_failed =
+            totals.assertions.failed == totals.assertions.total() ?
+            bothOrAll(totals.assertions.failed) : std::string();
+        out <<
+            "Failed " << bothOrAll(totals.testCases.failed)
+            << pluralise(totals.testCases.failed, "test case") << ", "
+            "failed " << qualify_assertions_failed <<
+            pluralise(totals.assertions.failed, "assertion") << '.';
+    } else if (totals.assertions.total() == 0) {
+        out <<
+            "Passed " << bothOrAll(totals.testCases.total())
+            << pluralise(totals.testCases.total(), "test case")
+            << " (no assertions).";
+    } else if (totals.assertions.failed) {
+        Colour colour(Colour::ResultError);
+        out <<
+            "Failed " << pluralise(totals.testCases.failed, "test case") << ", "
+            "failed " << pluralise(totals.assertions.failed, "assertion") << '.';
+    } else {
+        Colour colour(Colour::ResultSuccess);
+        out <<
+            "Passed " << bothOrAll(totals.testCases.passed)
+            << pluralise(totals.testCases.passed, "test case") <<
+            " with " << pluralise(totals.assertions.passed, "assertion") << '.';
+    }
+}
+
+// Implementation of CompactReporter formatting
+class AssertionPrinter {
+public:
+    AssertionPrinter& operator= (AssertionPrinter const&) = delete;
+    AssertionPrinter(AssertionPrinter const&) = delete;
+    AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
+        : stream(_stream)
+        , result(_stats.assertionResult)
+        , messages(_stats.infoMessages)
+        , itMessage(_stats.infoMessages.begin())
+        , printInfoMessages(_printInfoMessages) {}
+
+    void print() {
+        printSourceInfo();
+
+        itMessage = messages.begin();
+
+        switch (result.getResultType()) {
+        case ResultWas::Ok:
+            printResultType(Colour::ResultSuccess, passedString());
+            printOriginalExpression();
+            printReconstructedExpression();
+            if (!result.hasExpression())
+                printRemainingMessages(Colour::None);
+            else
+                printRemainingMessages();
+            break;
+        case ResultWas::ExpressionFailed:
+            if (result.isOk())
+                printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok"));
+            else
+                printResultType(Colour::Error, failedString());
+            printOriginalExpression();
+            printReconstructedExpression();
+            printRemainingMessages();
+            break;
+        case ResultWas::ThrewException:
+            printResultType(Colour::Error, failedString());
+            printIssue("unexpected exception with message:");
+            printMessage();
+            printExpressionWas();
+            printRemainingMessages();
+            break;
+        case ResultWas::FatalErrorCondition:
+            printResultType(Colour::Error, failedString());
+            printIssue("fatal error condition with message:");
+            printMessage();
+            printExpressionWas();
+            printRemainingMessages();
+            break;
+        case ResultWas::DidntThrowException:
+            printResultType(Colour::Error, failedString());
+            printIssue("expected exception, got none");
+            printExpressionWas();
+            printRemainingMessages();
+            break;
+        case ResultWas::Info:
+            printResultType(Colour::None, "info");
+            printMessage();
+            printRemainingMessages();
+            break;
+        case ResultWas::Warning:
+            printResultType(Colour::None, "warning");
+            printMessage();
+            printRemainingMessages();
+            break;
+        case ResultWas::ExplicitFailure:
+            printResultType(Colour::Error, failedString());
+            printIssue("explicitly");
+            printRemainingMessages(Colour::None);
+            break;
+            // These cases are here to prevent compiler warnings
+        case ResultWas::Unknown:
+        case ResultWas::FailureBit:
+        case ResultWas::Exception:
+            printResultType(Colour::Error, "** internal error **");
+            break;
+        }
+    }
+
+private:
+    void printSourceInfo() const {
+        Colour colourGuard(Colour::FileName);
+        stream << result.getSourceInfo() << ':';
+    }
+
+    void printResultType(Colour::Code colour, std::string const& passOrFail) const {
+        if (!passOrFail.empty()) {
+            {
+                Colour colourGuard(colour);
+                stream << ' ' << passOrFail;
+            }
+            stream << ':';
+        }
+    }
+
+    void printIssue(std::string const& issue) const {
+        stream << ' ' << issue;
+    }
+
+    void printExpressionWas() {
+        if (result.hasExpression()) {
+            stream << ';';
+            {
+                Colour colour(dimColour());
+                stream << " expression was:";
+            }
+            printOriginalExpression();
+        }
+    }
+
+    void printOriginalExpression() const {
+        if (result.hasExpression()) {
+            stream << ' ' << result.getExpression();
+        }
+    }
+
+    void printReconstructedExpression() const {
+        if (result.hasExpandedExpression()) {
+            {
+                Colour colour(dimColour());
+                stream << " for: ";
+            }
+            stream << result.getExpandedExpression();
+        }
+    }
+
+    void printMessage() {
+        if (itMessage != messages.end()) {
+            stream << " '" << itMessage->message << '\'';
+            ++itMessage;
+        }
+    }
+
+    void printRemainingMessages(Colour::Code colour = dimColour()) {
+        if (itMessage == messages.end())
+            return;
+
+        const auto itEnd = messages.cend();
+        const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
+
+        {
+            Colour colourGuard(colour);
+            stream << " with " << pluralise(N, "message") << ':';
+        }
+
+        while (itMessage != itEnd) {
+            // If this assertion is a warning ignore any INFO messages
+            if (printInfoMessages || itMessage->type != ResultWas::Info) {
+                printMessage();
+                if (itMessage != itEnd) {
+                    Colour colourGuard(dimColour());
+                    stream << " and";
+                }
+                continue;
+            }
+            ++itMessage;
+        }
+    }
+
+private:
+    std::ostream& stream;
+    AssertionResult const& result;
+    std::vector<MessageInfo> messages;
+    std::vector<MessageInfo>::const_iterator itMessage;
+    bool printInfoMessages;
+};
+
+} // anon namespace
+
+        std::string CompactReporter::getDescription() {
+            return "Reports test results on a single line, suitable for IDEs";
+        }
+
+        void CompactReporter::noMatchingTestCases( std::string const& spec ) {
+            stream << "No test cases matched '" << spec << '\'' << std::endl;
+        }
+
+        void CompactReporter::assertionStarting( AssertionInfo const& ) {}
+
+        bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
+            AssertionResult const& result = _assertionStats.assertionResult;
+
+            bool printInfoMessages = true;
+
+            // Drop out if result was successful and we're not printing those
+            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+                if( result.getResultType() != ResultWas::Warning )
+                    return false;
+                printInfoMessages = false;
+            }
+
+            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+            printer.print();
+
+            stream << std::endl;
+            return true;
+        }
+
+        void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
+            double dur = _sectionStats.durationInSeconds;
+            if ( shouldShowDuration( *m_config, dur ) ) {
+                stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+            }
+        }
+
+        void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) {
+            printTotals( stream, _testRunStats.totals );
+            stream << '\n' << std::endl;
+            StreamingReporterBase::testRunEnded( _testRunStats );
+        }
+
+        CompactReporter::~CompactReporter() {}
+
+    CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+// end catch_reporter_compact.cpp
+// start catch_reporter_console.cpp
+
+#include <cfloat>
+#include <cstdio>
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+ // Note that 4062 (not all labels are handled and default is missing) is enabled
+#endif
+
+#if defined(__clang__)
+#  pragma clang diagnostic push
+// For simplicity, benchmarking-only helpers are always enabled
+#  pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+namespace Catch {
+
+namespace {
+
+// Formatter impl for ConsoleReporter
+class ConsoleAssertionPrinter {
+public:
+    ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete;
+    ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete;
+    ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
+        : stream(_stream),
+        stats(_stats),
+        result(_stats.assertionResult),
+        colour(Colour::None),
+        message(result.getMessage()),
+        messages(_stats.infoMessages),
+        printInfoMessages(_printInfoMessages) {
+        switch (result.getResultType()) {
+        case ResultWas::Ok:
+            colour = Colour::Success;
+            passOrFail = "PASSED";
+            //if( result.hasMessage() )
+            if (_stats.infoMessages.size() == 1)
+                messageLabel = "with message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel = "with messages";
+            break;
+        case ResultWas::ExpressionFailed:
+            if (result.isOk()) {
+                colour = Colour::Success;
+                passOrFail = "FAILED - but was ok";
+            } else {
+                colour = Colour::Error;
+                passOrFail = "FAILED";
+            }
+            if (_stats.infoMessages.size() == 1)
+                messageLabel = "with message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel = "with messages";
+            break;
+        case ResultWas::ThrewException:
+            colour = Colour::Error;
+            passOrFail = "FAILED";
+            messageLabel = "due to unexpected exception with ";
+            if (_stats.infoMessages.size() == 1)
+                messageLabel += "message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel += "messages";
+            break;
+        case ResultWas::FatalErrorCondition:
+            colour = Colour::Error;
+            passOrFail = "FAILED";
+            messageLabel = "due to a fatal error condition";
+            break;
+        case ResultWas::DidntThrowException:
+            colour = Colour::Error;
+            passOrFail = "FAILED";
+            messageLabel = "because no exception was thrown where one was expected";
+            break;
+        case ResultWas::Info:
+            messageLabel = "info";
+            break;
+        case ResultWas::Warning:
+            messageLabel = "warning";
+            break;
+        case ResultWas::ExplicitFailure:
+            passOrFail = "FAILED";
+            colour = Colour::Error;
+            if (_stats.infoMessages.size() == 1)
+                messageLabel = "explicitly with message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel = "explicitly with messages";
+            break;
+            // These cases are here to prevent compiler warnings
+        case ResultWas::Unknown:
+        case ResultWas::FailureBit:
+        case ResultWas::Exception:
+            passOrFail = "** internal error **";
+            colour = Colour::Error;
+            break;
+        }
+    }
+
+    void print() const {
+        printSourceInfo();
+        if (stats.totals.assertions.total() > 0) {
+            printResultType();
+            printOriginalExpression();
+            printReconstructedExpression();
+        } else {
+            stream << '\n';
+        }
+        printMessage();
+    }
+
+private:
+    void printResultType() const {
+        if (!passOrFail.empty()) {
+            Colour colourGuard(colour);
+            stream << passOrFail << ":\n";
+        }
+    }
+    void printOriginalExpression() const {
+        if (result.hasExpression()) {
+            Colour colourGuard(Colour::OriginalExpression);
+            stream << "  ";
+            stream << result.getExpressionInMacro();
+            stream << '\n';
+        }
+    }
+    void printReconstructedExpression() const {
+        if (result.hasExpandedExpression()) {
+            stream << "with expansion:\n";
+            Colour colourGuard(Colour::ReconstructedExpression);
+            stream << Column(result.getExpandedExpression()).indent(2) << '\n';
+        }
+    }
+    void printMessage() const {
+        if (!messageLabel.empty())
+            stream << messageLabel << ':' << '\n';
+        for (auto const& msg : messages) {
+            // If this assertion is a warning ignore any INFO messages
+            if (printInfoMessages || msg.type != ResultWas::Info)
+                stream << Column(msg.message).indent(2) << '\n';
+        }
+    }
+    void printSourceInfo() const {
+        Colour colourGuard(Colour::FileName);
+        stream << result.getSourceInfo() << ": ";
+    }
+
+    std::ostream& stream;
+    AssertionStats const& stats;
+    AssertionResult const& result;
+    Colour::Code colour;
+    std::string passOrFail;
+    std::string messageLabel;
+    std::string message;
+    std::vector<MessageInfo> messages;
+    bool printInfoMessages;
+};
+
+std::size_t makeRatio(std::size_t number, std::size_t total) {
+    std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0;
+    return (ratio == 0 && number > 0) ? 1 : ratio;
+}
+
+std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) {
+    if (i > j && i > k)
+        return i;
+    else if (j > k)
+        return j;
+    else
+        return k;
+}
+
+struct ColumnInfo {
+    enum Justification { Left, Right };
+    std::string name;
+    int width;
+    Justification justification;
+};
+struct ColumnBreak {};
+struct RowBreak {};
+
+class Duration {
+    enum class Unit {
+        Auto,
+        Nanoseconds,
+        Microseconds,
+        Milliseconds,
+        Seconds,
+        Minutes
+    };
+    static const uint64_t s_nanosecondsInAMicrosecond = 1000;
+    static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond;
+    static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
+    static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
+
+    double m_inNanoseconds;
+    Unit m_units;
+
+public:
+    explicit Duration(double inNanoseconds, Unit units = Unit::Auto)
+        : m_inNanoseconds(inNanoseconds),
+        m_units(units) {
+        if (m_units == Unit::Auto) {
+            if (m_inNanoseconds < s_nanosecondsInAMicrosecond)
+                m_units = Unit::Nanoseconds;
+            else if (m_inNanoseconds < s_nanosecondsInAMillisecond)
+                m_units = Unit::Microseconds;
+            else if (m_inNanoseconds < s_nanosecondsInASecond)
+                m_units = Unit::Milliseconds;
+            else if (m_inNanoseconds < s_nanosecondsInAMinute)
+                m_units = Unit::Seconds;
+            else
+                m_units = Unit::Minutes;
+        }
+
+    }
+
+    auto value() const -> double {
+        switch (m_units) {
+        case Unit::Microseconds:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond);
+        case Unit::Milliseconds:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond);
+        case Unit::Seconds:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond);
+        case Unit::Minutes:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute);
+        default:
+            return m_inNanoseconds;
+        }
+    }
+    auto unitsAsString() const -> std::string {
+        switch (m_units) {
+        case Unit::Nanoseconds:
+            return "ns";
+        case Unit::Microseconds:
+            return "us";
+        case Unit::Milliseconds:
+            return "ms";
+        case Unit::Seconds:
+            return "s";
+        case Unit::Minutes:
+            return "m";
+        default:
+            return "** internal error **";
+        }
+
+    }
+    friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
+        return os << duration.value() << ' ' << duration.unitsAsString();
+    }
+};
+} // end anon namespace
+
+class TablePrinter {
+    std::ostream& m_os;
+    std::vector<ColumnInfo> m_columnInfos;
+    std::ostringstream m_oss;
+    int m_currentColumn = -1;
+    bool m_isOpen = false;
+
+public:
+    TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
+    :   m_os( os ),
+        m_columnInfos( std::move( columnInfos ) ) {}
+
+    auto columnInfos() const -> std::vector<ColumnInfo> const& {
+        return m_columnInfos;
+    }
+
+    void open() {
+        if (!m_isOpen) {
+            m_isOpen = true;
+            *this << RowBreak();
+
+			Columns headerCols;
+			Spacer spacer(2);
+			for (auto const& info : m_columnInfos) {
+				headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
+				headerCols += spacer;
+			}
+			m_os << headerCols << '\n';
+
+            m_os << Catch::getLineOfChars<'-'>() << '\n';
+        }
+    }
+    void close() {
+        if (m_isOpen) {
+            *this << RowBreak();
+            m_os << std::endl;
+            m_isOpen = false;
+        }
+    }
+
+    template<typename T>
+    friend TablePrinter& operator << (TablePrinter& tp, T const& value) {
+        tp.m_oss << value;
+        return tp;
+    }
+
+    friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
+        auto colStr = tp.m_oss.str();
+        const auto strSize = colStr.size();
+        tp.m_oss.str("");
+        tp.open();
+        if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) {
+            tp.m_currentColumn = -1;
+            tp.m_os << '\n';
+        }
+        tp.m_currentColumn++;
+
+        auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
+        auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width))
+            ? std::string(colInfo.width - (strSize + 1), ' ')
+            : std::string();
+        if (colInfo.justification == ColumnInfo::Left)
+            tp.m_os << colStr << padding << ' ';
+        else
+            tp.m_os << padding << colStr << ' ';
+        return tp;
+    }
+
+    friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
+        if (tp.m_currentColumn > 0) {
+            tp.m_os << '\n';
+            tp.m_currentColumn = -1;
+        }
+        return tp;
+    }
+};
+
+ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
+    : StreamingReporterBase(config),
+    m_tablePrinter(new TablePrinter(config.stream(),
+        [&config]() -> std::vector<ColumnInfo> {
+        if (config.fullConfig()->benchmarkNoAnalysis())
+        {
+            return{
+                { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
+                { "     samples", 14, ColumnInfo::Right },
+                { "  iterations", 14, ColumnInfo::Right },
+                { "        mean", 14, ColumnInfo::Right }
+            };
+        }
+        else
+        {
+            return{
+                { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
+                { "samples      mean       std dev", 14, ColumnInfo::Right },
+                { "iterations   low mean   low std dev", 14, ColumnInfo::Right },
+                { "estimated    high mean  high std dev", 14, ColumnInfo::Right }
+            };
+        }
+    }())) {}
+ConsoleReporter::~ConsoleReporter() = default;
+
+std::string ConsoleReporter::getDescription() {
+    return "Reports test results as plain lines of text";
+}
+
+void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
+    stream << "No test cases matched '" << spec << '\'' << std::endl;
+}
+
+void ConsoleReporter::reportInvalidArguments(std::string const&arg){
+    stream << "Invalid Filter: " << arg << std::endl;
+}
+
+void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
+
+bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
+    AssertionResult const& result = _assertionStats.assertionResult;
+
+    bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+    // Drop out if result was successful but we're not printing them.
+    if (!includeResults && result.getResultType() != ResultWas::Warning)
+        return false;
+
+    lazyPrint();
+
+    ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults);
+    printer.print();
+    stream << std::endl;
+    return true;
+}
+
+void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
+    m_tablePrinter->close();
+    m_headerPrinted = false;
+    StreamingReporterBase::sectionStarting(_sectionInfo);
+}
+void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
+    m_tablePrinter->close();
+    if (_sectionStats.missingAssertions) {
+        lazyPrint();
+        Colour colour(Colour::ResultError);
+        if (m_sectionStack.size() > 1)
+            stream << "\nNo assertions in section";
+        else
+            stream << "\nNo assertions in test case";
+        stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+    }
+    double dur = _sectionStats.durationInSeconds;
+    if (shouldShowDuration(*m_config, dur)) {
+        stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+    }
+    if (m_headerPrinted) {
+        m_headerPrinted = false;
+    }
+    StreamingReporterBase::sectionEnded(_sectionStats);
+}
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+void ConsoleReporter::benchmarkPreparing(std::string const& name) {
+	lazyPrintWithoutClosingBenchmarkTable();
+
+	auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
+
+	bool firstLine = true;
+	for (auto line : nameCol) {
+		if (!firstLine)
+			(*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
+		else
+			firstLine = false;
+
+		(*m_tablePrinter) << line << ColumnBreak();
+	}
+}
+
+void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
+    (*m_tablePrinter) << info.samples << ColumnBreak()
+        << info.iterations << ColumnBreak();
+    if (!m_config->benchmarkNoAnalysis())
+        (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak();
+}
+void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
+    if (m_config->benchmarkNoAnalysis())
+    {
+        (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak();
+    }
+    else
+    {
+        (*m_tablePrinter) << ColumnBreak()
+            << Duration(stats.mean.point.count()) << ColumnBreak()
+            << Duration(stats.mean.lower_bound.count()) << ColumnBreak()
+            << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak()
+            << Duration(stats.standardDeviation.point.count()) << ColumnBreak()
+            << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
+            << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
+    }
+}
+
+void ConsoleReporter::benchmarkFailed(std::string const& error) {
+	Colour colour(Colour::Red);
+    (*m_tablePrinter)
+        << "Benchmark failed (" << error << ')'
+        << ColumnBreak() << RowBreak();
+}
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
+    m_tablePrinter->close();
+    StreamingReporterBase::testCaseEnded(_testCaseStats);
+    m_headerPrinted = false;
+}
+void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) {
+    if (currentGroupInfo.used) {
+        printSummaryDivider();
+        stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+        printTotals(_testGroupStats.totals);
+        stream << '\n' << std::endl;
+    }
+    StreamingReporterBase::testGroupEnded(_testGroupStats);
+}
+void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
+    printTotalsDivider(_testRunStats.totals);
+    printTotals(_testRunStats.totals);
+    stream << std::endl;
+    StreamingReporterBase::testRunEnded(_testRunStats);
+}
+void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
+    StreamingReporterBase::testRunStarting(_testInfo);
+    printTestFilters();
+}
+
+void ConsoleReporter::lazyPrint() {
+
+    m_tablePrinter->close();
+    lazyPrintWithoutClosingBenchmarkTable();
+}
+
+void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() {
+
+    if (!currentTestRunInfo.used)
+        lazyPrintRunInfo();
+    if (!currentGroupInfo.used)
+        lazyPrintGroupInfo();
+
+    if (!m_headerPrinted) {
+        printTestCaseAndSectionHeader();
+        m_headerPrinted = true;
+    }
+}
+void ConsoleReporter::lazyPrintRunInfo() {
+    stream << '\n' << getLineOfChars<'~'>() << '\n';
+    Colour colour(Colour::SecondaryText);
+    stream << currentTestRunInfo->name
+        << " is a Catch v" << libraryVersion() << " host application.\n"
+        << "Run with -? for options\n\n";
+
+    if (m_config->rngSeed() != 0)
+        stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
+
+    currentTestRunInfo.used = true;
+}
+void ConsoleReporter::lazyPrintGroupInfo() {
+    if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) {
+        printClosedHeader("Group: " + currentGroupInfo->name);
+        currentGroupInfo.used = true;
+    }
+}
+void ConsoleReporter::printTestCaseAndSectionHeader() {
+    assert(!m_sectionStack.empty());
+    printOpenHeader(currentTestCaseInfo->name);
+
+    if (m_sectionStack.size() > 1) {
+        Colour colourGuard(Colour::Headers);
+
+        auto
+            it = m_sectionStack.begin() + 1, // Skip first section (test case)
+            itEnd = m_sectionStack.end();
+        for (; it != itEnd; ++it)
+            printHeaderString(it->name, 2);
+    }
+
+    SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
+
+    stream << getLineOfChars<'-'>() << '\n';
+    Colour colourGuard(Colour::FileName);
+    stream << lineInfo << '\n';
+    stream << getLineOfChars<'.'>() << '\n' << std::endl;
+}
+
+void ConsoleReporter::printClosedHeader(std::string const& _name) {
+    printOpenHeader(_name);
+    stream << getLineOfChars<'.'>() << '\n';
+}
+void ConsoleReporter::printOpenHeader(std::string const& _name) {
+    stream << getLineOfChars<'-'>() << '\n';
+    {
+        Colour colourGuard(Colour::Headers);
+        printHeaderString(_name);
+    }
+}
+
+// if string has a : in first line will set indent to follow it on
+// subsequent lines
+void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) {
+    std::size_t i = _string.find(": ");
+    if (i != std::string::npos)
+        i += 2;
+    else
+        i = 0;
+    stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n';
+}
+
+struct SummaryColumn {
+
+    SummaryColumn( std::string _label, Colour::Code _colour )
+    :   label( std::move( _label ) ),
+        colour( _colour ) {}
+    SummaryColumn addRow( std::size_t count ) {
+        ReusableStringStream rss;
+        rss << count;
+        std::string row = rss.str();
+        for (auto& oldRow : rows) {
+            while (oldRow.size() < row.size())
+                oldRow = ' ' + oldRow;
+            while (oldRow.size() > row.size())
+                row = ' ' + row;
+        }
+        rows.push_back(row);
+        return *this;
+    }
+
+    std::string label;
+    Colour::Code colour;
+    std::vector<std::string> rows;
+
+};
+
+void ConsoleReporter::printTotals( Totals const& totals ) {
+    if (totals.testCases.total() == 0) {
+        stream << Colour(Colour::Warning) << "No tests ran\n";
+    } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {
+        stream << Colour(Colour::ResultSuccess) << "All tests passed";
+        stream << " ("
+            << pluralise(totals.assertions.passed, "assertion") << " in "
+            << pluralise(totals.testCases.passed, "test case") << ')'
+            << '\n';
+    } else {
+
+        std::vector<SummaryColumn> columns;
+        columns.push_back(SummaryColumn("", Colour::None)
+                          .addRow(totals.testCases.total())
+                          .addRow(totals.assertions.total()));
+        columns.push_back(SummaryColumn("passed", Colour::Success)
+                          .addRow(totals.testCases.passed)
+                          .addRow(totals.assertions.passed));
+        columns.push_back(SummaryColumn("failed", Colour::ResultError)
+                          .addRow(totals.testCases.failed)
+                          .addRow(totals.assertions.failed));
+        columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure)
+                          .addRow(totals.testCases.failedButOk)
+                          .addRow(totals.assertions.failedButOk));
+
+        printSummaryRow("test cases", columns, 0);
+        printSummaryRow("assertions", columns, 1);
+    }
+}
+void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) {
+    for (auto col : cols) {
+        std::string value = col.rows[row];
+        if (col.label.empty()) {
+            stream << label << ": ";
+            if (value != "0")
+                stream << value;
+            else
+                stream << Colour(Colour::Warning) << "- none -";
+        } else if (value != "0") {
+            stream << Colour(Colour::LightGrey) << " | ";
+            stream << Colour(col.colour)
+                << value << ' ' << col.label;
+        }
+    }
+    stream << '\n';
+}
+
+void ConsoleReporter::printTotalsDivider(Totals const& totals) {
+    if (totals.testCases.total() > 0) {
+        std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total());
+        std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total());
+        std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total());
+        while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1)
+            findMax(failedRatio, failedButOkRatio, passedRatio)++;
+        while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1)
+            findMax(failedRatio, failedButOkRatio, passedRatio)--;
+
+        stream << Colour(Colour::Error) << std::string(failedRatio, '=');
+        stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '=');
+        if (totals.testCases.allPassed())
+            stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '=');
+        else
+            stream << Colour(Colour::Success) << std::string(passedRatio, '=');
+    } else {
+        stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '=');
+    }
+    stream << '\n';
+}
+void ConsoleReporter::printSummaryDivider() {
+    stream << getLineOfChars<'-'>() << '\n';
+}
+
+void ConsoleReporter::printTestFilters() {
+    if (m_config->testSpec().hasFilters()) {
+        Colour guard(Colour::BrightYellow);
+        stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n';
+    }
+}
+
+CATCH_REGISTER_REPORTER("console", ConsoleReporter)
+
+} // end namespace Catch
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(__clang__)
+#  pragma clang diagnostic pop
+#endif
+// end catch_reporter_console.cpp
+// start catch_reporter_junit.cpp
+
+#include <cassert>
+#include <sstream>
+#include <ctime>
+#include <algorithm>
+
+namespace Catch {
+
+    namespace {
+        std::string getCurrentTimestamp() {
+            // Beware, this is not reentrant because of backward compatibility issues
+            // Also, UTC only, again because of backward compatibility (%z is C++11)
+            time_t rawtime;
+            std::time(&rawtime);
+            auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
+
+#ifdef _MSC_VER
+            std::tm timeInfo = {};
+            gmtime_s(&timeInfo, &rawtime);
+#else
+            std::tm* timeInfo;
+            timeInfo = std::gmtime(&rawtime);
+#endif
+
+            char timeStamp[timeStampSize];
+            const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+            std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+            std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+            return std::string(timeStamp);
+        }
+
+        std::string fileNameTag(const std::vector<std::string> &tags) {
+            auto it = std::find_if(begin(tags),
+                                   end(tags),
+                                   [] (std::string const& tag) {return tag.front() == '#'; });
+            if (it != tags.end())
+                return it->substr(1);
+            return std::string();
+        }
+    } // anonymous namespace
+
+    JunitReporter::JunitReporter( ReporterConfig const& _config )
+        :   CumulativeReporterBase( _config ),
+            xml( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+            m_reporterPrefs.shouldReportAllAssertions = true;
+        }
+
+    JunitReporter::~JunitReporter() {}
+
+    std::string JunitReporter::getDescription() {
+        return "Reports test results in an XML format that looks like Ant's junitreport target";
+    }
+
+    void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {}
+
+    void JunitReporter::testRunStarting( TestRunInfo const& runInfo )  {
+        CumulativeReporterBase::testRunStarting( runInfo );
+        xml.startElement( "testsuites" );
+    }
+
+    void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) {
+        suiteTimer.start();
+        stdOutForSuite.clear();
+        stdErrForSuite.clear();
+        unexpectedExceptions = 0;
+        CumulativeReporterBase::testGroupStarting( groupInfo );
+    }
+
+    void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) {
+        m_okToFail = testCaseInfo.okToFail();
+    }
+
+    bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) {
+        if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
+            unexpectedExceptions++;
+        return CumulativeReporterBase::assertionEnded( assertionStats );
+    }
+
+    void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        stdOutForSuite += testCaseStats.stdOut;
+        stdErrForSuite += testCaseStats.stdErr;
+        CumulativeReporterBase::testCaseEnded( testCaseStats );
+    }
+
+    void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+        double suiteTime = suiteTimer.getElapsedSeconds();
+        CumulativeReporterBase::testGroupEnded( testGroupStats );
+        writeGroup( *m_testGroups.back(), suiteTime );
+    }
+
+    void JunitReporter::testRunEndedCumulative() {
+        xml.endElement();
+    }
+
+    void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+        XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+
+        TestGroupStats const& stats = groupNode.value;
+        xml.writeAttribute( "name", stats.groupInfo.name );
+        xml.writeAttribute( "errors", unexpectedExceptions );
+        xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+        xml.writeAttribute( "tests", stats.totals.assertions.total() );
+        xml.writeAttribute( "hostname", "tbd" ); // !TBD
+        if( m_config->showDurations() == ShowDurations::Never )
+            xml.writeAttribute( "time", "" );
+        else
+            xml.writeAttribute( "time", suiteTime );
+        xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+
+        // Write properties if there are any
+        if (m_config->hasTestFilters() || m_config->rngSeed() != 0) {
+            auto properties = xml.scopedElement("properties");
+            if (m_config->hasTestFilters()) {
+                xml.scopedElement("property")
+                    .writeAttribute("name", "filters")
+                    .writeAttribute("value", serializeFilters(m_config->getTestsOrTags()));
+            }
+            if (m_config->rngSeed() != 0) {
+                xml.scopedElement("property")
+                    .writeAttribute("name", "random-seed")
+                    .writeAttribute("value", m_config->rngSeed());
+            }
+        }
+
+        // Write test cases
+        for( auto const& child : groupNode.children )
+            writeTestCase( *child );
+
+        xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
+        xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline );
+    }
+
+    void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
+        TestCaseStats const& stats = testCaseNode.value;
+
+        // All test cases have exactly one section - which represents the
+        // test case itself. That section may have 0-n nested sections
+        assert( testCaseNode.children.size() == 1 );
+        SectionNode const& rootSection = *testCaseNode.children.front();
+
+        std::string className = stats.testInfo.className;
+
+        if( className.empty() ) {
+            className = fileNameTag(stats.testInfo.tags);
+            if ( className.empty() )
+                className = "global";
+        }
+
+        if ( !m_config->name().empty() )
+            className = m_config->name() + "." + className;
+
+        writeSection( className, "", rootSection );
+    }
+
+    void JunitReporter::writeSection(  std::string const& className,
+                        std::string const& rootName,
+                        SectionNode const& sectionNode ) {
+        std::string name = trim( sectionNode.stats.sectionInfo.name );
+        if( !rootName.empty() )
+            name = rootName + '/' + name;
+
+        if( !sectionNode.assertions.empty() ||
+            !sectionNode.stdOut.empty() ||
+            !sectionNode.stdErr.empty() ) {
+            XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+            if( className.empty() ) {
+                xml.writeAttribute( "classname", name );
+                xml.writeAttribute( "name", "root" );
+            }
+            else {
+                xml.writeAttribute( "classname", className );
+                xml.writeAttribute( "name", name );
+            }
+            xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
+            // This is not ideal, but it should be enough to mimic gtest's
+            // junit output.
+            // Ideally the JUnit reporter would also handle `skipTest`
+            // events and write those out appropriately.
+            xml.writeAttribute( "status", "run" );
+
+            writeAssertions( sectionNode );
+
+            if( !sectionNode.stdOut.empty() )
+                xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline );
+            if( !sectionNode.stdErr.empty() )
+                xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline );
+        }
+        for( auto const& childNode : sectionNode.childSections )
+            if( className.empty() )
+                writeSection( name, "", *childNode );
+            else
+                writeSection( className, name, *childNode );
+    }
+
+    void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
+        for( auto const& assertion : sectionNode.assertions )
+            writeAssertion( assertion );
+    }
+
+    void JunitReporter::writeAssertion( AssertionStats const& stats ) {
+        AssertionResult const& result = stats.assertionResult;
+        if( !result.isOk() ) {
+            std::string elementName;
+            switch( result.getResultType() ) {
+                case ResultWas::ThrewException:
+                case ResultWas::FatalErrorCondition:
+                    elementName = "error";
+                    break;
+                case ResultWas::ExplicitFailure:
+                case ResultWas::ExpressionFailed:
+                case ResultWas::DidntThrowException:
+                    elementName = "failure";
+                    break;
+
+                // We should never see these here:
+                case ResultWas::Info:
+                case ResultWas::Warning:
+                case ResultWas::Ok:
+                case ResultWas::Unknown:
+                case ResultWas::FailureBit:
+                case ResultWas::Exception:
+                    elementName = "internalError";
+                    break;
+            }
+
+            XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+            xml.writeAttribute( "message", result.getExpression() );
+            xml.writeAttribute( "type", result.getTestMacroName() );
+
+            ReusableStringStream rss;
+            if (stats.totals.assertions.total() > 0) {
+                rss << "FAILED" << ":\n";
+                if (result.hasExpression()) {
+                    rss << "  ";
+                    rss << result.getExpressionInMacro();
+                    rss << '\n';
+                }
+                if (result.hasExpandedExpression()) {
+                    rss << "with expansion:\n";
+                    rss << Column(result.getExpandedExpression()).indent(2) << '\n';
+                }
+            } else {
+                rss << '\n';
+            }
+
+            if( !result.getMessage().empty() )
+                rss << result.getMessage() << '\n';
+            for( auto const& msg : stats.infoMessages )
+                if( msg.type == ResultWas::Info )
+                    rss << msg.message << '\n';
+
+            rss << "at " << result.getSourceInfo();
+            xml.writeText( rss.str(), XmlFormatting::Newline );
+        }
+    }
+
+    CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+// end catch_reporter_junit.cpp
+// start catch_reporter_listening.cpp
+
+#include <cassert>
+
+namespace Catch {
+
+    ListeningReporter::ListeningReporter() {
+        // We will assume that listeners will always want all assertions
+        m_preferences.shouldReportAllAssertions = true;
+    }
+
+    void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) {
+        m_listeners.push_back( std::move( listener ) );
+    }
+
+    void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) {
+        assert(!m_reporter && "Listening reporter can wrap only 1 real reporter");
+        m_reporter = std::move( reporter );
+        m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut;
+    }
+
+    ReporterPreferences ListeningReporter::getPreferences() const {
+        return m_preferences;
+    }
+
+    std::set<Verbosity> ListeningReporter::getSupportedVerbosities() {
+        return std::set<Verbosity>{ };
+    }
+
+    void ListeningReporter::noMatchingTestCases( std::string const& spec ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->noMatchingTestCases( spec );
+        }
+        m_reporter->noMatchingTestCases( spec );
+    }
+
+    void ListeningReporter::reportInvalidArguments(std::string const&arg){
+        for ( auto const& listener : m_listeners ) {
+            listener->reportInvalidArguments( arg );
+        }
+        m_reporter->reportInvalidArguments( arg );
+    }
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+    void ListeningReporter::benchmarkPreparing( std::string const& name ) {
+		for (auto const& listener : m_listeners) {
+			listener->benchmarkPreparing(name);
+		}
+		m_reporter->benchmarkPreparing(name);
+	}
+    void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->benchmarkStarting( benchmarkInfo );
+        }
+        m_reporter->benchmarkStarting( benchmarkInfo );
+    }
+    void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->benchmarkEnded( benchmarkStats );
+        }
+        m_reporter->benchmarkEnded( benchmarkStats );
+    }
+
+	void ListeningReporter::benchmarkFailed( std::string const& error ) {
+		for (auto const& listener : m_listeners) {
+			listener->benchmarkFailed(error);
+		}
+		m_reporter->benchmarkFailed(error);
+	}
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+    void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testRunStarting( testRunInfo );
+        }
+        m_reporter->testRunStarting( testRunInfo );
+    }
+
+    void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testGroupStarting( groupInfo );
+        }
+        m_reporter->testGroupStarting( groupInfo );
+    }
+
+    void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testCaseStarting( testInfo );
+        }
+        m_reporter->testCaseStarting( testInfo );
+    }
+
+    void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->sectionStarting( sectionInfo );
+        }
+        m_reporter->sectionStarting( sectionInfo );
+    }
+
+    void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->assertionStarting( assertionInfo );
+        }
+        m_reporter->assertionStarting( assertionInfo );
+    }
+
+    // The return value indicates if the messages buffer should be cleared:
+    bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) {
+        for( auto const& listener : m_listeners ) {
+            static_cast<void>( listener->assertionEnded( assertionStats ) );
+        }
+        return m_reporter->assertionEnded( assertionStats );
+    }
+
+    void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->sectionEnded( sectionStats );
+        }
+        m_reporter->sectionEnded( sectionStats );
+    }
+
+    void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testCaseEnded( testCaseStats );
+        }
+        m_reporter->testCaseEnded( testCaseStats );
+    }
+
+    void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testGroupEnded( testGroupStats );
+        }
+        m_reporter->testGroupEnded( testGroupStats );
+    }
+
+    void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testRunEnded( testRunStats );
+        }
+        m_reporter->testRunEnded( testRunStats );
+    }
+
+    void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->skipTest( testInfo );
+        }
+        m_reporter->skipTest( testInfo );
+    }
+
+    bool ListeningReporter::isMulti() const {
+        return true;
+    }
+
+} // end namespace Catch
+// end catch_reporter_listening.cpp
+// start catch_reporter_xml.cpp
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+                              // Note that 4062 (not all labels are handled
+                              // and default is missing) is enabled
+#endif
+
+namespace Catch {
+    XmlReporter::XmlReporter( ReporterConfig const& _config )
+    :   StreamingReporterBase( _config ),
+        m_xml(_config.stream())
+    {
+        m_reporterPrefs.shouldRedirectStdOut = true;
+        m_reporterPrefs.shouldReportAllAssertions = true;
+    }
+
+    XmlReporter::~XmlReporter() = default;
+
+    std::string XmlReporter::getDescription() {
+        return "Reports test results as an XML document";
+    }
+
+    std::string XmlReporter::getStylesheetRef() const {
+        return std::string();
+    }
+
+    void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+        m_xml
+            .writeAttribute( "filename", sourceInfo.file )
+            .writeAttribute( "line", sourceInfo.line );
+    }
+
+    void XmlReporter::noMatchingTestCases( std::string const& s ) {
+        StreamingReporterBase::noMatchingTestCases( s );
+    }
+
+    void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) {
+        StreamingReporterBase::testRunStarting( testInfo );
+        std::string stylesheetRef = getStylesheetRef();
+        if( !stylesheetRef.empty() )
+            m_xml.writeStylesheetRef( stylesheetRef );
+        m_xml.startElement( "Catch" );
+        if( !m_config->name().empty() )
+            m_xml.writeAttribute( "name", m_config->name() );
+        if (m_config->testSpec().hasFilters())
+            m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) );
+        if( m_config->rngSeed() != 0 )
+            m_xml.scopedElement( "Randomness" )
+                .writeAttribute( "seed", m_config->rngSeed() );
+    }
+
+    void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) {
+        StreamingReporterBase::testGroupStarting( groupInfo );
+        m_xml.startElement( "Group" )
+            .writeAttribute( "name", groupInfo.name );
+    }
+
+    void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
+        StreamingReporterBase::testCaseStarting(testInfo);
+        m_xml.startElement( "TestCase" )
+            .writeAttribute( "name", trim( testInfo.name ) )
+            .writeAttribute( "description", testInfo.description )
+            .writeAttribute( "tags", testInfo.tagsAsString() );
+
+        writeSourceInfo( testInfo.lineInfo );
+
+        if ( m_config->showDurations() == ShowDurations::Always )
+            m_testCaseTimer.start();
+        m_xml.ensureTagClosed();
+    }
+
+    void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) {
+        StreamingReporterBase::sectionStarting( sectionInfo );
+        if( m_sectionDepth++ > 0 ) {
+            m_xml.startElement( "Section" )
+                .writeAttribute( "name", trim( sectionInfo.name ) );
+            writeSourceInfo( sectionInfo.lineInfo );
+            m_xml.ensureTagClosed();
+        }
+    }
+
+    void XmlReporter::assertionStarting( AssertionInfo const& ) { }
+
+    bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
+
+        AssertionResult const& result = assertionStats.assertionResult;
+
+        bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+        if( includeResults || result.getResultType() == ResultWas::Warning ) {
+            // Print any info messages in <Info> tags.
+            for( auto const& msg : assertionStats.infoMessages ) {
+                if( msg.type == ResultWas::Info && includeResults ) {
+                    m_xml.scopedElement( "Info" )
+                            .writeText( msg.message );
+                } else if ( msg.type == ResultWas::Warning ) {
+                    m_xml.scopedElement( "Warning" )
+                            .writeText( msg.message );
+                }
+            }
+        }
+
+        // Drop out if result was successful but we're not printing them.
+        if( !includeResults && result.getResultType() != ResultWas::Warning )
+            return true;
+
+        // Print the expression if there is one.
+        if( result.hasExpression() ) {
+            m_xml.startElement( "Expression" )
+                .writeAttribute( "success", result.succeeded() )
+                .writeAttribute( "type", result.getTestMacroName() );
+
+            writeSourceInfo( result.getSourceInfo() );
+
+            m_xml.scopedElement( "Original" )
+                .writeText( result.getExpression() );
+            m_xml.scopedElement( "Expanded" )
+                .writeText( result.getExpandedExpression() );
+        }
+
+        // And... Print a result applicable to each result type.
+        switch( result.getResultType() ) {
+            case ResultWas::ThrewException:
+                m_xml.startElement( "Exception" );
+                writeSourceInfo( result.getSourceInfo() );
+                m_xml.writeText( result.getMessage() );
+                m_xml.endElement();
+                break;
+            case ResultWas::FatalErrorCondition:
+                m_xml.startElement( "FatalErrorCondition" );
+                writeSourceInfo( result.getSourceInfo() );
+                m_xml.writeText( result.getMessage() );
+                m_xml.endElement();
+                break;
+            case ResultWas::Info:
+                m_xml.scopedElement( "Info" )
+                    .writeText( result.getMessage() );
+                break;
+            case ResultWas::Warning:
+                // Warning will already have been written
+                break;
+            case ResultWas::ExplicitFailure:
+                m_xml.startElement( "Failure" );
+                writeSourceInfo( result.getSourceInfo() );
+                m_xml.writeText( result.getMessage() );
+                m_xml.endElement();
+                break;
+            default:
+                break;
+        }
+
+        if( result.hasExpression() )
+            m_xml.endElement();
+
+        return true;
+    }
+
+    void XmlReporter::sectionEnded( SectionStats const& sectionStats ) {
+        StreamingReporterBase::sectionEnded( sectionStats );
+        if( --m_sectionDepth > 0 ) {
+            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+            e.writeAttribute( "successes", sectionStats.assertions.passed );
+            e.writeAttribute( "failures", sectionStats.assertions.failed );
+            e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+
+            if ( m_config->showDurations() == ShowDurations::Always )
+                e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+
+            m_xml.endElement();
+        }
+    }
+
+    void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        StreamingReporterBase::testCaseEnded( testCaseStats );
+        XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
+        e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+
+        if ( m_config->showDurations() == ShowDurations::Always )
+            e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+
+        if( !testCaseStats.stdOut.empty() )
+            m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
+        if( !testCaseStats.stdErr.empty() )
+            m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline );
+
+        m_xml.endElement();
+    }
+
+    void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+        StreamingReporterBase::testGroupEnded( testGroupStats );
+        // TODO: Check testGroupStats.aborting and act accordingly.
+        m_xml.scopedElement( "OverallResults" )
+            .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
+            .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
+            .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
+        m_xml.scopedElement( "OverallResultsCases")
+            .writeAttribute( "successes", testGroupStats.totals.testCases.passed )
+            .writeAttribute( "failures", testGroupStats.totals.testCases.failed )
+            .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk );
+        m_xml.endElement();
+    }
+
+    void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) {
+        StreamingReporterBase::testRunEnded( testRunStats );
+        m_xml.scopedElement( "OverallResults" )
+            .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+            .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+            .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+        m_xml.scopedElement( "OverallResultsCases")
+            .writeAttribute( "successes", testRunStats.totals.testCases.passed )
+            .writeAttribute( "failures", testRunStats.totals.testCases.failed )
+            .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk );
+        m_xml.endElement();
+    }
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+    void XmlReporter::benchmarkPreparing(std::string const& name) {
+        m_xml.startElement("BenchmarkResults")
+            .writeAttribute("name", name);
+    }
+
+    void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
+        m_xml.writeAttribute("samples", info.samples)
+            .writeAttribute("resamples", info.resamples)
+            .writeAttribute("iterations", info.iterations)
+            .writeAttribute("clockResolution", info.clockResolution)
+            .writeAttribute("estimatedDuration", info.estimatedDuration)
+            .writeComment("All values in nano seconds");
+    }
+
+    void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
+        m_xml.startElement("mean")
+            .writeAttribute("value", benchmarkStats.mean.point.count())
+            .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count())
+            .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count())
+            .writeAttribute("ci", benchmarkStats.mean.confidence_interval);
+        m_xml.endElement();
+        m_xml.startElement("standardDeviation")
+            .writeAttribute("value", benchmarkStats.standardDeviation.point.count())
+            .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count())
+            .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count())
+            .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval);
+        m_xml.endElement();
+        m_xml.startElement("outliers")
+            .writeAttribute("variance", benchmarkStats.outlierVariance)
+            .writeAttribute("lowMild", benchmarkStats.outliers.low_mild)
+            .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe)
+            .writeAttribute("highMild", benchmarkStats.outliers.high_mild)
+            .writeAttribute("highSevere", benchmarkStats.outliers.high_severe);
+        m_xml.endElement();
+        m_xml.endElement();
+    }
+
+    void XmlReporter::benchmarkFailed(std::string const &error) {
+        m_xml.scopedElement("failed").
+            writeAttribute("message", error);
+        m_xml.endElement();
+    }
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+    CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+
+} // end namespace Catch
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+// end catch_reporter_xml.cpp
+
+namespace Catch {
+    LeakDetector leakDetector;
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// end catch_impl.hpp
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// start catch_default_main.hpp
+
+#ifndef __OBJC__
+
+#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
+// Standard C/C++ Win32 Unicode wmain entry point
+extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
+#else
+// Standard C/C++ main entry point
+int main (int argc, char * argv[]) {
+#endif
+
+    return Catch::Session().run( argc, argv );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+    Catch::registerTestMethods();
+    int result = Catch::Session().run( argc, (char**)argv );
+
+#if !CATCH_ARC_ENABLED
+    [pool drain];
+#endif
+
+    return result;
+}
+
+#endif // __OBJC__
+
+// end catch_default_main.hpp
+#endif
+
+#if !defined(CATCH_CONFIG_IMPL_ONLY)
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+#  undef CLARA_CONFIG_MAIN
+#endif
+
+#if !defined(CATCH_CONFIG_DISABLE)
+//////
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+
+#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
+#endif// CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+
+#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+
+#define CATCH_CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ )
+
+#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
+#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#else
+#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#endif
+
+#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+#define CATCH_STATIC_REQUIRE( ... )       static_assert(   __VA_ARGS__ ,      #__VA_ARGS__ );     CATCH_SUCCEED( #__VA_ARGS__ )
+#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ )
+#else
+#define CATCH_STATIC_REQUIRE( ... )       CATCH_REQUIRE( __VA_ARGS__ )
+#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ )
+#endif
+
+// "BDD-style" convenience wrappers
+#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#define CATCH_GIVEN( desc )     INTERNAL_CATCH_DYNAMIC_SECTION( "    Given: " << desc )
+#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
+#define CATCH_WHEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     When: " << desc )
+#define CATCH_AND_WHEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
+#define CATCH_THEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     Then: " << desc )
+#define CATCH_AND_THEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( "      And: " << desc )
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+#define CATCH_BENCHMARK(...) \
+    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
+#define CATCH_BENCHMARK_ADVANCED(name) \
+    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name)
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__  )
+#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+
+#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+
+#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+
+#define CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
+#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ )
+
+#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
+#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
+#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
+#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#else
+#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
+#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#endif
+
+#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+#define STATIC_REQUIRE( ... )       static_assert(   __VA_ARGS__,  #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
+#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" )
+#else
+#define STATIC_REQUIRE( ... )       REQUIRE( __VA_ARGS__ )
+#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ )
+#endif
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+
+// "BDD-style" convenience wrappers
+#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+
+#define GIVEN( desc )     INTERNAL_CATCH_DYNAMIC_SECTION( "    Given: " << desc )
+#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
+#define WHEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     When: " << desc )
+#define AND_WHEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
+#define THEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     Then: " << desc )
+#define AND_THEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( "      And: " << desc )
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+#define BENCHMARK(...) \
+    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
+#define BENCHMARK_ADVANCED(name) \
+    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name)
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+using Catch::Detail::Approx;
+
+#else // CATCH_CONFIG_DISABLE
+
+//////
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( ... )        (void)(0)
+#define CATCH_REQUIRE_FALSE( ... )  (void)(0)
+
+#define CATCH_REQUIRE_THROWS( ... ) (void)(0)
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher )     (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif// CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0)
+
+#define CATCH_CHECK( ... )         (void)(0)
+#define CATCH_CHECK_FALSE( ... )   (void)(0)
+#define CATCH_CHECKED_IF( ... )    if (__VA_ARGS__)
+#define CATCH_CHECKED_ELSE( ... )  if (!(__VA_ARGS__))
+#define CATCH_CHECK_NOFAIL( ... )  (void)(0)
+
+#define CATCH_CHECK_THROWS( ... )  (void)(0)
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
+#define CATCH_CHECK_THROWS_WITH( expr, matcher )     (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_CHECK_NOTHROW( ... ) (void)(0)
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_CHECK_THAT( arg, matcher )   (void)(0)
+
+#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+#define CATCH_INFO( msg )          (void)(0)
+#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
+#define CATCH_WARN( msg )          (void)(0)
+#define CATCH_CAPTURE( msg )       (void)(0)
+
+#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+#define CATCH_METHOD_AS_TEST_CASE( method, ... )
+#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
+#define CATCH_SECTION( ... )
+#define CATCH_DYNAMIC_SECTION( ... )
+#define CATCH_FAIL( ... ) (void)(0)
+#define CATCH_FAIL_CHECK( ... ) (void)(0)
+#define CATCH_SUCCEED( ... ) (void)(0)
+
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
+#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#else
+#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#endif
+
+// "BDD-style" convenience wrappers
+#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
+#define CATCH_GIVEN( desc )
+#define CATCH_AND_GIVEN( desc )
+#define CATCH_WHEN( desc )
+#define CATCH_AND_WHEN( desc )
+#define CATCH_THEN( desc )
+#define CATCH_AND_THEN( desc )
+
+#define CATCH_STATIC_REQUIRE( ... )       (void)(0)
+#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0)
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#define REQUIRE( ... )       (void)(0)
+#define REQUIRE_FALSE( ... ) (void)(0)
+
+#define REQUIRE_THROWS( ... ) (void)(0)
+#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
+#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define REQUIRE_NOTHROW( ... ) (void)(0)
+
+#define CHECK( ... ) (void)(0)
+#define CHECK_FALSE( ... ) (void)(0)
+#define CHECKED_IF( ... ) if (__VA_ARGS__)
+#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__))
+#define CHECK_NOFAIL( ... ) (void)(0)
+
+#define CHECK_THROWS( ... )  (void)(0)
+#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
+#define CHECK_THROWS_WITH( expr, matcher ) (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CHECK_NOTHROW( ... ) (void)(0)
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THAT( arg, matcher ) (void)(0)
+
+#define REQUIRE_THAT( arg, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+#define INFO( msg ) (void)(0)
+#define UNSCOPED_INFO( msg ) (void)(0)
+#define WARN( msg ) (void)(0)
+#define CAPTURE( msg ) (void)(0)
+
+#define TEST_CASE( ... )  INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+#define METHOD_AS_TEST_CASE( method, ... )
+#define REGISTER_TEST_CASE( Function, ... ) (void)(0)
+#define SECTION( ... )
+#define DYNAMIC_SECTION( ... )
+#define FAIL( ... ) (void)(0)
+#define FAIL_CHECK( ... ) (void)(0)
+#define SUCCEED( ... ) (void)(0)
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
+#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#else
+#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
+#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#endif
+
+#define STATIC_REQUIRE( ... )       (void)(0)
+#define STATIC_REQUIRE_FALSE( ... ) (void)(0)
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+
+// "BDD-style" convenience wrappers
+#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
+
+#define GIVEN( desc )
+#define AND_GIVEN( desc )
+#define WHEN( desc )
+#define AND_WHEN( desc )
+#define THEN( desc )
+#define AND_THEN( desc )
+
+using Catch::Detail::Approx;
+
+#endif
+
+#endif // ! CATCH_CONFIG_IMPL_ONLY
+
+// start catch_reenable_warnings.h
+
+
+#ifdef __clang__
+#    ifdef __ICC // icpc defines the __clang__ macro
+#        pragma warning(pop)
+#    else
+#        pragma clang diagnostic pop
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic pop
+#endif
+
+// end catch_reenable_warnings.h
+// end catch.hpp
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/tests/custom_memory/custom_memory.c b/tests/custom_memory/custom_memory.c
new file mode 100644
index 0000000..085cccf
--- /dev/null
+++ b/tests/custom_memory/custom_memory.c
@@ -0,0 +1,35 @@
+#  include "custom_memory.h"
+#  include <stdlib.h>
+#  include <stdio.h>
+
+//Make a global counter and track the net number of allocations
+//by user defined allocators.   Should go to zero on exit if no leaks.
+long int alloc_counter = 0;
+
+void* my_malloc(size_t size) {
+  void *m = malloc(size);
+  alloc_counter++;
+  /* printf("OSQP allocator  (malloc): %zu bytes, %ld allocations \n",size, alloc_counter); */
+  return m;
+}
+
+void* my_calloc(size_t num, size_t size) {
+  void *m = calloc(num, size);
+  alloc_counter++;
+  /* printf("OSQP allocator  (calloc): %zu bytes, %ld allocations \n",num*size, alloc_counter); */
+  return m;
+}
+
+void* my_realloc(void *ptr, size_t size) {
+  void *m = realloc(ptr,size);
+  /* printf("OSQP allocator (realloc) : %zu bytes, %ld allocations \n",size, alloc_counter); */
+  return m;
+}
+
+void my_free(void *ptr) {
+  if(ptr != NULL){
+    free(ptr);
+    alloc_counter--;
+    /* printf("OSQP allocator   (free) : %ld allocations \n", alloc_counter); */
+  }
+}
diff --git a/tests/custom_memory/custom_memory.h b/tests/custom_memory/custom_memory.h
new file mode 100644
index 0000000..768516d
--- /dev/null
+++ b/tests/custom_memory/custom_memory.h
@@ -0,0 +1,27 @@
+#ifndef OSQP_CUSTOM_MEMORY_H
+
+#define OSQP_CUSTOM_MEMORY_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* ifdef __cplusplus */
+
+#  include <stdio.h> //for size_t
+
+#  define c_malloc  my_malloc
+#  define c_calloc  my_calloc
+#  define c_free    my_free
+#  define c_realloc my_realloc
+
+/* functions should have the same
+signatures as the standard ones */
+void* my_malloc(size_t size);
+void* my_calloc(size_t num, size_t size);
+void* my_realloc(void *ptr, size_t size);
+void  my_free(void *ptr);
+
+# ifdef __cplusplus
+}
+# endif /* ifdef __cplusplus */
+
+#endif /* ifndef  OSQP_CUSTOM_MEMORY_H */
diff --git a/tests/demo/CMakeLists.txt b/tests/demo/CMakeLists.txt
new file mode 100644
index 0000000..1fd4af2
--- /dev/null
+++ b/tests/demo/CMakeLists.txt
@@ -0,0 +1,7 @@
+get_directory_property(headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION headers)
+                        
+set(headers ${headers}
+    ${CMAKE_CURRENT_SOURCE_DIR}/demo.h PARENT_SCOPE)
+
diff --git a/tests/demo/test_demo.h b/tests/demo/test_demo.h
new file mode 100644
index 0000000..10ff138
--- /dev/null
+++ b/tests/demo/test_demo.h
@@ -0,0 +1,62 @@
+#include "osqp.h"        // OSQP API
+#include "osqp_tester.h" // Basic testing script header
+
+void test_demo_solve()
+{
+  // Load problem data
+  c_float P_x[3] = { 4.0, 1.0, 2.0, };
+  c_int   P_nnz  = 3;
+  c_int   P_i[3] = { 0, 0, 1, };
+  c_int   P_p[3] = { 0, 1, 3, };
+  c_float q[2]   = { 1.0, 1.0, };
+  c_float A_x[4] = { 1.0, 1.0, 1.0, 1.0, };
+  c_int   A_nnz  = 4;
+  c_int   A_i[4] = { 0, 1, 0, 2, };
+  c_int   A_p[3] = { 0, 2, 4, };
+  c_float l[3]   = { 1.0, 0.0, 0.0, };
+  c_float u[3]   = { 1.0, 0.7, 0.7, };
+  c_int n = 2;
+  c_int m = 3;
+
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // OSQPData
+
+  // Populate data
+  data = (OSQPData *)c_malloc(sizeof(OSQPData));
+  data->n = n;
+  data->m = m;
+  data->P = csc_matrix(data->n, data->n, P_nnz, P_x, P_i, P_p);
+  data->q = q;
+  data->A = csc_matrix(data->m, data->n, A_nnz, A_x, A_i, A_p);
+  data->l = l;
+  data->u = u;
+
+  // Define solver settings as default
+  osqp_set_default_settings(settings);
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Demo test solve: Setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Demo test solve: Error in solver status!",
+	work->info->status_val == OSQP_SOLVED);
+
+  // Clean workspace
+  osqp_cleanup(work);
+  c_free(data->A);
+  c_free(data->P);
+  c_free(data);
+  c_free(settings);
+}
\ No newline at end of file
diff --git a/tests/generate_tests_data.py b/tests/generate_tests_data.py
new file mode 100644
index 0000000..acf387e
--- /dev/null
+++ b/tests/generate_tests_data.py
@@ -0,0 +1,11 @@
+# Code to generate the unittests for OSQP C code
+
+import basic_qp.generate_problem
+import basic_qp2.generate_problem
+import lin_alg.generate_problem
+import non_cvx.generate_problem
+import primal_dual_infeasibility.generate_problem
+import primal_infeasibility.generate_problem
+import solve_linsys.generate_problem
+import unconstrained.generate_problem
+import update_matrices.generate_problem
diff --git a/tests/lin_alg/CMakeLists.txt b/tests/lin_alg/CMakeLists.txt
new file mode 100644
index 0000000..8a3ad38
--- /dev/null
+++ b/tests/lin_alg/CMakeLists.txt
@@ -0,0 +1,13 @@
+get_directory_property(headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION headers)
+
+set(headers ${headers}
+${CMAKE_CURRENT_SOURCE_DIR}/test_lin_alg.h PARENT_SCOPE)
+
+get_directory_property(codegen_headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION codegen_headers)
+
+set(codegen_headers ${codegen_headers}
+        ${CMAKE_CURRENT_SOURCE_DIR}/data.h PARENT_SCOPE)
diff --git a/tests/lin_alg/__init__.py b/tests/lin_alg/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/lin_alg/__init__.py
diff --git a/tests/lin_alg/generate_problem.py b/tests/lin_alg/generate_problem.py
new file mode 100644
index 0000000..463569c
--- /dev/null
+++ b/tests/lin_alg/generate_problem.py
@@ -0,0 +1,126 @@
+import numpy as np
+from scipy import sparse
+import scipy.sparse.linalg as sla
+import utils.codegen_utils as cu
+from numpy.random import Generator, PCG64
+
+# Set random seed for reproducibility
+rg = Generator(PCG64(2))
+
+# Test sparse matrix construction vs dense
+test_sp_matrix_Adns = np.around(.6*rg.random((5, 6))) + rg.standard_normal((5,6))
+test_sp_matrix_A = sparse.csc_matrix(test_sp_matrix_Adns)
+
+
+# Test vector operations
+test_vec_ops_n = 10
+test_vec_ops_v1 = rg.standard_normal(test_vec_ops_n)
+test_vec_ops_v2 = rg.standard_normal(test_vec_ops_n)
+test_vec_ops_sc = rg.standard_normal()
+test_vec_ops_norm_inf = np.linalg.norm(test_vec_ops_v1, np.inf)
+test_vec_ops_norm_inf_diff = np.linalg.norm(test_vec_ops_v1 - test_vec_ops_v2,
+                                            np.inf)
+test_vec_ops_add_scaled = test_vec_ops_v1 + test_vec_ops_sc * test_vec_ops_v2
+test_vec_ops_ew_reciprocal = np.reciprocal(test_vec_ops_v1)
+test_vec_ops_vec_prod = test_vec_ops_v1.dot(test_vec_ops_v2)
+test_vec_ops_ew_max_vec = np.maximum(test_vec_ops_v1, test_vec_ops_v2)
+test_vec_ops_ew_min_vec = np.minimum(test_vec_ops_v1, test_vec_ops_v2)
+
+
+# Test matrix operations
+test_mat_ops_n = 2
+test_mat_ops_A = sparse.random(test_mat_ops_n, test_mat_ops_n, density=0.8, format='csc', random_state=rg)
+test_mat_ops_d = rg.standard_normal(test_mat_ops_n)
+D = sparse.diags(test_mat_ops_d, format='csc')
+test_mat_ops_prem_diag = D.dot(test_mat_ops_A).tocoo().tocsc()  # Force matrix reordering
+test_mat_ops_postm_diag = test_mat_ops_A.dot(D).tocoo().tocsc()  # Force matrix reordering
+test_mat_ops_inf_norm_cols = np.amax(np.abs(
+    np.asarray(test_mat_ops_A.todense())), axis=0)
+test_mat_ops_inf_norm_rows = np.amax(np.abs(
+    np.asarray(test_mat_ops_A.todense())), axis=1)
+
+# Test matrix vector operations
+m = 5
+n = 4
+p = 0.4
+
+test_mat_vec_n = n
+test_mat_vec_m = m
+test_mat_vec_A = sparse.random(m, n, density=1.0, format='csc', random_state=rg)
+test_mat_vec_P = sparse.random(n, n, density=0.8, format='csc', random_state=rg)
+test_mat_vec_P = test_mat_vec_P + test_mat_vec_P.T
+test_mat_vec_Pu = sparse.triu(test_mat_vec_P, format='csc')
+test_mat_vec_x = rg.standard_normal(n)
+test_mat_vec_y = rg.standard_normal(m)
+test_mat_vec_Ax = test_mat_vec_A.dot(test_mat_vec_x)
+test_mat_vec_Ax_cum = test_mat_vec_A.dot(test_mat_vec_x) + test_mat_vec_y
+test_mat_vec_ATy = test_mat_vec_A.T.dot(test_mat_vec_y)
+test_mat_vec_ATy_cum = test_mat_vec_A.T.dot(test_mat_vec_y) + test_mat_vec_x
+test_mat_vec_Px = test_mat_vec_P.dot(test_mat_vec_x)
+test_mat_vec_Px_cum = test_mat_vec_P.dot(test_mat_vec_x) + test_mat_vec_x
+
+
+# Test extract upper triangular
+test_mat_extr_triu_n = 5
+test_mat_extr_triu_P = sparse.random(test_mat_extr_triu_n, test_mat_extr_triu_n, density=0.8, format='csc', random_state=rg)
+test_mat_extr_triu_P = test_mat_extr_triu_P + test_mat_extr_triu_P.T
+test_mat_extr_triu_Pu = sparse.triu(test_mat_extr_triu_P, format='csc')
+test_mat_extr_triu_P_inf_norm_cols = np.amax(np.abs(
+    np.asarray(test_mat_extr_triu_P.todense())), axis=0)
+
+
+# Test compute quad form
+test_qpform_n = 4
+test_qpform_P = sparse.random(test_qpform_n, test_qpform_n, density=0.8, format='csc', random_state=rg)
+test_qpform_P = test_qpform_P + test_qpform_P.T
+test_qpform_Pu = sparse.triu(test_qpform_P, format='csc')
+test_qpform_x = rg.standard_normal(test_qpform_n)
+test_qpform_value = .5 * test_qpform_x.T.dot(test_qpform_P.dot(test_qpform_x))
+
+
+# Generate test data and solutions
+data = {'test_sp_matrix_A': test_sp_matrix_A,
+        'test_sp_matrix_Adns': test_sp_matrix_Adns,
+        'test_vec_ops_n': test_vec_ops_n,
+        'test_vec_ops_v1': test_vec_ops_v1,
+        'test_vec_ops_v2': test_vec_ops_v2,
+        'test_vec_ops_sc': test_vec_ops_sc,
+        'test_vec_ops_norm_inf': test_vec_ops_norm_inf,
+        'test_vec_ops_norm_inf_diff': test_vec_ops_norm_inf_diff,
+        'test_vec_ops_add_scaled': test_vec_ops_add_scaled,
+        'test_vec_ops_ew_reciprocal': test_vec_ops_ew_reciprocal,
+        'test_vec_ops_vec_prod': test_vec_ops_vec_prod,
+        'test_vec_ops_ew_max_vec': test_vec_ops_ew_max_vec,
+        'test_vec_ops_ew_min_vec': test_vec_ops_ew_min_vec,
+        'test_mat_ops_n': test_mat_ops_n,
+        'test_mat_ops_A': test_mat_ops_A,
+        'test_mat_ops_d': test_mat_ops_d,
+        'test_mat_ops_prem_diag': test_mat_ops_prem_diag,
+        'test_mat_ops_postm_diag': test_mat_ops_postm_diag,
+        'test_mat_ops_inf_norm_cols': test_mat_ops_inf_norm_cols,
+        'test_mat_ops_inf_norm_rows': test_mat_ops_inf_norm_rows,
+        'test_mat_vec_n': test_mat_vec_n,
+        'test_mat_vec_m': test_mat_vec_m,
+        'test_mat_vec_A': test_mat_vec_A,
+        'test_mat_vec_Pu': test_mat_vec_Pu,
+        'test_mat_vec_x': test_mat_vec_x,
+        'test_mat_vec_y': test_mat_vec_y,
+        'test_mat_vec_Ax': test_mat_vec_Ax,
+        'test_mat_vec_Ax_cum': test_mat_vec_Ax_cum,
+        'test_mat_vec_ATy': test_mat_vec_ATy,
+        'test_mat_vec_ATy_cum': test_mat_vec_ATy_cum,
+        'test_mat_vec_Px': test_mat_vec_Px,
+        'test_mat_vec_Px_cum': test_mat_vec_Px_cum,
+        'test_mat_extr_triu_n': test_mat_extr_triu_n,
+        'test_mat_extr_triu_P': test_mat_extr_triu_P,
+        'test_mat_extr_triu_Pu': test_mat_extr_triu_Pu,
+        'test_mat_extr_triu_P_inf_norm_cols':
+        test_mat_extr_triu_P_inf_norm_cols,
+        'test_qpform_n': test_qpform_n,
+        'test_qpform_Pu': test_qpform_Pu,
+        'test_qpform_x': test_qpform_x,
+        'test_qpform_value': test_qpform_value,
+        }
+
+# Generate test data
+cu.generate_data('lin_alg', data)
diff --git a/tests/lin_alg/test_lin_alg.h b/tests/lin_alg/test_lin_alg.h
new file mode 100644
index 0000000..735216e
--- /dev/null
+++ b/tests/lin_alg/test_lin_alg.h
@@ -0,0 +1,292 @@
+#include <stdio.h>
+#include "osqp.h"
+#include "osqp_tester.h"
+
+#include "lin_alg/data.h"
+
+void test_constr_sparse_mat() {
+  c_float *Adns; // Conversion to dense matrix
+
+  lin_alg_sols_data *data = generate_problem_lin_alg_sols_data();
+
+  // Convert sparse to dense
+  Adns = csc_to_dns(data->test_sp_matrix_A);
+
+  // Compute norm of the elementwise difference with
+  mu_assert("Linear algebra tests: error in constructing sparse/dense matrix!",
+            vec_norm_inf_diff(Adns, data->test_sp_matrix_Adns,
+                              data->test_sp_matrix_A->m *
+                              data->test_sp_matrix_A->n) < TESTS_TOL);
+
+  // Free memory
+  c_free(Adns); // because of vars from file matrices.h
+  clean_problem_lin_alg_sols_data(data);
+
+}
+
+void test_vec_operations() {
+  c_float  norm_inf, vecprod; // normInf;
+  c_float *ew_reciprocal;
+  c_float *add_scaled;
+  c_float *vec_ew_max_vec_test, *vec_ew_min_vec_test;
+
+  lin_alg_sols_data *data = generate_problem_lin_alg_sols_data();
+
+
+  // Add scaled
+  add_scaled = vec_copy(data->test_vec_ops_v1, data->test_vec_ops_n);
+  vec_add_scaled(add_scaled,
+                 add_scaled,
+                 data->test_vec_ops_v2,
+                 data->test_vec_ops_n,
+                 data->test_vec_ops_sc);
+  mu_assert(
+    "Linear algebra tests: error in vector operation, adding scaled vector",
+    vec_norm_inf_diff(add_scaled, data->test_vec_ops_add_scaled,
+                      data->test_vec_ops_n) < TESTS_TOL);
+
+  // Norm_inf of the difference
+  mu_assert(
+    "Linear algebra tests: error in vector operation, norm_inf of difference",
+    c_absval(vec_norm_inf_diff(data->test_vec_ops_v1,
+                               data->test_vec_ops_v2,
+                               data->test_vec_ops_n) -
+             data->test_vec_ops_norm_inf_diff) <
+    TESTS_TOL);
+
+  // norm_inf
+  norm_inf = vec_norm_inf(data->test_vec_ops_v1, data->test_vec_ops_n);
+  mu_assert("Linear algebra tests: error in vector operation, norm_inf",
+            c_absval(norm_inf - data->test_vec_ops_norm_inf) < TESTS_TOL);
+
+  // Elementwise reciprocal
+  ew_reciprocal = (c_float *)c_malloc(data->test_vec_ops_n * sizeof(c_float));
+  vec_ew_recipr(data->test_vec_ops_v1, ew_reciprocal, data->test_vec_ops_n);
+  mu_assert(
+    "Linear algebra tests: error in vector operation, elementwise reciprocal",
+    vec_norm_inf_diff(ew_reciprocal, data->test_vec_ops_ew_reciprocal,
+                      data->test_vec_ops_n) < TESTS_TOL);
+
+
+  // Vector product
+  vecprod = vec_prod(data->test_vec_ops_v1,
+                     data->test_vec_ops_v2,
+                     data->test_vec_ops_n);
+  mu_assert("Linear algebra tests: error in vector operation, vector product",
+            c_absval(vecprod - data->test_vec_ops_vec_prod) < TESTS_TOL);
+
+  // Elementwise maximum between two vectors
+  vec_ew_max_vec_test =
+    (c_float *)c_malloc(data->test_vec_ops_n * sizeof(c_float));
+  vec_ew_max_vec(data->test_vec_ops_v1,
+                 data->test_vec_ops_v2,
+                 vec_ew_max_vec_test,
+                 data->test_vec_ops_n);
+  mu_assert(
+    "Linear algebra tests: error in vector operation, elementwise maximum between vectors",
+    vec_norm_inf_diff(vec_ew_max_vec_test, data->test_vec_ops_ew_max_vec,
+                      data
+                      ->test_vec_ops_n) < TESTS_TOL);
+
+  // Elementwise minimum between two vectors
+  vec_ew_min_vec_test =
+    (c_float *)c_malloc(data->test_vec_ops_n * sizeof(c_float));
+  vec_ew_min_vec(data->test_vec_ops_v1,
+                 data->test_vec_ops_v2,
+                 vec_ew_min_vec_test,
+                 data->test_vec_ops_n);
+  mu_assert(
+    "Linear algebra tests: error in vector operation, elementwise minimum between vectors",
+    vec_norm_inf_diff(vec_ew_min_vec_test, data->test_vec_ops_ew_min_vec,
+                      data
+                      ->test_vec_ops_n) < TESTS_TOL);
+
+  // cleanup
+  c_free(add_scaled);
+  c_free(ew_reciprocal);
+  c_free(vec_ew_min_vec_test);
+  c_free(vec_ew_max_vec_test);
+  clean_problem_lin_alg_sols_data(data);
+}
+
+void test_mat_operations() {
+  csc *Ad, *dA; // Matrices used for tests
+  // csc *A_ewsq, *A_ewabs;     // Matrices used for tests
+  c_int exitflag = 0;
+
+  // c_float trace, fro_sq;
+  c_float *inf_norm_cols_rows_test;
+
+
+  lin_alg_sols_data *data = generate_problem_lin_alg_sols_data();
+
+
+  // Copy matrices
+  Ad = copy_csc_mat(data->test_mat_ops_A);
+  dA = copy_csc_mat(data->test_mat_ops_A);
+
+
+  // Premultiply matrix A
+  mat_premult_diag(dA, data->test_mat_ops_d);
+  mu_assert(
+    "Linear algebra tests: error in matrix operation, premultiply diagonal",
+    is_eq_csc(dA, data->test_mat_ops_prem_diag, TESTS_TOL));
+
+
+  // Postmultiply matrix A
+  mat_postmult_diag(Ad, data->test_mat_ops_d);
+  mu_assert(
+    "Linear algebra tests: error in matrix operation, postmultiply diagonal",
+    is_eq_csc(Ad, data->test_mat_ops_postm_diag, TESTS_TOL));
+
+  // Maximum norm over columns
+  inf_norm_cols_rows_test =
+    (c_float *)c_malloc(data->test_mat_ops_n * sizeof(c_float));
+  mat_inf_norm_cols(data->test_mat_ops_A, inf_norm_cols_rows_test);
+  mu_assert(
+    "Linear algebra tests: error in matrix operation, max norm over columns",
+    vec_norm_inf_diff(inf_norm_cols_rows_test, data->test_mat_ops_inf_norm_cols,
+                      data
+                      ->test_mat_ops_n) < TESTS_TOL);
+
+  // Maximum norm over rows
+  mat_inf_norm_rows(data->test_mat_ops_A, inf_norm_cols_rows_test);
+  mu_assert("Linear algebra tests: error in matrix operation, max norm over rows",
+            vec_norm_inf_diff(inf_norm_cols_rows_test,
+                              data->test_mat_ops_inf_norm_rows,
+                              data
+                              ->test_mat_ops_n) < TESTS_TOL);
+
+
+  // cleanup
+  c_free(inf_norm_cols_rows_test);
+  csc_spfree(Ad);
+  csc_spfree(dA);
+  clean_problem_lin_alg_sols_data(data);
+}
+
+void test_mat_vec_multiplication() {
+  c_float *Ax, *ATy, *Px, *Ax_cum, *ATy_cum, *Px_cum;
+
+  lin_alg_sols_data *data = generate_problem_lin_alg_sols_data();
+
+
+  // Allocate vectors
+  Ax  = (c_float *)c_malloc(data->test_mat_vec_m * sizeof(c_float));
+  ATy = (c_float *)c_malloc(data->test_mat_vec_n * sizeof(c_float));
+  Px  = (c_float *)c_malloc(data->test_mat_vec_n * sizeof(c_float));
+
+
+  // Matrix-vector multiplication:  y = Ax
+  mat_vec(data->test_mat_vec_A, data->test_mat_vec_x, Ax, 0);
+  mu_assert(
+    "Linear algebra tests: error in matrix-vector operation, matrix-vector multiplication",
+    vec_norm_inf_diff(Ax, data->test_mat_vec_Ax,
+                      data->test_mat_vec_m) < TESTS_TOL);
+
+  // Cumulative matrix-vector multiplication:  y += Ax
+  Ax_cum = vec_copy(data->test_mat_vec_y, data->test_mat_vec_m);
+  mat_vec(data->test_mat_vec_A, data->test_mat_vec_x, Ax_cum, 1);
+  mu_assert(
+    "Linear algebra tests: error in matrix-vector operation, cumulative matrix-vector multiplication",
+    vec_norm_inf_diff(Ax_cum, data->test_mat_vec_Ax_cum,
+                      data->test_mat_vec_m) < TESTS_TOL);
+
+  // Matrix-transpose-vector multiplication:  x = A'*y
+  mat_tpose_vec(data->test_mat_vec_A, data->test_mat_vec_y, ATy, 0, 0);
+  mu_assert(
+    "Linear algebra tests: error in matrix-vector operation, matrix-transpose-vector multiplication",
+    vec_norm_inf_diff(ATy, data->test_mat_vec_ATy,
+                      data->test_mat_vec_n) < TESTS_TOL);
+
+  // Cumulative matrix-transpose-vector multiplication:  x += A'*y
+  ATy_cum = vec_copy(data->test_mat_vec_x, data->test_mat_vec_n);
+  mat_tpose_vec(data->test_mat_vec_A, data->test_mat_vec_y, ATy_cum, 1, 0);
+  mu_assert(
+    "Linear algebra tests: error in matrix-vector operation, cumulative matrix-transpose-vector multiplication",
+    vec_norm_inf_diff(ATy_cum, data->test_mat_vec_ATy_cum,
+                      data->test_mat_vec_n) < TESTS_TOL);
+
+  // Symmetric-matrix-vector multiplication (only upper part is stored)
+  mat_vec(data->test_mat_vec_Pu, data->test_mat_vec_x, Px, 0);          // upper
+                                                                        // traingular
+                                                                        // part
+  mat_tpose_vec(data->test_mat_vec_Pu, data->test_mat_vec_x, Px, 1, 1); // lower
+                                                                        // traingular
+                                                                        // part
+                                                                        // (without
+                                                                        // diagonal)
+  mu_assert(
+    "Linear algebra tests: error in matrix-vector operation, symmetric matrix-vector multiplication",
+    vec_norm_inf_diff(Px, data->test_mat_vec_Px,
+                      data->test_mat_vec_n) < TESTS_TOL);
+
+
+  // Cumulative symmetric-matrix-vector multiplication
+  Px_cum = vec_copy(data->test_mat_vec_x, data->test_mat_vec_n);
+  mat_vec(data->test_mat_vec_Pu, data->test_mat_vec_x, Px_cum, 1);          // upper
+                                                                            // traingular
+                                                                            // part
+  mat_tpose_vec(data->test_mat_vec_Pu, data->test_mat_vec_x, Px_cum, 1, 1); // lower
+                                                                            // traingular
+                                                                            // part
+                                                                            // (without
+                                                                            // diagonal)
+  mu_assert(
+    "Linear algebra tests: error in matrix-vector operation, cumulative symmetric matrix-vector multiplication",
+    vec_norm_inf_diff(Px_cum, data->test_mat_vec_Px_cum,
+                      data->test_mat_vec_n) < TESTS_TOL);
+
+
+  // cleanup
+  c_free(Ax);
+  c_free(ATy);
+  c_free(Px);
+  c_free(Ax_cum);
+  c_free(ATy_cum);
+  c_free(Px_cum);
+  clean_problem_lin_alg_sols_data(data);
+}
+
+void test_extract_upper_triangular() {
+  c_float *inf_norm_cols_test;
+  lin_alg_sols_data *data = generate_problem_lin_alg_sols_data();
+
+  // Extract upper triangular part
+  csc *Ptriu = csc_to_triu(data->test_mat_extr_triu_P);
+
+  mu_assert("Linear algebra tests: error in forming upper triangular matrix!",
+            is_eq_csc(data->test_mat_extr_triu_Pu, Ptriu, TESTS_TOL));
+
+  // Compute infinity norm over columns of the original matrix by using the
+  // upper triangular part only
+  inf_norm_cols_test = (c_float *)c_malloc(data->test_mat_extr_triu_n
+                                           * sizeof(c_float));
+  mat_inf_norm_cols_sym_triu(Ptriu, inf_norm_cols_test);
+  mu_assert(
+    "Linear algebra tests: error in forming upper triangular matrix, infinity norm over columns",
+    vec_norm_inf_diff(inf_norm_cols_test,
+                      data->test_mat_extr_triu_P_inf_norm_cols,
+                      data->test_mat_extr_triu_n) < TESTS_TOL);
+
+  // Cleanup
+  c_free(inf_norm_cols_test);
+  csc_spfree(Ptriu);
+  clean_problem_lin_alg_sols_data(data);
+}
+
+void test_quad_form_upper_triang() {
+  c_float quad_form_t;
+
+  lin_alg_sols_data *data = generate_problem_lin_alg_sols_data();
+
+  // Compute quadratic form
+  quad_form_t = quad_form(data->test_qpform_Pu, data->test_qpform_x);
+
+  mu_assert(
+    "Linear algebra tests: error in computing quadratic form using upper triangular matrix!",
+    (c_absval(quad_form_t - data->test_qpform_value) < TESTS_TOL));
+
+  // cleanup
+  clean_problem_lin_alg_sols_data(data);
+}
\ No newline at end of file
diff --git a/tests/non_cvx/CMakeLists.txt b/tests/non_cvx/CMakeLists.txt
new file mode 100644
index 0000000..a601501
--- /dev/null
+++ b/tests/non_cvx/CMakeLists.txt
@@ -0,0 +1,13 @@
+get_directory_property(headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION headers)
+
+set(headers ${headers}
+${CMAKE_CURRENT_SOURCE_DIR}/test_non_cvx.h PARENT_SCOPE)
+
+get_directory_property(codegen_headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION codegen_headers)
+
+set(codegen_headers ${codegen_headers}
+        ${CMAKE_CURRENT_SOURCE_DIR}/data.h PARENT_SCOPE)
diff --git a/tests/non_cvx/__init__.py b/tests/non_cvx/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/non_cvx/__init__.py
diff --git a/tests/non_cvx/generate_problem.py b/tests/non_cvx/generate_problem.py
new file mode 100644
index 0000000..9f48a51
--- /dev/null
+++ b/tests/non_cvx/generate_problem.py
@@ -0,0 +1,15 @@
+import numpy as np
+from scipy import sparse
+import utils.codegen_utils as cu
+
+P = sparse.triu([[2., 5.], [5., 1.]], format='csc')
+q = np.array([3., 4.])
+
+A = sparse.csc_matrix([[-1., 0.], [0., -1.], [-1., 3.], [2., 5.], [3., 4]])
+l = -np.inf * np.ones(A.shape[0])
+u = np.array([0., 0., -15., 100., 80.])
+
+sols_data = {'sigma_new': 5}
+
+# Generate problem data
+cu.generate_problem_data(P, q, A, l, u, 'non_cvx', sols_data)
diff --git a/tests/non_cvx/test_non_cvx.h b/tests/non_cvx/test_non_cvx.h
new file mode 100644
index 0000000..0f9783e
--- /dev/null
+++ b/tests/non_cvx/test_non_cvx.h
@@ -0,0 +1,67 @@
+#include "osqp.h"        // OSQP API
+#include "osqp_tester.h" // Basic testing script header
+
+
+#include "non_cvx/data.h"
+
+
+void test_non_cvx_solve()
+{
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  non_cvx_sols_data *sols_data;
+
+
+  // Populate data
+  data = generate_problem_non_cvx();
+  sols_data = generate_problem_non_cvx_sols_data();
+
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->verbose = 1;
+  settings->sigma = 1e-6;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup should fail due to (P + sigma I) having a negative eigenvalue
+  mu_assert("Non Convex test solve: Setup should have failed!",
+            exitflag == OSQP_NONCVX_ERROR);
+
+  osqp_cleanup(work);
+
+  // Update Solver settings
+  settings->sigma = sols_data->sigma_new;
+
+  // Setup workspace again
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup should work this time because (P + sigma I) is positive definite
+  mu_assert("Non Convex test solve: Setup error!", exitflag == 0);
+
+  // Solve Problem first time
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Non Convex test solve: Error in solver status!",
+            work->info->status_val == OSQP_NON_CVX);
+
+  // Compare objective values
+  mu_assert("Non Convex test solve: Error in objective value!",
+            work->info->obj_val == OSQP_NAN);
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+  // Cleanup settings and data
+  c_free(settings);
+  clean_problem_non_cvx(data);
+  clean_problem_non_cvx_sols_data(sols_data);
+}
\ No newline at end of file
diff --git a/tests/osqp_tester.cpp b/tests/osqp_tester.cpp
new file mode 100644
index 0000000..26b78d1
--- /dev/null
+++ b/tests/osqp_tester.cpp
@@ -0,0 +1,154 @@
+/* OSQP TESTER MODULE */
+
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+
+#include "osqp.h"
+#include "osqp_tester.h"
+#include "lin_alg/test_lin_alg.h"
+#include "solve_linsys/test_solve_linsys.h"
+#include "demo/test_demo.h"
+#include "basic_qp/test_basic_qp.h"
+#include "basic_qp2/test_basic_qp2.h"
+#include "non_cvx/test_non_cvx.h"
+#include "primal_dual_infeasibility/test_primal_dual_infeasibility.h"
+#include "primal_infeasibility/test_primal_infeasibility.h"
+#include "unconstrained/test_unconstrained.h"
+#include "update_matrices/test_update_matrices.h"
+
+
+TEST_CASE( "test_lin_alg", "[multi-file:1]" ) {
+    SECTION( "test_constr_sparse_mat" ) {
+        test_constr_sparse_mat();
+    }
+    SECTION( "test_vec_operations" ) {
+        test_vec_operations();
+    }
+    SECTION( "test_mat_operations" ) {
+        test_mat_operations();
+    }
+    SECTION( "test_mat_vec_multiplication" ) {
+        test_mat_vec_multiplication();
+    }
+    SECTION( "test_extract_upper_triangular" ) {
+        test_extract_upper_triangular();
+    }
+    SECTION( "test_quad_form_upper_triang" ) {
+        test_quad_form_upper_triang();
+    }
+}
+
+
+TEST_CASE( "test_solve_linsys", "[multi-file:2]" ) {
+    SECTION( "test_solveKKT" ) {
+        test_solveKKT();
+    }
+#ifdef ENABLE_MKL_PARDISO
+    SECTION( "test_solveKKT_pardiso" ) {
+        test_solveKKT_pardiso();
+    }
+#endif
+}
+
+
+TEST_CASE( "test_demo", "[multi-file:3]" ) {
+    SECTION("test_demo_solve") {
+        test_demo_solve();
+    }
+}
+
+
+TEST_CASE( "test_basic_qp", "[multi-file:4]" ) {
+    SECTION( "test_basic_qp_solve" ) {
+        test_basic_qp_solve();
+    }
+#ifdef ENABLE_MKL_PARDISO
+        SECTION( "test_basic_qp_solve_pardiso" ) {
+        test_basic_qp_solve_pardiso();
+    }
+#endif
+    SECTION( "test_basic_qp_update" ) {
+        test_basic_qp_update();
+    }
+    SECTION( "test_basic_qp_check_termination" ) {
+        test_basic_qp_check_termination();
+    }
+    SECTION( "test_basic_qp_update_rho" ) {
+        test_basic_qp_update_rho();
+    }
+#ifdef PROFILING
+    SECTION( "test_basic_qp_time_limit" ) {
+        test_basic_qp_time_limit();
+    }
+#endif
+    SECTION( "test_basic_qp_warm_start" ) {
+        test_basic_qp_warm_start();
+    }
+}
+
+
+TEST_CASE( "test_basic_qp2", "[multi-file:5]" ) {
+    SECTION( "test_basic_qp2_solve" ) {
+        test_basic_qp2_solve();
+    }
+#ifdef ENABLE_MKL_PARDISO
+    SECTION( "test_basic_qp2_solve_pardiso" ) {
+        test_basic_qp2_solve_pardiso();
+    }
+#endif
+    SECTION( "test_basic_qp2_update" ) {
+        test_basic_qp2_update();
+    }
+}
+
+
+TEST_CASE( "test_non_cvx", "[multi-file:6]" ) {
+    SECTION( "test_non_cvx_solve" ) {
+        test_non_cvx_solve();
+    }
+}
+
+
+TEST_CASE( "test_primal_infeasibility", "[multi-file:7]" ) {
+    SECTION( "test_primal_infeasible_qp_solve" ) {
+        test_primal_infeasible_qp_solve();
+    }
+}
+
+
+TEST_CASE( "test_primal_dual_infeasibility", "[multi-file:8]" ) {
+    SECTION( "test_optimal" ) {
+        test_optimal();
+    }
+    SECTION( "test_prim_infeas" ) {
+        test_prim_infeas();
+    }
+    SECTION( "test_dual_infeas" ) {
+        test_dual_infeas();
+    }
+    SECTION( "test_primal_dual_infeas" ) {
+        test_primal_dual_infeas();
+    }
+}
+
+
+TEST_CASE( "test_unconstrained", "[multi-file:9]" ) {
+    SECTION( "test_unconstrained_solve" ) {
+        test_unconstrained_solve();
+    }
+}
+
+
+TEST_CASE( "test_update_matrices", "[multi-file:10]" ) {
+    SECTION( "test_form_KKT" ) {
+        test_form_KKT();
+    }
+    SECTION( "test_update" ) {
+        test_update();
+    }
+#ifdef ENABLE_MKL_PARDISO
+    SECTION( "test_update_pardiso" ) {
+        test_update_pardiso();
+    }
+#endif
+}
diff --git a/tests/osqp_tester.h b/tests/osqp_tester.h
new file mode 100644
index 0000000..0d34e3a
--- /dev/null
+++ b/tests/osqp_tester.h
@@ -0,0 +1,58 @@
+// Utilities for testing
+
+#ifndef OSQP_TESTER_H
+#define OSQP_TESTER_H
+
+#ifndef EMBEDDED
+
+#define mu_assert(msg, pred) do { INFO(msg); REQUIRE(pred); } while((void)0, 0)
+#define TESTS_TOL 1e-4 // Define tests tolerance
+
+c_float* csc_to_dns(csc *M)
+{
+  c_int i, j = 0; // Predefine row index and column index
+  c_int idx;
+
+  // Initialize matrix of zeros
+  c_float *A = (c_float *)c_calloc(M->m * M->n, sizeof(c_float));
+
+  // Allocate elements
+  for (idx = 0; idx < M->p[M->n]; idx++)
+  {
+    // Get row index i (starting from 1)
+    i = M->i[idx];
+
+    // Get column index j (increase if necessary) (starting from 1)
+    while (M->p[j + 1] <= idx) {
+      j++;
+    }
+
+    // Assign values to A
+    A[j * (M->m) + i] = M->x[idx];
+  }
+  return A;
+}
+
+c_int is_eq_csc(csc *A, csc *B, c_float tol) {
+  c_int j, i;
+
+  // If number of columns does not coincide, they are not equal.
+  if (A->n != B->n) return 0;
+
+  for (j = 0; j < A->n; j++) { // Cycle over columns j
+    // if column pointer does not coincide, they are not equal
+    if (A->p[j] != B->p[j]) return 0;
+
+    for (i = A->p[j]; i < A->p[j + 1]; i++) { // Cycle rows i in column j
+      if ((A->i[i] != B->i[i]) ||             // Different row indices
+          (c_absval(A->x[i] - B->x[i]) > tol)) {
+        return 0;
+      }
+    }
+  }
+  return 1;
+}
+
+#endif // #ifndef EMBEDDED
+
+#endif // #ifndef OSQP_TESTER_H
\ No newline at end of file
diff --git a/tests/primal_dual_infeasibility/CMakeLists.txt b/tests/primal_dual_infeasibility/CMakeLists.txt
new file mode 100644
index 0000000..7e6e34d
--- /dev/null
+++ b/tests/primal_dual_infeasibility/CMakeLists.txt
@@ -0,0 +1,13 @@
+get_directory_property(headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION headers)
+
+set(headers ${headers}
+    ${CMAKE_CURRENT_SOURCE_DIR}/test_primal_dual_infeasibility.h PARENT_SCOPE)
+
+get_directory_property(codegen_headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION codegen_headers)
+
+set(codegen_headers ${codegen_headers}
+        ${CMAKE_CURRENT_SOURCE_DIR}/data.h PARENT_SCOPE)
diff --git a/tests/primal_dual_infeasibility/__init__.py b/tests/primal_dual_infeasibility/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/primal_dual_infeasibility/__init__.py
diff --git a/tests/primal_dual_infeasibility/generate_problem.py b/tests/primal_dual_infeasibility/generate_problem.py
new file mode 100644
index 0000000..11cf856
--- /dev/null
+++ b/tests/primal_dual_infeasibility/generate_problem.py
@@ -0,0 +1,36 @@
+import numpy as np
+from scipy import sparse
+import utils.codegen_utils as cu
+
+P = sparse.diags([1., 0.], format='csc')
+q = np.array([1., -1.])
+
+A12 = sparse.csc_matrix([[1., 1.], [1., 0.], [0., 1.]])
+A34 = sparse.csc_matrix([[1., 0.], [1., 0.], [0., 1.]])
+l = np.array([0., 1., 1.])
+u1 = np.array([5., 3., 3.])
+u2 = np.array([0., 3., 3.])
+u3 = np.array([2., 3., np.inf])
+u4 = np.array([0., 3., np.inf])
+
+# Generate problem solutions
+data = {'P': P,
+        'q': q,
+        'A12': A12,
+        'A34': A34,
+        'l': l,
+        'u1': u1,
+        'u2': u2,
+        'u3': u3,
+        'u4': u4,
+        'x1': np.array([1., 3.]),
+        'y1': np.array([0., -2., 1.]),
+        'obj_value1': -1.5,
+        'status1': 'optimal',
+        'status2': 'primal_infeasible',
+        'status3': 'dual_infeasible',
+        'status4': 'primal_infeasible'
+        }
+
+# Generate problem data
+cu.generate_data('primal_dual_infeasibility', data)
diff --git a/tests/primal_dual_infeasibility/test_primal_dual_infeasibility.h b/tests/primal_dual_infeasibility/test_primal_dual_infeasibility.h
new file mode 100644
index 0000000..8a26987
--- /dev/null
+++ b/tests/primal_dual_infeasibility/test_primal_dual_infeasibility.h
@@ -0,0 +1,232 @@
+#include "osqp.h"
+#include "cs.h"
+#include "util.h"
+#include "osqp_tester.h"
+
+#include "primal_dual_infeasibility/data.h"
+
+
+void test_optimal()
+{
+  c_int exitflag;
+
+  // Structures
+  OSQPWorkspace *work;    // Workspace
+  OSQPData *problem;      // Problem data
+  OSQPSettings *settings; // Settings
+  primal_dual_infeasibility_sols_data *data;
+
+  // Load problem data
+  data = generate_problem_primal_dual_infeasibility_sols_data();
+
+  // Populate problem data
+  problem    = (OSQPData*) c_malloc(sizeof(OSQPData));
+  problem->P = data->P;
+  problem->q = data->q;
+  problem->A = data->A12;
+  problem->l = data->l;
+  problem->u = data->u1;
+  problem->n = data->P->n;
+  problem->m = data->A12->m;
+
+  // Define Solver settings as default
+  settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+  osqp_set_default_settings(settings);
+  settings->max_iter = 2000;
+  settings->alpha    = 1.6;
+  settings->polish   = 1;
+  settings->scaling  = 0;
+  settings->verbose  = 1;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, problem, settings);
+
+  // Setup correct
+  mu_assert("Primal dual infeasibility test 1: Setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Primal dual infeasibility test 1: Error in solver status!",
+            work->info->status_val == OSQP_SOLVED);
+
+  // Compare primal solutions
+  mu_assert("Primal dual infeasibility test 1: Error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, data->x1,
+                              problem->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Primal dual infeasibility test 1: Error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, data->y1,
+                              problem->m) < TESTS_TOL);
+
+
+  // Compare objective values
+  mu_assert("Primal dual infeasibility test 1: Error in objective value!",
+            c_absval(work->info->obj_val - data->obj_value1) < TESTS_TOL);
+
+
+  // Cleanup
+  osqp_cleanup(work);
+  clean_problem_primal_dual_infeasibility_sols_data(data);
+  c_free(problem);
+  c_free(settings);
+}
+
+void test_prim_infeas()
+{
+  c_int exitflag;
+
+  // Structures
+  OSQPWorkspace *work;    // Workspace
+  OSQPData *problem;      // Problem data
+  OSQPSettings *settings; // Settings
+  primal_dual_infeasibility_sols_data *data;
+
+  // Load problem data
+  data = generate_problem_primal_dual_infeasibility_sols_data();
+
+  // Populate problem data
+  problem    = (OSQPData*) c_malloc(sizeof(OSQPData));
+  problem->P = data->P;
+  problem->q = data->q;
+  problem->A = data->A12;
+  problem->l = data->l;
+  problem->u = data->u2;
+  problem->n = data->P->n;
+  problem->m = data->A12->m;
+
+  // Define Solver settings as default
+  settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+  osqp_set_default_settings(settings);
+  settings->max_iter = 2000;
+  settings->alpha    = 1.6;
+  settings->polish   = 0;
+  settings->scaling  = 0;
+  settings->verbose  = 1;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, problem, settings);
+
+  // Setup correct
+  mu_assert("Primal dual infeasibility test 2: Setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Primal dual infeasibility test 2: Error in solver status!",
+            work->info->status_val == OSQP_PRIMAL_INFEASIBLE);
+
+  // Cleanup
+  osqp_cleanup(work);
+  clean_problem_primal_dual_infeasibility_sols_data(data);
+  c_free(problem);
+  c_free(settings);
+}
+
+void test_dual_infeas()
+{
+  c_int exitflag;
+
+  // Structures
+  OSQPWorkspace *work;    // Workspace
+  OSQPData *problem;      // Problem data
+  OSQPSettings *settings; // Settings
+  primal_dual_infeasibility_sols_data *data;
+
+  // Load problem data
+  data = generate_problem_primal_dual_infeasibility_sols_data();
+
+  // Populate problem data
+  problem    = (OSQPData*) c_malloc(sizeof(OSQPData));
+  problem->P = data->P;
+  problem->q = data->q;
+  problem->A = data->A34;
+  problem->l = data->l;
+  problem->u = data->u3;
+  problem->n = data->P->n;
+  problem->m = data->A34->m;
+
+  // Define Solver settings as default
+  settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+  osqp_set_default_settings(settings);
+  settings->max_iter = 2000;
+  settings->alpha    = 1.6;
+  settings->polish   = 0;
+  settings->scaling  = 0;
+  settings->verbose  = 1;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, problem, settings);
+
+  // Setup correct
+  mu_assert("Primal dual infeasibility test 3: Setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Primal dual infeasibility test 3: Error in solver status!",
+            work->info->status_val == OSQP_DUAL_INFEASIBLE);
+
+  // Cleanup
+  osqp_cleanup(work);
+  clean_problem_primal_dual_infeasibility_sols_data(data);
+  c_free(problem);
+  c_free(settings);
+}
+
+void test_primal_dual_infeas()
+{
+  c_int exitflag;
+
+  // Structures
+  OSQPWorkspace *work;    // Workspace
+  OSQPData *problem;      // Problem data
+  OSQPSettings *settings; // Settings
+  primal_dual_infeasibility_sols_data *data;
+
+  // Load problem data
+  data = generate_problem_primal_dual_infeasibility_sols_data();
+
+  // Populate problem data
+  problem    = (OSQPData*) c_malloc(sizeof(OSQPData));
+  problem->P = data->P;
+  problem->q = data->q;
+  problem->A = data->A34;
+  problem->l = data->l;
+  problem->u = data->u4;
+  problem->n = data->P->n;
+  problem->m = data->A34->m;
+
+  // Define Solver settings as default
+  settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+  osqp_set_default_settings(settings);
+  settings->max_iter = 2000;
+  settings->alpha    = 1.6;
+  settings->polish   = 0;
+  settings->scaling  = 0;
+  settings->verbose  = 1;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, problem, settings);
+
+  // Setup correct
+  mu_assert("Primal dual infeasibility test 4: Setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Primal dual infeasibility test 4: Error in solver status!",
+            ((work->info->status_val == OSQP_PRIMAL_INFEASIBLE) ||
+             (work->info->status_val == OSQP_DUAL_INFEASIBLE)));
+
+  // Cleanup
+  osqp_cleanup(work);
+  clean_problem_primal_dual_infeasibility_sols_data(data);
+  c_free(problem);
+  c_free(settings);
+}
\ No newline at end of file
diff --git a/tests/primal_infeasibility/CMakeLists.txt b/tests/primal_infeasibility/CMakeLists.txt
new file mode 100644
index 0000000..70c8c90
--- /dev/null
+++ b/tests/primal_infeasibility/CMakeLists.txt
@@ -0,0 +1,13 @@
+get_directory_property(headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION headers)
+
+set(headers ${headers}
+${CMAKE_CURRENT_SOURCE_DIR}/test_primal_infeasibility.h PARENT_SCOPE)
+
+get_directory_property(codegen_headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION codegen_headers)
+
+set(codegen_headers ${codegen_headers}
+        ${CMAKE_CURRENT_SOURCE_DIR}/data.h PARENT_SCOPE)
diff --git a/tests/primal_infeasibility/__init__.py b/tests/primal_infeasibility/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/primal_infeasibility/__init__.py
diff --git a/tests/primal_infeasibility/generate_problem.py b/tests/primal_infeasibility/generate_problem.py
new file mode 100644
index 0000000..42ca3bd
--- /dev/null
+++ b/tests/primal_infeasibility/generate_problem.py
@@ -0,0 +1,35 @@
+import numpy as np
+from scipy import sparse
+import scipy as sp
+import utils.codegen_utils as cu
+from numpy.random import Generator, PCG64
+
+# Set random seed for reproducibility
+rg = Generator(PCG64(2))
+
+n = 50
+m = 150
+
+# Generate random Matrices
+Pt = sparse.random(n, n, random_state=rg)
+P = Pt.T.dot(Pt) + sparse.eye(n)
+P = sparse.triu(P, format='csc')
+q = sp.randn(n)
+A = sparse.random(m, n, random_state=rg).tolil()  # Lil for efficiency
+u = 3 + sp.randn(m)
+l = -3 + sp.randn(m)
+
+# Make random problem primal infeasible
+A[int(n/2), :] = A[int(n/2)+1, :]
+l[int(n/2)] = u[int(n/2)+1] + 10 * rg.random()
+u[int(n/2)] = l[int(n/2)] + 0.5
+
+# Convert A to csc
+A = A.tocsc()
+
+# Generate problem solutions
+sols_data = {'status_test': 'primal_infeasible'}
+
+
+# Generate problem data
+cu.generate_problem_data(P, q, A, l, u, 'primal_infeasibility', sols_data)
diff --git a/tests/primal_infeasibility/test_primal_infeasibility.h b/tests/primal_infeasibility/test_primal_infeasibility.h
new file mode 100644
index 0000000..17373df
--- /dev/null
+++ b/tests/primal_infeasibility/test_primal_infeasibility.h
@@ -0,0 +1,59 @@
+#include "osqp.h"        // OSQP API
+#include "cs.h"          // CSC data structure
+#include "util.h"        // Utilities for testing
+#include "osqp_tester.h" // Basic testing script header
+
+#include "primal_infeasibility/data.h"
+
+
+void test_primal_infeasible_qp_solve()
+{
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  primal_infeasibility_sols_data *sols_data;
+
+  // Populate data
+  data      = generate_problem_primal_infeasibility();
+  sols_data = generate_problem_primal_infeasibility_sols_data();
+
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->max_iter   = 10000;
+  settings->alpha      = 1.6;
+  settings->polish     = 1;
+  settings->scaling    = 0;
+  settings->verbose    = 1;
+  settings->warm_start = 0;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Primal infeasible QP test solve: Setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Primal infeasible QP test solve: Error in solver status!",
+            work->info->status_val == sols_data->status_test);
+
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+
+  // Cleanup data
+  clean_problem_primal_infeasibility(data);
+  clean_problem_primal_infeasibility_sols_data(sols_data);
+
+  // Cleanup
+  c_free(settings);
+}
\ No newline at end of file
diff --git a/tests/solve_linsys/CMakeLists.txt b/tests/solve_linsys/CMakeLists.txt
new file mode 100644
index 0000000..1d589d0
--- /dev/null
+++ b/tests/solve_linsys/CMakeLists.txt
@@ -0,0 +1,13 @@
+get_directory_property(headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION headers)
+
+set(headers ${headers}
+${CMAKE_CURRENT_SOURCE_DIR}/test_solve_linsys.h PARENT_SCOPE)
+
+get_directory_property(codegen_headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION codegen_headers)
+
+set(codegen_headers ${codegen_headers}
+        ${CMAKE_CURRENT_SOURCE_DIR}/data.h PARENT_SCOPE)
diff --git a/tests/solve_linsys/__init__.py b/tests/solve_linsys/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/solve_linsys/__init__.py
diff --git a/tests/solve_linsys/generate_problem.py b/tests/solve_linsys/generate_problem.py
new file mode 100644
index 0000000..3ab9aa5
--- /dev/null
+++ b/tests/solve_linsys/generate_problem.py
@@ -0,0 +1,48 @@
+import numpy as np
+from scipy import sparse
+import scipy.sparse.linalg as spla
+import utils.codegen_utils as cu
+from numpy.random import Generator, PCG64
+
+# Set random seed for reproducibility
+rg = Generator(PCG64(2))
+
+# Simple case
+test_solve_KKT_n = 3
+test_solve_KKT_m = 4
+
+test_solve_KKT_P = sparse.random(test_solve_KKT_n, test_solve_KKT_n,
+                                 density=0.4, format='csc', random_state=rg)
+test_solve_KKT_P = test_solve_KKT_P.dot(test_solve_KKT_P.T).tocsc()
+test_solve_KKT_A = sparse.random(test_solve_KKT_m, test_solve_KKT_n,
+                                 density=0.4, format='csc', random_state=rg)
+test_solve_KKT_Pu = sparse.triu(test_solve_KKT_P, format='csc')
+
+test_solve_KKT_rho = 4.0
+test_solve_KKT_sigma = 1.0
+test_solve_KKT_KKT = sparse.vstack([
+                        sparse.hstack([test_solve_KKT_P + test_solve_KKT_sigma *
+                        sparse.eye(test_solve_KKT_n), test_solve_KKT_A.T]),
+                        sparse.hstack([test_solve_KKT_A,
+                        -1./test_solve_KKT_rho * sparse.eye(test_solve_KKT_m)])
+                        ], format='csc')
+test_solve_KKT_rhs = rg.standard_normal(test_solve_KKT_m + test_solve_KKT_n)
+test_solve_KKT_x = spla.splu(test_solve_KKT_KKT).solve(test_solve_KKT_rhs)
+
+test_solve_KKT_x[test_solve_KKT_n:] = test_solve_KKT_rhs[test_solve_KKT_n:] + \
+                                      test_solve_KKT_x[test_solve_KKT_n:] / test_solve_KKT_rho
+
+# Generate test data and solutions
+data = {'test_solve_KKT_n': test_solve_KKT_n,
+        'test_solve_KKT_m': test_solve_KKT_m,
+        'test_solve_KKT_A': test_solve_KKT_A,
+        'test_solve_KKT_Pu': test_solve_KKT_Pu,
+        'test_solve_KKT_rho': test_solve_KKT_rho,
+        'test_solve_KKT_sigma': test_solve_KKT_sigma,
+        'test_solve_KKT_KKT': test_solve_KKT_KKT,
+        'test_solve_KKT_rhs': test_solve_KKT_rhs,
+        'test_solve_KKT_x': test_solve_KKT_x
+        }
+
+# Generate test data
+cu.generate_data('solve_linsys', data)
diff --git a/tests/solve_linsys/test_solve_linsys.h b/tests/solve_linsys/test_solve_linsys.h
new file mode 100644
index 0000000..d68a682
--- /dev/null
+++ b/tests/solve_linsys/test_solve_linsys.h
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include "osqp.h"
+#include "cs.h"
+#include "util.h"
+#include "osqp_tester.h"
+#include "lin_sys.h"
+
+
+#include "solve_linsys/data.h"
+
+
+void test_solveKKT() {
+  c_int m, exitflag = 0;
+  c_float *rho_vec;
+  LinSysSolver *s;  // Private structure to form KKT factorization
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings)); // Settings
+  solve_linsys_sols_data *data = generate_problem_solve_linsys_sols_data();
+
+  // Settings
+  settings->rho   = data->test_solve_KKT_rho;
+  settings->sigma = data->test_solve_KKT_sigma;
+
+  // Set rho_vec
+  m       = data->test_solve_KKT_A->m;
+  rho_vec = (c_float*) c_calloc(m, sizeof(c_float));
+  vec_add_scalar(rho_vec, settings->rho, m);
+
+  // Form and factorize KKT matrix
+  exitflag = init_linsys_solver(&s, data->test_solve_KKT_Pu, data->test_solve_KKT_A,
+                                settings->sigma, rho_vec, LINSYS_SOLVER, 0);
+
+  // Solve  KKT x = b via LDL given factorization
+  s->solve(s, data->test_solve_KKT_rhs);
+
+  mu_assert(
+    "Linear systems solve tests: error in forming and solving KKT system!",
+    vec_norm_inf_diff(data->test_solve_KKT_rhs, data->test_solve_KKT_x,
+                      data->test_solve_KKT_m + data->test_solve_KKT_n) < TESTS_TOL);
+
+
+  // Cleanup
+  s->free(s);
+  c_free(settings);
+  c_free(rho_vec);
+  clean_problem_solve_linsys_sols_data(data);
+}
+
+#ifdef ENABLE_MKL_PARDISO
+void test_solveKKT_pardiso() {
+  c_int m, exitflag = 0;
+  c_float *rho_vec;
+  LinSysSolver *s;  // Private  structure  to  form  KKT  factorization
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings)); // Settings
+
+  solve_linsys_sols_data *data = generate_problem_solve_linsys_sols_data();
+
+  // Settings
+  settings->rho   = data->test_solve_KKT_rho;
+  settings->sigma = data->test_solve_KKT_sigma;
+
+  // Set rho_vec
+  m = data->test_solve_KKT_A->m;
+  rho_vec = (c_float*)c_calloc(m, sizeof(c_float));
+  vec_add_scalar(rho_vec, settings->rho, m);
+
+  // Load Pardiso shared library
+  exitflag = load_linsys_solver(MKL_PARDISO_SOLVER);
+  mu_assert("Linear system solve test: error in loading Pardiso shared library",
+            exitflag == 0);
+
+  // Form and factorize KKT matrix
+  exitflag = init_linsys_solver(&s, data->test_solve_KKT_Pu, data->test_solve_KKT_A,
+                                settings->sigma, rho_vec, MKL_PARDISO_SOLVER, 0);
+
+  // Solve  KKT x = b via LDL given factorization
+  s->solve(s, data->test_solve_KKT_rhs);
+
+  mu_assert(
+    "Linear systems solve tests: error in forming and solving KKT system with PARDISO!",
+    vec_norm_inf_diff(data->test_solve_KKT_rhs, data->test_solve_KKT_x,
+                      data->test_solve_KKT_m + data->test_solve_KKT_n) < TESTS_TOL);
+
+
+  // Cleanup
+  s->free(s);
+  c_free(settings);
+  c_free(rho_vec);
+  clean_problem_solve_linsys_sols_data(data);
+
+  // Unload Pardiso shared library
+  exitflag = unload_linsys_solver(MKL_PARDISO_SOLVER);
+}
+#endif
\ No newline at end of file
diff --git a/tests/unconstrained/CMakeLists.txt b/tests/unconstrained/CMakeLists.txt
new file mode 100644
index 0000000..741b2c7
--- /dev/null
+++ b/tests/unconstrained/CMakeLists.txt
@@ -0,0 +1,13 @@
+get_directory_property(headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION headers)
+
+set(headers ${headers}
+${CMAKE_CURRENT_SOURCE_DIR}/test_unconstrained.h PARENT_SCOPE)
+
+get_directory_property(codegen_headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION codegen_headers)
+
+set(codegen_headers ${codegen_headers}
+        ${CMAKE_CURRENT_SOURCE_DIR}/data.h PARENT_SCOPE)
diff --git a/tests/unconstrained/__init__.py b/tests/unconstrained/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/unconstrained/__init__.py
diff --git a/tests/unconstrained/generate_problem.py b/tests/unconstrained/generate_problem.py
new file mode 100644
index 0000000..489f016
--- /dev/null
+++ b/tests/unconstrained/generate_problem.py
@@ -0,0 +1,17 @@
+import numpy as np
+from scipy import sparse
+import utils.codegen_utils as cu
+
+P = sparse.diags([0.617022, 0.92032449, 0.20011437, 0.50233257, 0.34675589], format='csc')
+q = np.array([-1.10593508, -1.65451545, -2.3634686, 1.13534535, -1.01701414])
+A = sparse.csc_matrix((0,5))
+l = np.array([])
+u = np.array([])
+
+# Generate problem solutions
+sols_data = {'x_test': np.array([1.79237542, 1.79775228, 11.81058885, -2.26014678, 2.93293975]),
+             'obj_value_test': -19.209752026813277,
+             'status_test': 'optimal'}
+
+# Generate problem data
+cu.generate_problem_data(P, q, A, l, u, 'unconstrained', sols_data)
\ No newline at end of file
diff --git a/tests/unconstrained/test_unconstrained.h b/tests/unconstrained/test_unconstrained.h
new file mode 100644
index 0000000..95b8de6
--- /dev/null
+++ b/tests/unconstrained/test_unconstrained.h
@@ -0,0 +1,60 @@
+#include "osqp.h"        // OSQP API
+#include "osqp_tester.h" // Basic testing script header
+
+
+#include "unconstrained/data.h"
+
+
+void test_unconstrained_solve()
+{
+  c_int exitflag;
+
+  // Problem settings
+  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+
+  // Structures
+  OSQPWorkspace *work; // Workspace
+  OSQPData *data;      // Data
+  unconstrained_sols_data *sols_data;
+
+
+  // Populate data
+  data = generate_problem_unconstrained();
+  sols_data = generate_problem_unconstrained_sols_data();
+
+
+  // Define Solver settings as default
+  osqp_set_default_settings(settings);
+  settings->verbose = 1;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, data, settings);
+
+  // Setup correct
+  mu_assert("Unconstrained test solve: Setup error!", exitflag == 0);
+
+  // Solve Problem first time
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Unconstrained test solve: Error in solver status!",
+            work->info->status_val == sols_data->status_test);
+
+  // Compare primal solutions
+  mu_assert("Unconstrained test solve: Error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, sols_data->x_test,
+                              data->n) < TESTS_TOL);
+
+  // Compare objective values
+  mu_assert("Unconstrained test solve: Error in objective value!",
+            c_absval(work->info->obj_val - sols_data->obj_value_test) <
+            TESTS_TOL);
+
+  // Clean workspace
+  osqp_cleanup(work);
+
+  // Cleanup settings and data
+  c_free(settings);
+  clean_problem_unconstrained(data);
+  clean_problem_unconstrained_sols_data(sols_data);
+}
\ No newline at end of file
diff --git a/tests/update_matrices/CMakeLists.txt b/tests/update_matrices/CMakeLists.txt
new file mode 100644
index 0000000..2d10bd8
--- /dev/null
+++ b/tests/update_matrices/CMakeLists.txt
@@ -0,0 +1,13 @@
+get_directory_property(headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION headers)
+
+set(headers ${headers}
+${CMAKE_CURRENT_SOURCE_DIR}/test_update_matrices.h PARENT_SCOPE)
+
+get_directory_property(codegen_headers
+                        DIRECTORY ${PROJECT_SOURCE_DIR}/tests
+                        DEFINITION codegen_headers)
+
+set(codegen_headers ${codegen_headers}
+        ${CMAKE_CURRENT_SOURCE_DIR}/data.h PARENT_SCOPE)
diff --git a/tests/update_matrices/__init__.py b/tests/update_matrices/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/update_matrices/__init__.py
diff --git a/tests/update_matrices/generate_problem.py b/tests/update_matrices/generate_problem.py
new file mode 100644
index 0000000..d801fc7
--- /dev/null
+++ b/tests/update_matrices/generate_problem.py
@@ -0,0 +1,109 @@
+import numpy as np
+from scipy import sparse
+import utils.codegen_utils as cu
+from numpy.random import Generator, PCG64
+
+# Set random seed for reproducibility
+rg = Generator(PCG64(2))
+
+# Define tests
+n = 5
+m = 8
+test_form_KKT_n = n
+test_form_KKT_m = m
+p = 0.7
+
+test_form_KKT_A = sparse.random(test_form_KKT_m, test_form_KKT_n, density=p, format='csc', random_state=rg)
+test_form_KKT_P = sparse.random(n, n, density=p, random_state=rg)
+test_form_KKT_P = (test_form_KKT_P @ test_form_KKT_P.T).tocsc() + sparse.eye(n, format='csc')
+test_form_KKT_Pu = sparse.triu(test_form_KKT_P, format='csc')
+test_form_KKT_rho = 1.6
+test_form_KKT_sigma = 0.1
+test_form_KKT_KKT = sparse.bmat([[test_form_KKT_P + test_form_KKT_sigma *
+                                  sparse.eye(test_form_KKT_n), test_form_KKT_A.T],
+                                 [test_form_KKT_A, -1./test_form_KKT_rho *
+                                  sparse.eye(test_form_KKT_m)]], format='csc')
+test_form_KKT_KKTu = sparse.triu(test_form_KKT_KKT, format='csc')
+
+
+# Create new P, A and KKT
+test_form_KKT_A_new = test_form_KKT_A.copy()
+test_form_KKT_A_new.data += rg.standard_normal(test_form_KKT_A_new.nnz)
+test_form_KKT_Pu_new = test_form_KKT_Pu.copy()
+test_form_KKT_Pu_new.data += 0.1 * rg.standard_normal(test_form_KKT_Pu_new.nnz)
+test_form_KKT_P_new = test_form_KKT_Pu_new + test_form_KKT_Pu_new.T - sparse.diags(test_form_KKT_Pu_new.diagonal())
+
+test_form_KKT_KKT_new = sparse.bmat([[test_form_KKT_P_new + test_form_KKT_sigma *
+                                      sparse.eye(test_form_KKT_n), test_form_KKT_A_new.T],
+                                     [test_form_KKT_A_new, -1./test_form_KKT_rho *
+                                      sparse.eye(test_form_KKT_m)]], format='csc')
+test_form_KKT_KKTu_new = sparse.triu(test_form_KKT_KKT_new, format='csc')
+
+
+# Test solve problem with initial P and A
+test_solve_P = test_form_KKT_P.copy()
+test_solve_Pu = test_form_KKT_Pu.copy()
+test_solve_q = rg.standard_normal(n)
+test_solve_A = test_form_KKT_A.copy()
+test_solve_l = -30 + rg.standard_normal(m)
+test_solve_u = 30 + rg.standard_normal(m)
+
+
+# Define new P
+test_solve_P_new = test_form_KKT_P_new.copy()
+test_solve_Pu_new = test_form_KKT_Pu_new.copy()
+
+
+# Define new A
+test_solve_A_new = test_form_KKT_A_new.copy()
+
+
+# Generate test data and solutions
+data = {'test_form_KKT_n': test_form_KKT_n,
+        'test_form_KKT_m': test_form_KKT_m,
+        'test_form_KKT_A': test_form_KKT_A,
+        'test_form_KKT_Pu': test_form_KKT_Pu,
+        'test_form_KKT_rho': test_form_KKT_rho,
+        'test_form_KKT_sigma': test_form_KKT_sigma,
+        'test_form_KKT_KKT': test_form_KKT_KKT,
+        'test_form_KKT_KKTu': test_form_KKT_KKTu,
+        'test_form_KKT_A_new': test_form_KKT_A_new,
+        'test_form_KKT_Pu_new': test_form_KKT_Pu_new,
+        'test_form_KKT_KKT_new': test_form_KKT_KKT_new,
+        'test_form_KKT_KKTu_new': test_form_KKT_KKTu_new,
+        'test_solve_Pu': test_solve_Pu,
+        'test_solve_q': test_solve_q,
+        'test_solve_A': test_solve_A,
+        'test_solve_l': test_solve_l,
+        'test_solve_u': test_solve_u,
+        'n': n,
+        'm': m,
+        'test_solve_x': np.array([-4.61725223e-01, 7.97298788e-01,
+                                  5.55470173e-04,  3.37603740e-01,
+                                  -1.14060693e+00]),
+        'test_solve_y': np.zeros(m),
+        'test_solve_obj_value': -1.885431747787806,
+        'test_solve_status': 'optimal',
+        'test_solve_Pu_new': test_solve_Pu_new,
+        'test_solve_P_new_x': np.array([-0.48845963, 0.70997599, -0.09017696,
+                                        0.33176037, -1.01867464]),
+        'test_solve_P_new_y': np.zeros(m),
+        'test_solve_P_new_obj_value': -1.7649689689774013,
+        'test_solve_P_new_status': 'optimal',
+        'test_solve_A_new': test_solve_A_new,
+        'test_solve_A_new_x': np.array([-4.61725223e-01, 7.97298788e-01,
+                                        5.55470173e-04, 3.37603740e-01,
+                                        -1.14060693e+00]),
+        'test_solve_A_new_y': np.zeros(m),
+        'test_solve_A_new_obj_value': -1.8854317477878062,
+        'test_solve_A_new_status': 'optimal',
+        'test_solve_P_A_new_x': np.array([-0.48845963, 0.70997599, -0.09017696,
+                                          0.33176037, -1.01867464]),
+        'test_solve_P_A_new_y': np.zeros(m),
+        'test_solve_P_A_new_obj_value': -1.764968968977401,
+        'test_solve_P_A_new_status': 'optimal'
+        }
+
+
+# Generate test data
+cu.generate_data('update_matrices', data)
diff --git a/tests/update_matrices/test_update_matrices.h b/tests/update_matrices/test_update_matrices.h
new file mode 100644
index 0000000..bb347a6
--- /dev/null
+++ b/tests/update_matrices/test_update_matrices.h
@@ -0,0 +1,471 @@
+#include <stdio.h>
+#include "osqp.h"
+#include "cs.h"
+#include "util.h"
+#include "osqp_tester.h"
+#include "kkt.h"
+#include "lin_sys.h"
+
+
+#include "update_matrices/data.h"
+
+
+void test_form_KKT() {
+  update_matrices_sols_data *data;
+  c_float sigma, *rho_vec, *rho_inv_vec;
+  c_int   m, *PtoKKT, *AtoKKT, *Pdiag_idx, Pdiag_n;
+  csc    *KKT;
+
+  // Load problem data
+  data = generate_problem_update_matrices_sols_data();
+
+  // Define rho_vec and sigma to form KKT
+  sigma       = data->test_form_KKT_sigma;
+  m           = data->test_form_KKT_A->m;
+  rho_vec     = (c_float*) c_calloc(m, sizeof(c_float));
+  rho_inv_vec = (c_float*) c_calloc(m, sizeof(c_float));
+  vec_add_scalar(rho_vec, data->test_form_KKT_rho, m);
+  vec_ew_recipr(rho_vec, rho_inv_vec, m);
+
+  // Allocate vectors of indices
+  PtoKKT = (c_int*) c_malloc((data->test_form_KKT_Pu->p[data->test_form_KKT_Pu->n]) *
+                    sizeof(c_int));
+  AtoKKT = (c_int*) c_malloc((data->test_form_KKT_A->p[data->test_form_KKT_A->n]) *
+                    sizeof(c_int));
+
+  // Form KKT matrix storing the index vectors
+  KKT = form_KKT(data->test_form_KKT_Pu,
+                 data->test_form_KKT_A,
+                 0,
+                 sigma,
+                 rho_inv_vec,
+                 PtoKKT,
+                 AtoKKT,
+                 &Pdiag_idx,
+                 &Pdiag_n,
+                 OSQP_NULL);
+
+  // Assert if KKT matrix is the same as predicted one
+  mu_assert("Update matrices: error in forming KKT matrix!",
+            is_eq_csc(KKT, data->test_form_KKT_KKTu, TESTS_TOL));
+
+  // Update KKT matrix with new P and new A
+  update_KKT_P(KKT, data->test_form_KKT_Pu_new, PtoKKT, sigma, Pdiag_idx,
+               Pdiag_n);
+  update_KKT_A(KKT, data->test_form_KKT_A_new, AtoKKT);
+
+
+  // Assert if KKT matrix is the same as predicted one
+  mu_assert("Update matrices: error in updating KKT matrix!",
+            is_eq_csc(KKT, data->test_form_KKT_KKTu_new, TESTS_TOL));
+
+
+  // Cleanup
+  clean_problem_update_matrices_sols_data(data);
+  c_free(Pdiag_idx);
+  csc_spfree(KKT);
+  c_free(rho_vec);
+  c_free(rho_inv_vec);
+  c_free(AtoKKT);
+  c_free(PtoKKT);
+}
+
+void test_update() {
+  c_int i, nnzP, nnzA;
+  update_matrices_sols_data *data;
+  OSQPData *problem;
+  OSQPWorkspace *work;
+  OSQPSettings  *settings;
+  c_int exitflag;
+
+  // Update matrix P
+  c_int *Px_new_idx;
+
+  // Update matrix A
+  c_int *Ax_new_idx;
+
+  // Load problem data
+  data = generate_problem_update_matrices_sols_data();
+
+  // Generate first problem data
+  problem    = (OSQPData*) c_malloc(sizeof(OSQPData));
+  problem->P = data->test_solve_Pu;
+  problem->q = data->test_solve_q;
+  problem->A = data->test_solve_A;
+  problem->l = data->test_solve_l;
+  problem->u = data->test_solve_u;
+  problem->n = data->test_solve_Pu->n;
+  problem->m = data->test_solve_A->m;
+
+
+  // Define Solver settings as default
+  // Problem settings
+  settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+  osqp_set_default_settings(settings);
+  settings->max_iter = 1000;
+  settings->alpha    = 1.6;
+  settings->verbose  = 1;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, problem, settings);
+
+  // Setup correct
+  mu_assert("Update matrices: original problem, setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Update matrices: original problem, error in solver status!",
+            work->info->status_val == data->test_solve_status);
+
+  // Compare primal solutions
+  mu_assert("Update matrices: original problem, error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, data->test_solve_x,
+                              data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Update matrices: original problem, error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, data->test_solve_y,
+                              data->m) < TESTS_TOL);
+
+
+  // Update P
+  nnzP       = data->test_solve_Pu->p[data->test_solve_Pu->n];
+  Px_new_idx = (c_int*) c_malloc(nnzP * sizeof(c_int));
+
+  // Generate indices going from beginning to end of P
+  for (i = 0; i < nnzP; i++) {
+    Px_new_idx[i] = i;
+  }
+
+  osqp_update_P(work, data->test_solve_Pu_new->x, Px_new_idx, nnzP);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Update matrices: problem with updating P, error in solver status!",
+            work->info->status_val == data->test_solve_P_new_status);
+
+  // Compare primal solutions
+  mu_assert("Update matrices: problem with updating P, error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, data->test_solve_P_new_x,
+                              data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Update matrices: problem with updating P, error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, data->test_solve_P_new_y,
+                              data->m) < TESTS_TOL);
+
+  // Cleanup and setup workspace
+  osqp_cleanup(work);
+  exitflag = osqp_setup(&work, problem, settings);
+
+
+  // Update P (all indices)
+  osqp_update_P(work, data->test_solve_Pu_new->x, OSQP_NULL, nnzP);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Update matrices: problem with updating P (all indices), error in solver status!",
+            work->info->status_val == data->test_solve_P_new_status);
+
+  // Compare primal solutions
+  mu_assert("Update matrices: problem with updating P (all indices), error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, data->test_solve_P_new_x,
+                              data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Update matrices: problem with updating P (all indices), error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, data->test_solve_P_new_y,
+                              data->m) < TESTS_TOL);
+
+  // Cleanup and setup workspace
+  osqp_cleanup(work);
+  exitflag = osqp_setup(&work, problem, settings);
+
+
+  // Update A
+  nnzA       = data->test_solve_A->p[data->test_solve_A->n];
+  Ax_new_idx = (c_int*) c_malloc(nnzA * sizeof(c_int));
+
+  // Generate indices going from beginning to end of A
+  for (i = 0; i < nnzA; i++) {
+    Ax_new_idx[i] = i;
+  }
+
+  osqp_update_A(work, data->test_solve_A_new->x, Ax_new_idx, nnzA);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Update matrices: problem with updating A, error in solver status!",
+            work->info->status_val == data->test_solve_A_new_status);
+
+  // Compare primal solutions
+  mu_assert("Update matrices: problem with updating A, error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, data->test_solve_A_new_x,
+                              data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Update matrices: problem with updating A, error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, data->test_solve_A_new_y,
+                              data->m) < TESTS_TOL);
+
+  // Cleanup and setup workspace
+  osqp_cleanup(work);
+  exitflag = osqp_setup(&work, problem, settings);
+
+
+  // Update A (all indices)
+  osqp_update_A(work, data->test_solve_A_new->x, OSQP_NULL, nnzA);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Update matrices: problem with updating A (all indices), error in solver status!",
+            work->info->status_val == data->test_solve_A_new_status);
+
+  // Compare primal solutions
+  mu_assert("Update matrices: problem with updating A (all indices), error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, data->test_solve_A_new_x,
+                              data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Update matrices: problem with updating A (all indices), error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, data->test_solve_A_new_y,
+                              data->m) < TESTS_TOL);
+
+
+  // Cleanup and setup workspace
+  osqp_cleanup(work);
+  exitflag = osqp_setup(&work, problem, settings);
+
+  // Update P and A
+  osqp_update_P_A(work, data->test_solve_Pu_new->x, Px_new_idx, nnzP,
+                  data->test_solve_A_new->x, Ax_new_idx, nnzA);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert(
+    "Update matrices: problem with updating P and A, error in solver status!",
+    work->info->status_val == data->test_solve_P_A_new_status);
+
+  // Compare primal solutions
+  mu_assert(
+    "Update matrices: problem with updating P and A, error in primal solution!",
+    vec_norm_inf_diff(work->solution->x, data->test_solve_P_A_new_x,
+                      data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert(
+    "Update matrices: problem with updating P and A, error in dual solution!",
+    vec_norm_inf_diff(work->solution->y, data->test_solve_P_A_new_y,
+                      data->m) < TESTS_TOL);
+
+  // Cleanup and setup workspace
+  osqp_cleanup(work);
+  exitflag = osqp_setup(&work, problem, settings);
+
+
+  // Update P and A (all indices)
+  osqp_update_P_A(work, data->test_solve_Pu_new->x, OSQP_NULL, nnzP,
+                  data->test_solve_A_new->x, OSQP_NULL, nnzA);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert(
+    "Update matrices: problem with updating P and A (all indices), error in solver status!",
+    work->info->status_val == data->test_solve_P_A_new_status);
+
+  // Compare primal solutions
+  mu_assert(
+    "Update matrices: problem with updating P and A (all indices), error in primal solution!",
+    vec_norm_inf_diff(work->solution->x, data->test_solve_P_A_new_x,
+                      data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert(
+    "Update matrices: problem with updating P and A (all indices), error in dual solution!",
+    vec_norm_inf_diff(work->solution->y, data->test_solve_P_A_new_y,
+                      data->m) < TESTS_TOL);
+
+
+  // Cleanup problems
+  osqp_cleanup(work);
+  clean_problem_update_matrices_sols_data(data);
+  c_free(problem);
+  c_free(settings);
+  c_free(Ax_new_idx);
+  c_free(Px_new_idx);
+}
+
+#ifdef ENABLE_MKL_PARDISO
+void test_update_pardiso() {
+  c_int i, nnzP, nnzA, exitflag;
+  update_matrices_sols_data *data;
+  OSQPData *problem;
+  OSQPWorkspace *work;
+  OSQPSettings  *settings;
+
+  // Update matrix P
+  c_int *Px_new_idx;
+
+  // Update matrix A
+  c_int *Ax_new_idx;
+
+  // Load problem data
+  data = generate_problem_update_matrices_sols_data();
+
+  // Generate first problem data
+  problem    = (OSQPData*)c_malloc(sizeof(OSQPData));
+  problem->P = data->test_solve_Pu;
+  problem->q = data->test_solve_q;
+  problem->A = data->test_solve_A;
+  problem->l = data->test_solve_l;
+  problem->u = data->test_solve_u;
+  problem->n = data->test_solve_Pu->n;
+  problem->m = data->test_solve_A->m;
+
+
+  // Define Solver settings as default
+  // Problem settings
+  settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
+  osqp_set_default_settings(settings);
+  settings->max_iter      = 1000;
+  settings->alpha         = 1.6;
+  settings->verbose       = 1;
+  settings->linsys_solver = MKL_PARDISO_SOLVER;
+
+  // Setup workspace
+  exitflag = osqp_setup(&work, problem, settings);
+
+  // Setup correct
+  mu_assert("Update matrices: original problem, setup error!", exitflag == 0);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Update matrices: original problem, error in solver status!",
+            work->info->status_val == data->test_solve_status);
+
+  // Compare primal solutions
+  mu_assert("Update matrices: original problem, error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, data->test_solve_x,
+                              data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Update matrices: original problem, error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, data->test_solve_y,
+                              data->m) < TESTS_TOL);
+
+
+  // Update P
+  nnzP       = data->test_solve_Pu->p[data->test_solve_Pu->n];
+  Px_new_idx = (c_int*)c_malloc(nnzP * sizeof(c_int)); // Generate indices going from
+                                               // beginning to end of P
+
+  for (i = 0; i < nnzP; i++) {
+    Px_new_idx[i] = i;
+  }
+
+  osqp_update_P(work, data->test_solve_Pu_new->x, Px_new_idx, nnzP);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Update matrices: problem with P updated, error in solver status!",
+            work->info->status_val == data->test_solve_P_new_status);
+
+  // Compare primal solutions
+  mu_assert("Update matrices: problem with P updated, error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, data->test_solve_P_new_x,
+                              data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Update matrices: problem with P updated, error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, data->test_solve_P_new_y,
+                              data->m) < TESTS_TOL);
+
+
+  // Update A
+  nnzA       = data->test_solve_A->p[data->test_solve_A->n];
+  Ax_new_idx = (c_int*)c_malloc(nnzA * sizeof(c_int)); // Generate indices going from
+                                               // beginning to end of P
+
+  for (i = 0; i < nnzA; i++) {
+    Ax_new_idx[i] = i;
+  }
+
+  // Cleanup and setup workspace
+  osqp_cleanup(work);
+  exitflag = osqp_setup(&work, problem, settings);
+
+  osqp_update_A(work, data->test_solve_A_new->x, Ax_new_idx, nnzA);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert("Update matrices: problem with A updated, error in solver status!",
+            work->info->status_val == data->test_solve_A_new_status);
+
+  // Compare primal solutions
+  mu_assert("Update matrices: problem with A updated, error in primal solution!",
+            vec_norm_inf_diff(work->solution->x, data->test_solve_A_new_x,
+                              data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert("Update matrices: problem with A updated, error in dual solution!",
+            vec_norm_inf_diff(work->solution->y, data->test_solve_A_new_y,
+                              data->m) < TESTS_TOL);
+
+
+  // Cleanup and setup workspace
+  osqp_cleanup(work);
+  exitflag = osqp_setup(&work, problem, settings);
+
+  osqp_update_P_A(work, data->test_solve_Pu_new->x, Px_new_idx, nnzP,
+                  data->test_solve_A_new->x, Ax_new_idx, nnzA);
+
+  // Solve Problem
+  osqp_solve(work);
+
+  // Compare solver statuses
+  mu_assert(
+    "Update matrices: problem with P and A updated, error in solver status!",
+    work->info->status_val == data->test_solve_P_A_new_status);
+
+  // Compare primal solutions
+  mu_assert(
+    "Update matrices: problem with P and A updated, error in primal solution!",
+    vec_norm_inf_diff(work->solution->x, data->test_solve_P_A_new_x,
+                      data->n) < TESTS_TOL);
+
+  // Compare dual solutions
+  mu_assert(
+    "Update matrices: problem with P and A updated, error in dual solution!",
+    vec_norm_inf_diff(work->solution->y, data->test_solve_P_A_new_y,
+                      data->m) < TESTS_TOL);
+
+
+  // Cleanup problems
+  osqp_cleanup(work);
+  clean_problem_update_matrices_sols_data(data);
+  c_free(problem);
+  c_free(settings);
+  c_free(Ax_new_idx);
+  c_free(Px_new_idx);
+}
+#endif
\ No newline at end of file
diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/utils/__init__.py
diff --git a/tests/utils/codegen_utils.py b/tests/utils/codegen_utils.py
new file mode 100644
index 0000000..cdf400e
--- /dev/null
+++ b/tests/utils/codegen_utils.py
@@ -0,0 +1,461 @@
+# Compatibility with Python 2
+from __future__ import print_function
+
+from scipy import sparse
+import numpy as np
+
+
+def write_int(f, x, name, *args):
+    if any(args):
+        for arg in args:
+            f.write("%s->" % arg)
+        f.write("%s = %i;\n" % (name, x))
+    else:
+        f.write("c_int %s = %i;\n" % (name, x))
+
+
+def write_float(f, x, name, *args):
+    if any(args):
+        for arg in args:
+            f.write("%s->" % arg)
+        f.write("%s = %.20f;\n" % (name, x))
+    else:
+        f.write("c_float %s = %.20f;\n" % (name, x))
+
+
+def write_vec_int(f, x, name, *args):
+    n = len(x)
+    if any(args):
+        for arg in args:
+            f.write("%s->" % arg)
+    else:
+        f.write("c_int * ")
+    f.write("%s = (c_int*) c_malloc(%i * sizeof(c_int));\n" % (name, n))
+
+    for i in range(n):
+        for arg in args:
+            f.write("%s->" % arg)
+        f.write("%s[%i] = " % (name, i))
+        f.write("%i;\n" % x[i])
+
+
+def write_vec_float(f, x, name, *args):
+    n = len(x)
+    if any(args):
+        for arg in args:
+            f.write("%s->" % arg)
+    else:
+        f.write("c_float * ")
+    f.write("%s = (c_float*) c_malloc(%i * sizeof(c_float));\n" % (name, n))
+
+    for i in range(n):
+        for arg in args:
+            f.write("%s->" % arg)
+        f.write("%s[%i] = " % (name, i))
+        if x[i] == np.inf:
+            f.write("OSQP_INFTY;\n")
+        elif x[i] == -np.inf:
+            f.write("-OSQP_INFTY;\n")
+        else:
+            f.write("%.20f;\n" % x[i])
+
+
+def clean_vec(f, name, *args):
+    f.write("c_free(")
+    if any(args):
+        for arg in args:
+            f.write("%s->" % arg)
+    # else:
+        # f.write("c_float * ")
+    f.write("%s);\n" % name)
+
+
+def write_mat_sparse(f, A, name, *args):
+    m = A.shape[0]
+    n = A.shape[1]
+
+    f.write("\n// Matrix " + name + "\n")
+    f.write("//")
+    f.write("-"*(len("Matrix  ") + len(name)) + "\n")
+
+    # Allocate Matrix
+    if any(args):
+        for arg in args:
+            f.write("%s->" % arg)
+    else:
+        f.write("csc * ")
+    f.write(name + " = (csc*) c_malloc(sizeof(csc));\n")
+
+    # Write dimensions and number of nonzeros
+    if any(args):
+        write_int(f, m, "m", args, name)
+        write_int(f, n, "n", args, name)
+        write_int(f, -1, "nz", args, name)
+        write_int(f, A.nnz, "nzmax", args, name)
+    else:
+        write_int(f, m, "m", name)
+        write_int(f, n, "n", name)
+        write_int(f, -1, "nz", name)
+        write_int(f, A.nnz, "nzmax", name)
+
+    for arg in args:
+        f.write("%s->" % arg)
+    if min(m,n) == 0:
+        f.write("%s->x = OSQP_NULL;\n" % name)
+    else:
+        f.write("%s->" % name)
+        f.write("x = (c_float*) c_malloc(%i * sizeof(c_float));\n" % A.nnz)
+        for i in range(A.nnz):
+            for arg in args:
+                f.write("%s->" % arg)
+            f.write("%s->" % name)
+            f.write("x[%i] = %.20f;\n" % (i, A.data[i]))
+
+    for arg in args:
+        f.write("%s->" % arg)
+    if min(m,n) == 0:
+        f.write("%s->i = OSQP_NULL;\n" % name)
+    else:
+        f.write("%s->" % name)
+        f.write("i = (c_int*) c_malloc(%i * sizeof(c_int));\n" % A.nnz)
+        for i in range(A.nnz):
+            for arg in args:
+                f.write("%s->" % arg)
+            f.write("%s->" % name)
+            f.write("i[%i] = %i;\n" % (i, A.indices[i]))
+
+    for arg in args:
+        f.write("%s->" % arg)
+    f.write("%s->" % name)
+    f.write("p = (c_int*) c_malloc((%i + 1) * sizeof(c_int));\n" % n)
+    for i in range(A.shape[1] + 1):
+        for arg in args:
+            f.write("%s->" % arg)
+        f.write("%s->" % name)
+        f.write("p[%i] = %i;\n" % (i, A.indptr[i]))
+
+    # Do the same for i and p
+    f.write("\n")
+
+
+def clean_mat(f, name, *args):
+
+    # Clean data vector
+    f.write("c_free(")
+    if any(args):
+        for arg in args:
+            f.write("%s->" % arg)
+    f.write("%s->x);\n" % name)
+
+    # Clean index vector
+    f.write("c_free(")
+    if any(args):
+        for arg in args:
+            f.write("%s->" % arg)
+    f.write("%s->i);\n" % name)
+
+    # Clean col pointer vector
+    f.write("c_free(")
+    if any(args):
+        for arg in args:
+            f.write("%s->" % arg)
+    f.write("%s->p);\n" % name)
+
+    # Clean matrix
+    f.write("c_free(")
+    if any(args):
+        for arg in args:
+            f.write("%s->" % arg)
+    f.write("%s);\n" % name)
+
+
+def generate_problem_data(P, q, A, l, u, problem_name, sols_data={}):
+    """
+    Generate test problem data.
+
+    The additional structure sols_data defines the additional vectors/scalars
+    we need to perform the tests
+    """
+    # Get problem dimension
+    n = P.shape[0]
+    m = A.shape[0]
+
+    #
+    # GENERATE HEADER FILE
+    #
+    f = open(problem_name + "/data.h", "w")
+
+    # Add definition check
+    f.write("#ifndef " + problem_name.upper() + "_DATA_H\n")
+    f.write("#define " + problem_name.upper() + "_DATA_H\n")
+
+    # Add Includes
+    f.write("#include \"osqp.h\"\n")
+    f.write("\n\n")
+
+    #
+    # Create additional data structure
+    #
+    f.write("/* create additional data and solutions structure */\n")
+    f.write("typedef struct {\n")
+    # Generate further data and solutions
+    for key, value in sols_data.items():
+        if isinstance(value, str):
+            # Status test get from C code
+            f.write("c_int %s;\n" % key)
+        # Check if it is an array or a scalar
+        elif isinstance(value, np.ndarray):
+            if isinstance(value.flatten(order='F')[0], int):
+                f.write("c_int * %s;\n" % key)
+            elif isinstance(value.flatten(order='F')[0], float):
+                f.write("c_float * %s;\n" % key)
+        else:
+            if isinstance(value, int):
+                f.write("c_int %s;\n" % key)
+            elif isinstance(value, float):
+                f.write("c_float %s;\n" % key)
+    f.write("} %s_sols_data;\n\n" % problem_name)
+
+    # prototypes
+    f.write("/* function prototypes */\n")
+    f.write("OSQPData * generate_problem_%s();\n" % problem_name)
+    f.write("void clean_problem_%s(OSQPData * data);\n" % problem_name)
+    f.write("%s_sols_data *  generate_problem_%s_sols_data();\n" % (problem_name, problem_name))
+    f.write("void clean_problem_%s_sols_data(%s_sols_data * data);\n" % (problem_name, problem_name))
+    f.write("\n\n")
+
+    #
+    # Generate QP problem data
+    #
+    f.write("/* function to generate QP problem data */\n")
+    f.write("OSQPData * generate_problem_%s(){\n\n" % problem_name)
+
+    # Initialize structure data
+    f.write("OSQPData * data = (OSQPData *)c_malloc(sizeof(OSQPData));\n\n")
+
+    # Write problem dimensions
+    f.write("// Problem dimensions\n")
+    write_int(f, n, "n", "data")
+    write_int(f, m, "m", "data")
+    f.write("\n")
+
+    # Write problem vectors
+    f.write("// Problem vectors\n")
+    write_vec_float(f, l, "l", "data")
+    write_vec_float(f, u, "u", "data")
+    write_vec_float(f, q, "q", "data")
+    f.write("\n")
+
+    # Write matrix A
+    write_mat_sparse(f, A, "A", "data")
+    write_mat_sparse(f, P, "P", "data")
+
+    # Return data and end function
+    f.write("return data;\n\n")
+
+    f.write("}\n\n")
+
+
+    #
+    # Generate QP problem data
+    #
+    f.write("/* function to clean problem data structure */\n")
+    f.write("void clean_problem_%s(OSQPData * data){\n\n" % problem_name)
+
+    # Free vectors
+    f.write("// Clean vectors\n")
+    clean_vec(f, "l", "data")
+    clean_vec(f, "u", "data")
+    clean_vec(f, "q", "data")
+    f.write("\n")
+
+    # Free matrices
+    f.write("//Clean Matrices\n")
+    clean_mat(f, "A", "data")
+    clean_mat(f, "P", "data")
+    f.write("\n")
+
+    f.write("c_free(data);\n\n")
+
+    f.write("}\n\n")
+
+
+
+    #
+    # Generate additional problem data for solutions
+    #
+    f.write("/* function to define solutions and additional data struct */\n")
+    f.write("%s_sols_data *  generate_problem_%s_sols_data(){\n\n" % (problem_name, problem_name))
+
+    # Initialize structure data
+    f.write("%s_sols_data * data = (%s_sols_data *)c_malloc(sizeof(%s_sols_data));\n\n" % (problem_name, problem_name, problem_name))
+
+
+    # Generate further data and solutions
+    for key, value in sols_data.items():
+        if isinstance(value, str):
+            # Status test get from C code
+            if value == 'optimal':
+                f.write("data->%s = %s;\n" % (key, 'OSQP_SOLVED'))
+            elif value == 'optimal_inaccurate':
+                f.write("data->%s = %s;\n" % (key, 'OSQP_SOLVED_INACCURATE'))
+            elif value == 'primal_infeasible':
+                f.write("data->%s = %s;\n" % (key, 'OSQP_PRIMAL_INFEASIBLE'))
+            elif value == 'primal_infeasible_inaccurate':
+                f.write("data->%s = %s;\n" %
+                        (key, 'OSQP_PRIMAL_INFEASIBLE_INACCURATE'))
+            elif value == 'dual_infeasible':
+                f.write("data->%s = %s;\n" % (key, 'OSQP_DUAL_INFEASIBLE'))
+            elif value == 'dual_infeasible_inaccurate':
+                f.write("data->%s = %s;\n" % (key, 'OSQP_DUAL_INFEASIBLE_INACCURATE'))
+
+        # Check if it is an array or a scalar
+        if type(value) is np.ndarray:
+            if isinstance(value.flatten(order='F')[0], int):
+                write_vec_int(f, value.flatten(order='F'), key, "data")
+            elif isinstance(value.flatten(order='F')[0], float):
+                write_vec_float(f, value.flatten(order='F'), key, "data")
+        else:
+            if isinstance(value, int):
+                write_int(f, value, key, "data")
+            elif isinstance(value, float):
+                write_float(f, value, key, "data")
+
+    # Return data and end function
+    f.write("\nreturn data;\n\n")
+
+    f.write("}\n\n")
+
+
+
+    #
+    # Clean additional problem data for solutions
+    #
+    f.write("/* function to clean solutions and additional data struct */\n")
+    f.write("void clean_problem_%s_sols_data(%s_sols_data * data){\n\n" % (problem_name, problem_name))
+    # Generate further data and solutions
+    for key, value in sols_data.items():
+        # Check if it is an array or a scalar
+        if type(value) is np.ndarray:
+            clean_vec(f, key, "data")
+
+    f.write("\nc_free(data);\n\n")
+
+    f.write("}\n\n")
+
+    f.write("#endif\n")
+
+    f.close()
+
+
+def generate_data(problem_name, sols_data):
+    """
+    Generate test data vectors.
+
+    The additional structure sols_data defines the additional vectors/scalars
+    we need to perform the tests
+    """
+
+    #
+    # GENERATE HEADER FILE
+    #
+    f = open(problem_name + "/data.h", "w")
+
+    # Add definition check
+    f.write("#ifndef " + problem_name.upper() + "_DATA_H\n")
+    f.write("#define " + problem_name.upper() + "_DATA_H\n")
+
+    # Add Includes
+    f.write("#include \"osqp.h\"\n")
+    f.write("\n\n")
+
+    #
+    # Create additional data structure
+    #
+    f.write("/* create data and solutions structure */\n")
+    f.write("typedef struct {\n")
+    # Generate further data and solutions
+    for key, value in sols_data.items():
+        if isinstance(value, str):
+            # Status test get from C code
+            f.write("c_int %s;\n" % key)
+        # Check if it is an array or a scalar
+        elif sparse.issparse(value):  # Sparse matrix
+            f.write("csc * %s;\n" % key)
+        elif isinstance(value, np.ndarray):
+            if isinstance(value.flatten(order='F')[0], int):
+                f.write("c_int * %s;\n" % key)
+            elif isinstance(value.flatten(order='F')[0], float):
+                f.write("c_float * %s;\n" % key)
+        else:
+            if isinstance(value, int):
+                f.write("c_int %s;\n" % key)
+            elif isinstance(value, float):
+                f.write("c_float %s;\n" % key)
+    f.write("} %s_sols_data;\n\n" % problem_name)
+
+    # prototypes
+    f.write("/* function prototypes */\n")
+    f.write("%s_sols_data *  generate_problem_%s_sols_data();\n" % (problem_name, problem_name))
+    f.write("void clean_problem_%s_sols_data(%s_sols_data * data);\n" % (problem_name, problem_name))
+    f.write("\n\n")
+
+    #
+    # Generate additional problem data for solutions
+    #
+    f.write("/* function to define problem data */\n")
+    f.write("%s_sols_data *  generate_problem_%s_sols_data(){\n\n" % (problem_name, problem_name))
+
+    # Initialize structure data
+    f.write("%s_sols_data * data = (%s_sols_data *)c_malloc(sizeof(%s_sols_data));\n\n" % (problem_name, problem_name, problem_name))
+
+    # Generate further data and solutions
+    for key, value in sols_data.items():
+        if isinstance(value, str):
+            # Status test get from C code
+            if value == 'optimal':
+                f.write("data->%s = %s;\n" % (key, 'OSQP_SOLVED'))
+            elif value == 'primal_infeasible':
+                f.write("data->%s = %s;\n" % (key, 'OSQP_PRIMAL_INFEASIBLE'))
+            elif value == 'dual_infeasible':
+                f.write("data->%s = %s;\n" % (key, 'OSQP_DUAL_INFEASIBLE'))
+        # Check if it is an array or a scalar
+        elif sparse.issparse(value):  # Sparse matrix
+            write_mat_sparse(f, value, key, "data")
+        elif type(value) is np.ndarray:
+            if isinstance(value.flatten(order='F')[0], int):
+                write_vec_int(f, value.flatten(order='F'), key, "data")
+            elif isinstance(value.flatten(order='F')[0], float):
+                write_vec_float(f, value.flatten(order='F'), key, "data")
+        else:
+            if isinstance(value, int):
+                write_int(f, value, key, "data")
+            elif isinstance(value, float):
+                write_float(f, value, key, "data")
+
+    # Return data and end function
+    f.write("\nreturn data;\n\n")
+
+    f.write("}\n\n")
+
+
+    #
+    # Clean  data
+    #
+    f.write("/* function to clean data struct */\n")
+    f.write("void clean_problem_%s_sols_data(%s_sols_data * data){\n\n" % (problem_name, problem_name))
+    # Generate further data and solutions
+    for key, value in sols_data.items():
+        # Check if it is an array or a scalar
+        if sparse.issparse(value):  # Sparse matrix
+            clean_mat(f, key, "data")
+        elif type(value) is np.ndarray:
+            clean_vec(f, key, "data")
+
+    f.write("\nc_free(data);\n\n")
+
+    f.write("}\n\n")
+
+    f.write("#endif\n")
+
+    f.close()