diff --git a/README.md b/README.md
index 41ed579a1b69ff17d8ceb4830e8bb045804c81ba..57c0823ad7d3ad70cf28da7da5371f2141163ff1 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,15 @@ Detector benchmarks are meant to test for regressions in individual detector sub
 The analysis is meant to avoid a reconstruction step. 
 So this precludes using [juggler](https://eicweb.phy.anl.gov/EIC/juggler) for processing the events.
 
+## Documentation
+
+See [common_bench](https://eicweb.phy.anl.gov/EIC/benchmarks/common_bench/).
+
 ## Adding new benchmarks
 
+See `benchmarks` directory for examples.
+
+
 ### Pass/Fail tests
 
 - Create a script that returns exit status 0 for success.
diff --git a/bin/gen_ci_config b/bin/gen_ci_config
deleted file mode 100755
index debe668fca95cc5027e57ae4e7197cb97d71e9bd..0000000000000000000000000000000000000000
--- a/bin/gen_ci_config
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/bin/bash
-set -o nounset
-set -o errexit
-
-CI_TAG=sodium
-BENCHMARK_SCRIPT_DIR=.
-CI_JOB_PREFIX=cal_test_
-
-function print_the_help {
-  echo "USAGE: $0 [-t <runner_tag>] "
-  echo "  OPTIONS: "
-  echo "            -i,--input         Input scripts directory "
-  echo "            -t,--tag           Gitlab Runner tag"
-  echo "            -p,--prefix        job name prefix"
-  exit 
-}
-
-POSITIONAL=()
-while [[ $# -gt 0 ]]
-do
-  key="$1"
-  case $key in
-    -h|--help)
-      shift # past argument
-      print_the_help
-      ;;
-    -i|--input)
-      BENCHMARK_SCRIPT_DIR="$2"
-      shift # past argument
-      shift # past value
-      ;;
-    -t|--tag)
-      CI_TAG="$2"
-      shift # past argument
-      shift # past value
-      ;;
-    -p|--prefix)
-      CI_JOB_PREFIX="$2"
-      shift # past argument
-      shift # past value
-      ;;
-    *)    # unknown option
-      #POSITIONAL+=("$1") # save it in an array for later
-      echo "unknown option $1"
-      print_the_help
-      shift # past argument
-      ;;
-  esac
-done
-set -- "${POSITIONAL[@]}" # restore positional parameters
-
-shopt -s nullglob
-ifile=0
-
-
-cat <<EOF 
-stages:
-  #- simulate
-  - benchmarks
-EOF
-
-for script_name in ${BENCHMARK_SCRIPT_DIR}/*.sh
-do
-  filename=$(basename ${script_name})
-  filename_noext="${filename%%.*}"
-  ifile=$((ifile+1))
-  cat <<EOF 
-${CI_JOB_PREFIX}${ifile}_${filename_noext}:
-  stage: benchmarks
-  script:
-    - bash ${script_name}
-  artifact:
-    paths: 
-      - results/
-  allow_failure: true
-
-EOF
-done
-
-for script_name in ${BENCHMARK_SCRIPT_DIR}/*.cxx
-do
-  filename=$(basename ${script_name})
-  filename_noext="${filename%%.*}"
-  ifile=$((ifile+1))
-  cat <<EOF 
-${CI_JOB_PREFIX}${ifile}_${filename_noext}:
-  stage: benchmarks
-  script:
-    - root -b -q ${script_name}
-  artifact:
-    paths: 
-      - results/
-  allow_failure: true
-
-EOF
-done
-        
diff --git a/include/benchmark.h b/include/benchmark.h
deleted file mode 100644
index 0634be0a1494739c4e7337f99e35ffbac0be7f7f..0000000000000000000000000000000000000000
--- a/include/benchmark.h
+++ /dev/null
@@ -1,124 +0,0 @@
-#ifndef BENCHMARK_H
-#define BENCHMARK_H
-
-#include "exception.h"
-#include <fmt/core.h>
-#include <fstream>
-#include <iomanip>
-#include <iostream>
-#include <nlohmann/json.hpp>
-#include <string>
-
-// Bookkeeping of test data to store data of one or more tests in a json file to
-// facilitate future accounting.
-//
-// Usage Example 1 (single test):
-// ==============================
-// 1. define our test
-//      eic::util::Test test1{
-//        {{"name", "example_test"},
-//        {"title", "Example Test"},
-//        {"description", "This is an example of a test definition"},
-//        {"quantity", "efficiency"},
-//        {"target", "1"}}};
-// 2. set pass/fail/error status and return value (in this case .99)
-//      test1.pass(0.99)
-// 3. write our test data to a json file
-//      eic::util::write_test(test1, "test1.json");
-//
-// Usage Example 2 (multiple tests):
-// =================================
-// 1. define our tests
-//      eic::util::Test test1{
-//        {{"name", "example_test"},
-//        {"title", "Example Test"},
-//        {"description", "This is an example of a test definition"},
-//        {"quantity", "efficiency"},
-//        {"target", "1"}}};
-//      eic::util::Test test2{
-//        {{"name", "another_test"},
-//        {"title", "Another example Test"},
-//        {"description", "This is a second example of a test definition"},
-//        {"quantity", "resolution"},
-//        {"target", "3."}}};
-// 2. set pass/fail/error status and return value (in this case .99)
-//      test1.fail(10)
-// 3. write our test data to a json file
-//      eic::util::write_test({test1, test2}, "test.json");
-
-// Namespace for utility scripts, FIXME this should be part of an independent
-// library
-namespace eic::util {
-
-  struct TestDefinitionError : Exception {
-    TestDefinitionError(std::string_view msg) : Exception(msg, "test_definition_error") {}
-  };
-
-  // Wrapper for our test data json, with three methods to set the status
-  // after test completion (to pass, fail or error). The default value
-  // is error.
-  // The following fields should be defined in the definitions json
-  // for the test to make sense:
-  //  - name: unique identifier for this test
-  //  - title: Slightly more verbose identifier for this test
-  //  - description: Concise description of what is tested
-  //  - quantity: What quantity is tested? Unites of value/target
-  //  - target: Target value of <quantity> that we want to reach
-  //  - value: Actual value of <quantity>
-  //  - weight: Weight for this test (this is defaulted to 1.0 if not specified)
-  //  - result: pass/fail/error
-  struct Test {
-    // note: round braces for the json constructor, as else it will pick the wrong
-    //       initializer-list constructur (it will put everything in an array)
-    Test(const std::map<std::string, std::string>& definition) : json(definition)
-    {
-      // std::cout << json.dump() << std::endl;
-      // initialize with error (as we don't have a value yet)
-      error();
-      // Check that all required fields are present
-      for (const auto& field :
-           {"name", "title", "description", "quantity", "target", "value", "result"}) {
-        if (json.find(field) == json.end()) {
-          throw TestDefinitionError{
-              fmt::format("Error in test definition: field '{}' missing", field)};
-        }
-      }
-      // Default "weight" to 1 if not set
-      if (json.find("weight") == json.end()) {
-        json["weight"] = 1.0;
-      }
-    }
-    // Set this test to pass/fail/error
-    void pass(double value) { update_result("pass", value); }
-    void fail(double value) { update_result("fail", value); }
-    void error(double value = 0) { update_result("error", value); }
-
-    nlohmann::json json;
-
-  private:
-    void update_result(std::string_view status, double value)
-    {
-      json["result"] = status;
-      json["value"]  = value;
-    }
-  };
-
-  void write_test(const std::vector<Test>& data, const std::string& fname)
-  {
-    nlohmann::json test;
-    for (auto& entry : data) {
-      test["tests"].push_back(entry.json);
-    }
-    std::cout << fmt::format("Writing test data to {}\n", fname);
-    std::ofstream output_file(fname);
-    output_file << std::setw(4) << test << "\n";
-  }
-  void write_test(const Test& data, const std::string& fname)
-  {
-    std::vector<Test> vtd{data};
-    write_test(vtd, fname);
-  }
-
-} // namespace eic::util
-
-#endif
diff --git a/include/clipp.h b/include/clipp.h
deleted file mode 100644
index a2a0cf8bcb160ad9d0ca5d5132864f4f184f66de..0000000000000000000000000000000000000000
--- a/include/clipp.h
+++ /dev/null
@@ -1,6248 +0,0 @@
-/*****************************************************************************
- *
- * CLIPP - command line interfaces for modern C++
- *
- * released under MIT license
- *
- * (c) 2017 André Müller; foss@andremueller-online.de
- *
- *****************************************************************************/
-
-#ifndef AM_CLIPP_H__
-#define AM_CLIPP_H__
-
-#include <cstring>
-#include <string>
-#include <cstdlib>
-#include <cstring>
-#include <cctype>
-#include <cmath>
-#include <memory>
-#include <vector>
-#include <limits>
-#include <stack>
-#include <algorithm>
-#include <sstream>
-#include <utility>
-#include <iterator>
-#include <functional>
-
-
-/*************************************************************************//**
- *
- * @brief primary namespace
- *
- *****************************************************************************/
-namespace clipp {
-
-
-
-/*****************************************************************************
- *
- * basic constants and datatype definitions
- *
- *****************************************************************************/
-using arg_index = int;
-
-using arg_string = std::string;
-using doc_string = std::string;
-
-using arg_list  = std::vector<arg_string>;
-
-
-
-/*************************************************************************//**
- *
- * @brief tristate
- *
- *****************************************************************************/
-enum class tri : char { no, yes, either };
-
-inline constexpr bool operator == (tri t, bool b) noexcept {
-    return b ? t != tri::no : t != tri::yes;
-}
-inline constexpr bool operator == (bool b, tri t) noexcept { return  (t == b); }
-inline constexpr bool operator != (tri t, bool b) noexcept { return !(t == b); }
-inline constexpr bool operator != (bool b, tri t) noexcept { return !(t == b); }
-
-
-
-/*************************************************************************//**
- *
- * @brief (start,size) index range
- *
- *****************************************************************************/
-class subrange {
-public:
-    using size_type = arg_string::size_type;
-
-    /** @brief default: no match */
-    explicit constexpr
-    subrange() noexcept :
-        at_{arg_string::npos}, length_{0}
-    {}
-
-    /** @brief match length & position within subject string */
-    explicit constexpr
-    subrange(size_type pos, size_type len) noexcept :
-        at_{pos}, length_{len}
-    {}
-
-    /** @brief position of the match within the subject string */
-    constexpr size_type at()     const noexcept { return at_; }
-    /** @brief length of the matching subsequence */
-    constexpr size_type length() const noexcept { return length_; }
-
-    /** @brief returns true, if query string is a prefix of the subject string */
-    constexpr bool prefix() const noexcept {
-        return at_ == 0 && length_ > 0;
-    }
-
-    /** @brief returns true, if query is a substring of the query string */
-    constexpr explicit operator bool () const noexcept {
-        return at_ != arg_string::npos && length_ > 0;
-    }
-
-private:
-    size_type at_;
-    size_type length_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief match predicates
- *
- *****************************************************************************/
-using match_predicate = std::function<bool(const arg_string&)>;
-using match_function  = std::function<subrange(const arg_string&)>;
-
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief type traits (NOT FOR DIRECT USE IN CLIENT CODE!)
- *        no interface guarantees; might be changed or removed in the future
- *
- *****************************************************************************/
-namespace traits {
-
-/*************************************************************************//**
- *
- * @brief function (class) signature type trait
- *
- *****************************************************************************/
-template<class Fn, class Ret, class... Args>
-constexpr auto
-check_is_callable(int) -> decltype(
-    std::declval<Fn>()(std::declval<Args>()...),
-    std::integral_constant<bool,
-        std::is_same<Ret,typename std::result_of<Fn(Args...)>::type>::value>{} );
-
-template<class,class,class...>
-constexpr auto
-check_is_callable(long) -> std::false_type;
-
-template<class Fn, class Ret>
-constexpr auto
-check_is_callable_without_arg(int) -> decltype(
-    std::declval<Fn>()(),
-    std::integral_constant<bool,
-        std::is_same<Ret,typename std::result_of<Fn()>::type>::value>{} );
-
-template<class,class>
-constexpr auto
-check_is_callable_without_arg(long) -> std::false_type;
-
-
-
-template<class Fn, class... Args>
-constexpr auto
-check_is_void_callable(int) -> decltype(
-    std::declval<Fn>()(std::declval<Args>()...), std::true_type{});
-
-template<class,class,class...>
-constexpr auto
-check_is_void_callable(long) -> std::false_type;
-
-template<class Fn>
-constexpr auto
-check_is_void_callable_without_arg(int) -> decltype(
-    std::declval<Fn>()(), std::true_type{});
-
-template<class>
-constexpr auto
-check_is_void_callable_without_arg(long) -> std::false_type;
-
-
-
-template<class Fn, class Ret>
-struct is_callable;
-
-
-template<class Fn, class Ret, class... Args>
-struct is_callable<Fn, Ret(Args...)> :
-    decltype(check_is_callable<Fn,Ret,Args...>(0))
-{};
-
-template<class Fn, class Ret>
-struct is_callable<Fn,Ret()> :
-    decltype(check_is_callable_without_arg<Fn,Ret>(0))
-{};
-
-
-template<class Fn, class... Args>
-struct is_callable<Fn, void(Args...)> :
-    decltype(check_is_void_callable<Fn,Args...>(0))
-{};
-
-template<class Fn>
-struct is_callable<Fn,void()> :
-    decltype(check_is_void_callable_without_arg<Fn>(0))
-{};
-
-
-
-/*************************************************************************//**
- *
- * @brief input range type trait
- *
- *****************************************************************************/
-template<class T>
-constexpr auto
-check_is_input_range(int) -> decltype(
-    begin(std::declval<T>()), end(std::declval<T>()),
-    std::true_type{});
-
-template<class T>
-constexpr auto
-check_is_input_range(char) -> decltype(
-    std::begin(std::declval<T>()), std::end(std::declval<T>()),
-    std::true_type{});
-
-template<class>
-constexpr auto
-check_is_input_range(long) -> std::false_type;
-
-template<class T>
-struct is_input_range :
-    decltype(check_is_input_range<T>(0))
-{};
-
-
-
-/*************************************************************************//**
- *
- * @brief size() member type trait
- *
- *****************************************************************************/
-template<class T>
-constexpr auto
-check_has_size_getter(int) ->
-    decltype(std::declval<T>().size(), std::true_type{});
-
-template<class>
-constexpr auto
-check_has_size_getter(long) -> std::false_type;
-
-template<class T>
-struct has_size_getter :
-    decltype(check_has_size_getter<T>(0))
-{};
-
-} // namespace traits
-
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
- *        no interface guarantees; might be changed or removed in the future
- *
- *****************************************************************************/
-namespace detail {
-
-
-/*************************************************************************//**
- * @brief forwards string to first non-whitespace char;
- *        std string -> unsigned conv yields max value, but we want 0;
- *        also checks for nullptr
- *****************************************************************************/
-inline bool
-fwd_to_unsigned_int(const char*& s)
-{
-    if(!s) return false;
-    for(; std::isspace(*s); ++s);
-    if(!s[0] || s[0] == '-') return false;
-    if(s[0] == '-') return false;
-    return true;
-}
-
-
-/*************************************************************************//**
- *
- * @brief value limits clamping
- *
- *****************************************************************************/
-template<class T, class V, bool = (sizeof(V) > sizeof(T))>
-struct limits_clamped {
-    static T from(const V& v) {
-        if(v > V(std::numeric_limits<T>::max())) {
-            return std::numeric_limits<T>::max();
-        }
-        if(v < V(std::numeric_limits<T>::lowest())) {
-            return std::numeric_limits<T>::lowest();
-        }
-        return T(v);
-    }
-};
-
-template<class T, class V>
-struct limits_clamped<T,V,false> {
-    static T from(const V& v) { return T(v); }
-};
-
-
-/*************************************************************************//**
- *
- * @brief returns value of v as a T, clamped at T's maximum
- *
- *****************************************************************************/
-template<class T, class V>
-inline T clamped_on_limits(const V& v) {
-    return limits_clamped<T,V>::from(v);
-}
-
-
-
-
-/*************************************************************************//**
- *
- * @brief type conversion helpers
- *
- *****************************************************************************/
-template<class T>
-struct make;
-
-template<>
-struct make<bool> {
-    static inline bool from(const char* s) {
-        if(!s) return false;
-        return static_cast<bool>(s);
-    }
-};
-
-template<>
-struct make<unsigned char> {
-    static inline unsigned char from(const char* s) {
-        if(!fwd_to_unsigned_int(s)) return (0);
-        return clamped_on_limits<unsigned char>(std::strtoull(s,nullptr,10));
-    }
-};
-
-template<>
-struct make<unsigned short int> {
-    static inline unsigned short int from(const char* s) {
-        if(!fwd_to_unsigned_int(s)) return (0);
-        return clamped_on_limits<unsigned short int>(std::strtoull(s,nullptr,10));
-    }
-};
-
-template<>
-struct make<unsigned int> {
-    static inline unsigned int from(const char* s) {
-        if(!fwd_to_unsigned_int(s)) return (0);
-        return clamped_on_limits<unsigned int>(std::strtoull(s,nullptr,10));
-    }
-};
-
-template<>
-struct make<unsigned long int> {
-    static inline unsigned long int from(const char* s) {
-        if(!fwd_to_unsigned_int(s)) return (0);
-        return clamped_on_limits<unsigned long int>(std::strtoull(s,nullptr,10));
-    }
-};
-
-template<>
-struct make<unsigned long long int> {
-    static inline unsigned long long int from(const char* s) {
-        if(!fwd_to_unsigned_int(s)) return (0);
-        return clamped_on_limits<unsigned long long int>(std::strtoull(s,nullptr,10));
-    }
-};
-
-template<>
-struct make<char> {
-    static inline char from(const char* s) {
-        //parse as single character?
-        const auto n = std::strlen(s);
-        if(n == 1) return s[0];
-        //parse as integer
-        return clamped_on_limits<char>(std::strtoll(s,nullptr,10));
-    }
-};
-
-template<>
-struct make<short int> {
-    static inline short int from(const char* s) {
-        return clamped_on_limits<short int>(std::strtoll(s,nullptr,10));
-    }
-};
-
-template<>
-struct make<int> {
-    static inline int from(const char* s) {
-        return clamped_on_limits<int>(std::strtoll(s,nullptr,10));
-    }
-};
-
-template<>
-struct make<long int> {
-    static inline long int from(const char* s) {
-        return clamped_on_limits<long int>(std::strtoll(s,nullptr,10));
-    }
-};
-
-template<>
-struct make<long long int> {
-    static inline long long int from(const char* s) {
-        return (std::strtoll(s,nullptr,10));
-    }
-};
-
-template<>
-struct make<float> {
-    static inline float from(const char* s) {
-        return (std::strtof(s,nullptr));
-    }
-};
-
-template<>
-struct make<double> {
-    static inline double from(const char* s) {
-        return (std::strtod(s,nullptr));
-    }
-};
-
-template<>
-struct make<long double> {
-    static inline long double from(const char* s) {
-        return (std::strtold(s,nullptr));
-    }
-};
-
-template<>
-struct make<std::string> {
-    static inline std::string from(const char* s) {
-        return std::string(s);
-    }
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief assigns boolean constant to one or multiple target objects
- *
- *****************************************************************************/
-template<class T, class V = T>
-class assign_value
-{
-public:
-    template<class X>
-    explicit constexpr
-    assign_value(T& target, X&& value) noexcept :
-        t_{std::addressof(target)}, v_{std::forward<X>(value)}
-    {}
-
-    void operator () () const {
-        if(t_) *t_ = v_;
-    }
-
-private:
-    T* t_;
-    V v_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief flips bools
- *
- *****************************************************************************/
-class flip_bool
-{
-public:
-    explicit constexpr
-    flip_bool(bool& target) noexcept :
-        b_{&target}
-    {}
-
-    void operator () () const {
-        if(b_) *b_ = !*b_;
-    }
-
-private:
-    bool* b_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief increments using operator ++
- *
- *****************************************************************************/
-template<class T>
-class increment
-{
-public:
-    explicit constexpr
-    increment(T& target) noexcept : t_{std::addressof(target)} {}
-
-    void operator () () const {
-        if(t_) ++(*t_);
-    }
-
-private:
-    T* t_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief decrements using operator --
- *
- *****************************************************************************/
-template<class T>
-class decrement
-{
-public:
-    explicit constexpr
-    decrement(T& target) noexcept : t_{std::addressof(target)} {}
-
-    void operator () () const {
-        if(t_) --(*t_);
-    }
-
-private:
-    T* t_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief increments by a fixed amount using operator +=
- *
- *****************************************************************************/
-template<class T>
-class increment_by
-{
-public:
-    explicit constexpr
-    increment_by(T& target, T by) noexcept :
-        t_{std::addressof(target)}, by_{std::move(by)}
-    {}
-
-    void operator () () const {
-        if(t_) (*t_) += by_;
-    }
-
-private:
-    T* t_;
-    T by_;
-};
-
-
-
-
-/*************************************************************************//**
- *
- * @brief makes a value from a string and assigns it to an object
- *
- *****************************************************************************/
-template<class T>
-class map_arg_to
-{
-public:
-    explicit constexpr
-    map_arg_to(T& target) noexcept : t_{std::addressof(target)} {}
-
-    void operator () (const char* s) const {
-        if(t_ && s && (std::strlen(s) > 0))
-            *t_ = detail::make<T>::from(s);
-    }
-
-private:
-    T* t_;
-};
-
-
-//-------------------------------------------------------------------
-/**
- * @brief specialization for vectors: append element
- */
-template<class T>
-class map_arg_to<std::vector<T>>
-{
-public:
-    map_arg_to(std::vector<T>& target): t_{std::addressof(target)} {}
-
-    void operator () (const char* s) const {
-        if(t_ && s) t_->push_back(detail::make<T>::from(s));
-    }
-
-private:
-    std::vector<T>* t_;
-};
-
-
-//-------------------------------------------------------------------
-/**
- * @brief specialization for bools:
- *        set to true regardless of string content
- */
-template<>
-class map_arg_to<bool>
-{
-public:
-    map_arg_to(bool& target): t_{&target} {}
-
-    void operator () (const char* s) const {
-        if(t_ && s) *t_ = true;
-    }
-
-private:
-    bool* t_;
-};
-
-
-} // namespace detail
-
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief string matching and processing tools
- *
- *****************************************************************************/
-
-namespace str {
-
-
-/*************************************************************************//**
- *
- * @brief converts string to value of target type 'T'
- *
- *****************************************************************************/
-template<class T>
-T make(const arg_string& s)
-{
-    return detail::make<T>::from(s);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief removes trailing whitespace from string
- *
- *****************************************************************************/
-template<class C, class T, class A>
-inline void
-trimr(std::basic_string<C,T,A>& s)
-{
-    if(s.empty()) return;
-
-    s.erase(
-        std::find_if_not(s.rbegin(), s.rend(),
-                         [](char c) { return std::isspace(c);} ).base(),
-        s.end() );
-}
-
-
-/*************************************************************************//**
- *
- * @brief removes leading whitespace from string
- *
- *****************************************************************************/
-template<class C, class T, class A>
-inline void
-triml(std::basic_string<C,T,A>& s)
-{
-    if(s.empty()) return;
-
-    s.erase(
-        s.begin(),
-        std::find_if_not(s.begin(), s.end(),
-                         [](char c) { return std::isspace(c);})
-    );
-}
-
-
-/*************************************************************************//**
- *
- * @brief removes leading and trailing whitespace from string
- *
- *****************************************************************************/
-template<class C, class T, class A>
-inline void
-trim(std::basic_string<C,T,A>& s)
-{
-    triml(s);
-    trimr(s);
-}
-
-
-/*************************************************************************//**
- *
- * @brief removes all whitespaces from string
- *
- *****************************************************************************/
-template<class C, class T, class A>
-inline void
-remove_ws(std::basic_string<C,T,A>& s)
-{
-    if(s.empty()) return;
-
-    s.erase(std::remove_if(s.begin(), s.end(),
-                           [](char c) { return std::isspace(c); }),
-            s.end() );
-}
-
-
-/*************************************************************************//**
- *
- * @brief returns true, if the 'prefix' argument
- *        is a prefix of the 'subject' argument
- *
- *****************************************************************************/
-template<class C, class T, class A>
-inline bool
-has_prefix(const std::basic_string<C,T,A>& subject,
-           const std::basic_string<C,T,A>& prefix)
-{
-    if(prefix.size() > subject.size()) return false;
-    return subject.find(prefix) == 0;
-}
-
-
-/*************************************************************************//**
- *
- * @brief returns true, if the 'postfix' argument
- *        is a postfix of the 'subject' argument
- *
- *****************************************************************************/
-template<class C, class T, class A>
-inline bool
-has_postfix(const std::basic_string<C,T,A>& subject,
-            const std::basic_string<C,T,A>& postfix)
-{
-    if(postfix.size() > subject.size()) return false;
-    return (subject.size() - postfix.size()) == subject.find(postfix);
-}
-
-
-
-/*************************************************************************//**
-*
-* @brief   returns longest common prefix of several
-*          sequential random access containers
-*
-* @details InputRange require begin and end (member functions or overloads)
-*          the elements of InputRange require a size() member
-*
-*****************************************************************************/
-template<class InputRange>
-auto
-longest_common_prefix(const InputRange& strs)
-    -> typename std::decay<decltype(*begin(strs))>::type
-{
-    static_assert(traits::is_input_range<InputRange>(),
-        "parameter must satisfy the InputRange concept");
-
-    static_assert(traits::has_size_getter<
-        typename std::decay<decltype(*begin(strs))>::type>(),
-        "elements of input range must have a ::size() member function");
-
-    using std::begin;
-    using std::end;
-
-    using item_t = typename std::decay<decltype(*begin(strs))>::type;
-    using str_size_t = typename std::decay<decltype(begin(strs)->size())>::type;
-
-    const auto n = size_t(distance(begin(strs), end(strs)));
-    if(n < 1) return item_t("");
-    if(n == 1) return *begin(strs);
-
-    //length of shortest string
-    auto m = std::min_element(begin(strs), end(strs),
-                [](const item_t& a, const item_t& b) {
-                    return a.size() < b.size(); })->size();
-
-    //check each character until we find a mismatch
-    for(str_size_t i = 0; i < m; ++i) {
-        for(str_size_t j = 1; j < n; ++j) {
-            if(strs[j][i] != strs[j-1][i])
-                return strs[0].substr(0, i);
-        }
-    }
-    return strs[0].substr(0, m);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief  returns longest substring range that could be found in 'arg'
- *
- * @param  arg         string to be searched in
- * @param  substrings  range of candidate substrings
- *
- *****************************************************************************/
-template<class C, class T, class A, class InputRange>
-subrange
-longest_substring_match(const std::basic_string<C,T,A>& arg,
-                        const InputRange& substrings)
-{
-    using string_t = std::basic_string<C,T,A>;
-
-    static_assert(traits::is_input_range<InputRange>(),
-        "parameter must satisfy the InputRange concept");
-
-    static_assert(std::is_same<string_t,
-        typename std::decay<decltype(*begin(substrings))>::type>(),
-        "substrings must have same type as 'arg'");
-
-    auto i = string_t::npos;
-    auto n = string_t::size_type(0);
-    for(const auto& s : substrings) {
-        auto j = arg.find(s);
-        if(j != string_t::npos && s.size() > n) {
-            i = j;
-            n = s.size();
-        }
-    }
-    return subrange{i,n};
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief  returns longest prefix range that could be found in 'arg'
- *
- * @param  arg       string to be searched in
- * @param  prefixes  range of candidate prefix strings
- *
- *****************************************************************************/
-template<class C, class T, class A, class InputRange>
-subrange
-longest_prefix_match(const std::basic_string<C,T,A>& arg,
-                     const InputRange& prefixes)
-{
-    using string_t = std::basic_string<C,T,A>;
-    using s_size_t = typename string_t::size_type;
-
-    static_assert(traits::is_input_range<InputRange>(),
-        "parameter must satisfy the InputRange concept");
-
-    static_assert(std::is_same<string_t,
-        typename std::decay<decltype(*begin(prefixes))>::type>(),
-        "prefixes must have same type as 'arg'");
-
-    auto i = string_t::npos;
-    auto n = s_size_t(0);
-    for(const auto& s : prefixes) {
-        auto j = arg.find(s);
-        if(j == 0 && s.size() > n) {
-            i = 0;
-            n = s.size();
-        }
-    }
-    return subrange{i,n};
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief returns the first occurrence of 'query' within 'subject'
- *
- *****************************************************************************/
-template<class C, class T, class A>
-inline subrange
-substring_match(const std::basic_string<C,T,A>& subject,
-                const std::basic_string<C,T,A>& query)
-{
-    if(subject.empty() || query.empty()) return subrange{};
-    auto i = subject.find(query);
-    if(i == std::basic_string<C,T,A>::npos) return subrange{};
-    return subrange{i,query.size()};
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief returns first substring match (pos,len) within the input string
- *        that represents a number
- *        (with at maximum one decimal point and digit separators)
- *
- *****************************************************************************/
-template<class C, class T, class A>
-subrange
-first_number_match(std::basic_string<C,T,A> s,
-                   C digitSeparator = C(','),
-                   C decimalPoint = C('.'),
-                   C exponential = C('e'))
-{
-    using string_t = std::basic_string<C,T,A>;
-
-    str::trim(s);
-    if(s.empty()) return subrange{};
-
-    auto i = s.find_first_of("0123456789+-");
-    if(i == string_t::npos) {
-        i = s.find(decimalPoint);
-        if(i == string_t::npos) return subrange{};
-    }
-
-    bool point = false;
-    bool sep = false;
-    auto exp = string_t::npos;
-    auto j = i + 1;
-    for(; j < s.size(); ++j) {
-        if(s[j] == digitSeparator) {
-            if(!sep) sep = true; else break;
-        }
-        else {
-            sep = false;
-            if(s[j] == decimalPoint) {
-                //only one decimal point before exponent allowed
-                if(!point && exp == string_t::npos) point = true; else break;
-            }
-            else if(std::tolower(s[j]) == std::tolower(exponential)) {
-                //only one exponent separator allowed
-                if(exp == string_t::npos) exp = j; else break;
-            }
-            else if(exp != string_t::npos && (exp+1) == j) {
-                //only sign or digit after exponent separator
-                if(s[j] != '+' && s[j] != '-' && !std::isdigit(s[j])) break;
-            }
-            else if(!std::isdigit(s[j])) {
-                break;
-            }
-        }
-    }
-
-    //if length == 1 then must be a digit
-    if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
-
-    return subrange{i,j-i};
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief returns first substring match (pos,len)
- *        that represents an integer (with optional digit separators)
- *
- *****************************************************************************/
-template<class C, class T, class A>
-subrange
-first_integer_match(std::basic_string<C,T,A> s,
-                    C digitSeparator = C(','))
-{
-    using string_t = std::basic_string<C,T,A>;
-
-    str::trim(s);
-    if(s.empty()) return subrange{};
-
-    auto i = s.find_first_of("0123456789+-");
-    if(i == string_t::npos) return subrange{};
-
-    bool sep = false;
-    auto j = i + 1;
-    for(; j < s.size(); ++j) {
-        if(s[j] == digitSeparator) {
-            if(!sep) sep = true; else break;
-        }
-        else {
-            sep = false;
-            if(!std::isdigit(s[j])) break;
-        }
-    }
-
-    //if length == 1 then must be a digit
-    if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
-
-    return subrange{i,j-i};
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief returns true if candidate string represents a number
- *
- *****************************************************************************/
-template<class C, class T, class A>
-bool represents_number(const std::basic_string<C,T,A>& candidate,
-                   C digitSeparator = C(','),
-                   C decimalPoint = C('.'),
-                   C exponential = C('e'))
-{
-    const auto match = str::first_number_match(candidate, digitSeparator,
-                                               decimalPoint, exponential);
-
-    return (match && match.length() == candidate.size());
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief returns true if candidate string represents an integer
- *
- *****************************************************************************/
-template<class C, class T, class A>
-bool represents_integer(const std::basic_string<C,T,A>& candidate,
-                        C digitSeparator = C(','))
-{
-    const auto match = str::first_integer_match(candidate, digitSeparator);
-    return (match && match.length() == candidate.size());
-}
-
-} // namespace str
-
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief makes function object with a const char* parameter
- *        that assigns a value to a ref-captured object
- *
- *****************************************************************************/
-template<class T, class V>
-inline detail::assign_value<T,V>
-set(T& target, V value) {
-    return detail::assign_value<T>{target, std::move(value)};
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes parameter-less function object
- *        that assigns value(s) to a ref-captured object;
- *        value(s) are obtained by converting the const char* argument to
- *        the captured object types;
- *        bools are always set to true if the argument is not nullptr
- *
- *****************************************************************************/
-template<class T>
-inline detail::map_arg_to<T>
-set(T& target) {
-    return detail::map_arg_to<T>{target};
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes function object that sets a bool to true
- *
- *****************************************************************************/
-inline detail::assign_value<bool>
-set(bool& target) {
-    return detail::assign_value<bool>{target,true};
-}
-
-/*************************************************************************//**
- *
- * @brief makes function object that sets a bool to false
- *
- *****************************************************************************/
-inline detail::assign_value<bool>
-unset(bool& target) {
-    return detail::assign_value<bool>{target,false};
-}
-
-/*************************************************************************//**
- *
- * @brief makes function object that flips the value of a ref-captured bool
- *
- *****************************************************************************/
-inline detail::flip_bool
-flip(bool& b) {
-    return detail::flip_bool(b);
-}
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief makes function object that increments using operator ++
- *
- *****************************************************************************/
-template<class T>
-inline detail::increment<T>
-increment(T& target) {
-    return detail::increment<T>{target};
-}
-
-/*************************************************************************//**
- *
- * @brief makes function object that decrements using operator --
- *
- *****************************************************************************/
-template<class T>
-inline detail::increment_by<T>
-increment(T& target, T by) {
-    return detail::increment_by<T>{target, std::move(by)};
-}
-
-/*************************************************************************//**
- *
- * @brief makes function object that increments by a fixed amount using operator +=
- *
- *****************************************************************************/
-template<class T>
-inline detail::decrement<T>
-decrement(T& target) {
-    return detail::decrement<T>{target};
-}
-
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
- *
- *****************************************************************************/
-namespace detail {
-
-
-/*************************************************************************//**
- *
- * @brief mixin that provides action definition and execution
- *
- *****************************************************************************/
-template<class Derived>
-class action_provider
-{
-private:
-    //---------------------------------------------------------------
-    using simple_action = std::function<void()>;
-    using arg_action    = std::function<void(const char*)>;
-    using index_action  = std::function<void(int)>;
-
-    //-----------------------------------------------------
-    class simple_action_adapter {
-    public:
-        simple_action_adapter() = default;
-        simple_action_adapter(const simple_action& a): action_(a) {}
-        simple_action_adapter(simple_action&& a): action_(std::move(a)) {}
-        void operator() (const char*) const { action_(); }
-        void operator() (int) const { action_(); }
-    private:
-        simple_action action_;
-    };
-
-
-public:
-    //---------------------------------------------------------------
-    /** @brief adds an action that has an operator() that is callable
-     *         with a 'const char*' argument */
-    Derived&
-    call(arg_action a) {
-        argActions_.push_back(std::move(a));
-        return *static_cast<Derived*>(this);
-    }
-
-    /** @brief adds an action that has an operator()() */
-    Derived&
-    call(simple_action a) {
-        argActions_.push_back(simple_action_adapter(std::move(a)));
-        return *static_cast<Derived*>(this);
-    }
-
-    /** @brief adds an action that has an operator() that is callable
-     *         with a 'const char*' argument */
-    Derived& operator () (arg_action a)    { return call(std::move(a)); }
-
-    /** @brief adds an action that has an operator()() */
-    Derived& operator () (simple_action a) { return call(std::move(a)); }
-
-
-    //---------------------------------------------------------------
-    /** @brief adds an action that will set the value of 't' from
-     *         a 'const char*' arg */
-    template<class Target>
-    Derived&
-    set(Target& t) {
-        return call(clipp::set(t));
-    }
-
-    /** @brief adds an action that will set the value of 't' to 'v' */
-    template<class Target, class Value>
-    Derived&
-    set(Target& t, Value&& v) {
-        return call(clipp::set(t, std::forward<Value>(v)));
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief adds an action that will be called if a parameter
-     *         matches an argument for the 2nd, 3rd, 4th, ... time
-     */
-    Derived&
-    if_repeated(simple_action a) {
-        repeatActions_.push_back(simple_action_adapter{std::move(a)});
-        return *static_cast<Derived*>(this);
-    }
-    /** @brief adds an action that will be called with the argument's
-     *         index if a parameter matches an argument for
-     *         the 2nd, 3rd, 4th, ... time
-     */
-    Derived&
-    if_repeated(index_action a) {
-        repeatActions_.push_back(std::move(a));
-        return *static_cast<Derived*>(this);
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief adds an action that will be called if a required parameter
-     *         is missing
-     */
-    Derived&
-    if_missing(simple_action a) {
-        missingActions_.push_back(simple_action_adapter{std::move(a)});
-        return *static_cast<Derived*>(this);
-    }
-    /** @brief adds an action that will be called if a required parameter
-     *         is missing; the action will get called with the index of
-     *         the command line argument where the missing event occured first
-     */
-    Derived&
-    if_missing(index_action a) {
-        missingActions_.push_back(std::move(a));
-        return *static_cast<Derived*>(this);
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief adds an action that will be called if a parameter
-     *         was matched, but was unreachable in the current scope
-     */
-    Derived&
-    if_blocked(simple_action a) {
-        blockedActions_.push_back(simple_action_adapter{std::move(a)});
-        return *static_cast<Derived*>(this);
-    }
-    /** @brief adds an action that will be called if a parameter
-     *         was matched, but was unreachable in the current scope;
-     *         the action will be called with the index of
-     *         the command line argument where the problem occured
-     */
-    Derived&
-    if_blocked(index_action a) {
-        blockedActions_.push_back(std::move(a));
-        return *static_cast<Derived*>(this);
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief adds an action that will be called if a parameter match
-     *         was in conflict with a different alternative parameter
-     */
-    Derived&
-    if_conflicted(simple_action a) {
-        conflictActions_.push_back(simple_action_adapter{std::move(a)});
-        return *static_cast<Derived*>(this);
-    }
-    /** @brief adds an action that will be called if a parameter match
-     *         was in conflict with a different alternative paramete;
-     *         the action will be called with the index of
-     *         the command line argument where the problem occuredr
-     */
-    Derived&
-    if_conflicted(index_action a) {
-        conflictActions_.push_back(std::move(a));
-        return *static_cast<Derived*>(this);
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief adds targets = either objects whose values should be
-     *         set by command line arguments or actions that should
-     *         be called in case of a match */
-    template<class T, class... Ts>
-    Derived&
-    target(T&& t, Ts&&... ts) {
-        target(std::forward<T>(t));
-        target(std::forward<Ts>(ts)...);
-        return *static_cast<Derived*>(this);
-    }
-
-    /** @brief adds action that should be called in case of a match */
-    template<class T, class = typename std::enable_if<
-            !std::is_fundamental<typename std::decay<T>::type>() &&
-            (traits::is_callable<T,void()>() ||
-             traits::is_callable<T,void(const char*)>() )
-        >::type>
-    Derived&
-    target(T&& t) {
-        call(std::forward<T>(t));
-        return *static_cast<Derived*>(this);
-    }
-
-    /** @brief adds object whose value should be set by command line arguments
-     */
-    template<class T, class = typename std::enable_if<
-            std::is_fundamental<typename std::decay<T>::type>() ||
-            (!traits::is_callable<T,void()>() &&
-             !traits::is_callable<T,void(const char*)>() )
-        >::type>
-    Derived&
-    target(T& t) {
-        set(t);
-        return *static_cast<Derived*>(this);
-    }
-
-    //TODO remove ugly empty param list overload
-    Derived&
-    target() {
-        return *static_cast<Derived*>(this);
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief adds target, see member function 'target' */
-    template<class Target>
-    inline friend Derived&
-    operator << (Target&& t, Derived& p) {
-        p.target(std::forward<Target>(t));
-        return p;
-    }
-    /** @brief adds target, see member function 'target' */
-    template<class Target>
-    inline friend Derived&&
-    operator << (Target&& t, Derived&& p) {
-        p.target(std::forward<Target>(t));
-        return std::move(p);
-    }
-
-    //-----------------------------------------------------
-    /** @brief adds target, see member function 'target' */
-    template<class Target>
-    inline friend Derived&
-    operator >> (Derived& p, Target&& t) {
-        p.target(std::forward<Target>(t));
-        return p;
-    }
-    /** @brief adds target, see member function 'target' */
-    template<class Target>
-    inline friend Derived&&
-    operator >> (Derived&& p, Target&& t) {
-        p.target(std::forward<Target>(t));
-        return std::move(p);
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief executes all argument actions */
-    void execute_actions(const arg_string& arg) const {
-        int i = 0;
-        for(const auto& a : argActions_) {
-            ++i;
-            a(arg.c_str());
-        }
-    }
-
-    /** @brief executes repeat actions */
-    void notify_repeated(arg_index idx) const {
-        for(const auto& a : repeatActions_) a(idx);
-    }
-    /** @brief executes missing error actions */
-    void notify_missing(arg_index idx) const {
-        for(const auto& a : missingActions_) a(idx);
-    }
-    /** @brief executes blocked error actions */
-    void notify_blocked(arg_index idx) const {
-        for(const auto& a : blockedActions_) a(idx);
-    }
-    /** @brief executes conflict error actions */
-    void notify_conflict(arg_index idx) const {
-        for(const auto& a : conflictActions_) a(idx);
-    }
-
-private:
-    //---------------------------------------------------------------
-    std::vector<arg_action> argActions_;
-    std::vector<index_action> repeatActions_;
-    std::vector<index_action> missingActions_;
-    std::vector<index_action> blockedActions_;
-    std::vector<index_action> conflictActions_;
-};
-
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief mixin that provides basic common settings of parameters and groups
- *
- *****************************************************************************/
-template<class Derived>
-class token
-{
-public:
-    //---------------------------------------------------------------
-    using doc_string = clipp::doc_string;
-
-
-    //---------------------------------------------------------------
-    /** @brief returns documentation string */
-    const doc_string& doc() const noexcept {
-        return doc_;
-    }
-
-    /** @brief sets documentations string */
-    Derived& doc(const doc_string& txt) {
-        doc_ = txt;
-        return *static_cast<Derived*>(this);
-    }
-
-    /** @brief sets documentations string */
-    Derived& doc(doc_string&& txt) {
-        doc_ = std::move(txt);
-        return *static_cast<Derived*>(this);
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns if a group/parameter is repeatable */
-    bool repeatable() const noexcept {
-        return repeatable_;
-    }
-
-    /** @brief sets repeatability of group/parameter */
-    Derived& repeatable(bool yes) noexcept {
-        repeatable_ = yes;
-        return *static_cast<Derived*>(this);
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns if a group/parameter is blocking/positional */
-    bool blocking() const noexcept {
-        return blocking_;
-    }
-
-    /** @brief determines, if a group/parameter is blocking/positional */
-    Derived& blocking(bool yes) noexcept {
-        blocking_ = yes;
-        return *static_cast<Derived*>(this);
-    }
-
-
-private:
-    //---------------------------------------------------------------
-    doc_string doc_;
-    bool repeatable_ = false;
-    bool blocking_ = false;
-};
-
-
-
-
-/*************************************************************************//**
- *
- * @brief sets documentation strings on a token
- *
- *****************************************************************************/
-template<class T>
-inline T&
-operator % (doc_string docstr, token<T>& p)
-{
-    return p.doc(std::move(docstr));
-}
-//---------------------------------------------------------
-template<class T>
-inline T&&
-operator % (doc_string docstr, token<T>&& p)
-{
-    return std::move(p.doc(std::move(docstr)));
-}
-
-//---------------------------------------------------------
-template<class T>
-inline T&
-operator % (token<T>& p, doc_string docstr)
-{
-    return p.doc(std::move(docstr));
-}
-//---------------------------------------------------------
-template<class T>
-inline T&&
-operator % (token<T>&& p, doc_string docstr)
-{
-    return std::move(p.doc(std::move(docstr)));
-}
-
-
-
-
-/*************************************************************************//**
- *
- * @brief sets documentation strings on a token
- *
- *****************************************************************************/
-template<class T>
-inline T&
-doc(doc_string docstr, token<T>& p)
-{
-    return p.doc(std::move(docstr));
-}
-//---------------------------------------------------------
-template<class T>
-inline T&&
-doc(doc_string docstr, token<T>&& p)
-{
-    return std::move(p.doc(std::move(docstr)));
-}
-
-
-
-} // namespace detail
-
-
-
-/*************************************************************************//**
- *
- * @brief contains parameter matching functions and function classes
- *
- *****************************************************************************/
-namespace match {
-
-
-/*************************************************************************//**
- *
- * @brief predicate that is always true
- *
- *****************************************************************************/
-inline bool
-any(const arg_string&) { return true; }
-
-/*************************************************************************//**
- *
- * @brief predicate that is always false
- *
- *****************************************************************************/
-inline bool
-none(const arg_string&) { return false; }
-
-
-
-/*************************************************************************//**
- *
- * @brief predicate that returns true if the argument string is non-empty string
- *
- *****************************************************************************/
-inline bool
-nonempty(const arg_string& s) {
-    return !s.empty();
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief predicate that returns true if the argument is a non-empty
- *        string that consists only of alphanumeric characters
- *
- *****************************************************************************/
-inline bool
-alphanumeric(const arg_string& s) {
-    if(s.empty()) return false;
-    return std::all_of(s.begin(), s.end(), [](char c) {return std::isalnum(c); });
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief predicate that returns true if the argument is a non-empty
- *        string that consists only of alphabetic characters
- *
- *****************************************************************************/
-inline bool
-alphabetic(const arg_string& s) {
-    return std::all_of(s.begin(), s.end(), [](char c) {return std::isalpha(c); });
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief predicate that returns the first substring match within the input
- *        string that rmeepresents a number
- *        (with at maximum one decimal point and digit separators)
- *
- *****************************************************************************/
-class numbers
-{
-public:
-    explicit
-    numbers(char decimalPoint = '.',
-            char digitSeparator = ' ',
-            char exponentSeparator = 'e')
-    :
-        decpoint_{decimalPoint}, separator_{digitSeparator},
-        exp_{exponentSeparator}
-    {}
-
-    subrange operator () (const arg_string& s) const {
-        return str::first_number_match(s, separator_, decpoint_, exp_);
-    }
-
-private:
-    char decpoint_;
-    char separator_;
-    char exp_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief predicate that returns true if the input string represents an integer
- *        (with optional digit separators)
- *
- *****************************************************************************/
-class integers {
-public:
-    explicit
-    integers(char digitSeparator = ' '): separator_{digitSeparator} {}
-
-    subrange operator () (const arg_string& s) const {
-        return str::first_integer_match(s, separator_);
-    }
-
-private:
-    char separator_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief predicate that returns true if the input string represents
- *        a non-negative integer (with optional digit separators)
- *
- *****************************************************************************/
-class positive_integers {
-public:
-    explicit
-    positive_integers(char digitSeparator = ' '): separator_{digitSeparator} {}
-
-    subrange operator () (const arg_string& s) const {
-        auto match = str::first_integer_match(s, separator_);
-        if(!match) return subrange{};
-        if(s[match.at()] == '-') return subrange{};
-        return match;
-    }
-
-private:
-    char separator_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief predicate that returns true if the input string
- *        contains a given substring
- *
- *****************************************************************************/
-class substring
-{
-public:
-    explicit
-    substring(arg_string str): str_{std::move(str)} {}
-
-    subrange operator () (const arg_string& s) const {
-        return str::substring_match(s, str_);
-    }
-
-private:
-    arg_string str_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief predicate that returns true if the input string starts
- *        with a given prefix
- *
- *****************************************************************************/
-class prefix {
-public:
-    explicit
-    prefix(arg_string p): prefix_{std::move(p)} {}
-
-    bool operator () (const arg_string& s) const {
-        return s.find(prefix_) == 0;
-    }
-
-private:
-    arg_string prefix_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief predicate that returns true if the input string does not start
- *        with a given prefix
- *
- *****************************************************************************/
-class prefix_not {
-public:
-    explicit
-    prefix_not(arg_string p): prefix_{std::move(p)} {}
-
-    bool operator () (const arg_string& s) const {
-        return s.find(prefix_) != 0;
-    }
-
-private:
-    arg_string prefix_;
-};
-
-
-/** @brief alias for prefix_not */
-using noprefix = prefix_not;
-
-
-
-/*************************************************************************//**
- *
- * @brief predicate that returns true if the length of the input string
- *        is wihtin a given interval
- *
- *****************************************************************************/
-class length {
-public:
-    explicit
-    length(std::size_t exact):
-        min_{exact}, max_{exact}
-    {}
-
-    explicit
-    length(std::size_t min, std::size_t max):
-        min_{min}, max_{max}
-    {}
-
-    bool operator () (const arg_string& s) const {
-        return s.size() >= min_ && s.size() <= max_;
-    }
-
-private:
-    std::size_t min_;
-    std::size_t max_;
-};
-
-
-/*************************************************************************//**
- *
- * @brief makes function object that returns true if the input string has a
- *        given minimum length
- *
- *****************************************************************************/
-inline length min_length(std::size_t min)
-{
-    return length{min, arg_string::npos-1};
-}
-
-/*************************************************************************//**
- *
- * @brief makes function object that returns true if the input string is
- *        not longer than a given maximum length
- *
- *****************************************************************************/
-inline length max_length(std::size_t max)
-{
-    return length{0, max};
-}
-
-
-} // namespace match
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief command line parameter that can match one or many arguments.
- *
- *****************************************************************************/
-class parameter :
-    public detail::token<parameter>,
-    public detail::action_provider<parameter>
-{
-    class predicate_adapter {
-    public:
-        explicit
-        predicate_adapter(match_predicate pred): match_{std::move(pred)} {}
-
-        subrange operator () (const arg_string& arg) const {
-            return match_(arg) ? subrange{0,arg.size()} : subrange{};
-        }
-
-    private:
-        match_predicate match_;
-    };
-
-public:
-    //---------------------------------------------------------------
-    /** @brief makes default parameter, that will match nothing */
-    parameter():
-        flags_{},
-        matcher_{predicate_adapter{match::none}},
-        label_{}, required_{false}
-    {}
-
-    /** @brief makes "flag" parameter */
-    template<class... Strings>
-    explicit
-    parameter(arg_string str, Strings&&... strs):
-        flags_{},
-        matcher_{predicate_adapter{match::none}},
-        label_{}, required_{false}
-    {
-        add_flags(std::move(str), std::forward<Strings>(strs)...);
-    }
-
-    /** @brief makes "flag" parameter from range of strings */
-    explicit
-    parameter(const arg_list& flaglist):
-        flags_{},
-        matcher_{predicate_adapter{match::none}},
-        label_{}, required_{false}
-    {
-        add_flags(flaglist);
-    }
-
-    //-----------------------------------------------------
-    /** @brief makes "value" parameter with custom match predicate
-     *         (= yes/no matcher)
-     */
-    explicit
-    parameter(match_predicate filter):
-        flags_{},
-        matcher_{predicate_adapter{std::move(filter)}},
-        label_{}, required_{false}
-    {}
-
-    /** @brief makes "value" parameter with custom match function
-     *         (= partial matcher)
-     */
-    explicit
-    parameter(match_function filter):
-        flags_{},
-        matcher_{std::move(filter)},
-        label_{}, required_{false}
-    {}
-
-
-    //---------------------------------------------------------------
-    /** @brief returns if a parameter is required */
-    bool
-    required() const noexcept {
-        return required_;
-    }
-
-    /** @brief determines if a parameter is required */
-    parameter&
-    required(bool yes) noexcept {
-        required_ = yes;
-        return *this;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns parameter label;
-     *         will be used for documentation, if flags are empty
-     */
-    const doc_string&
-    label() const {
-        return label_;
-    }
-
-    /** @brief sets parameter label;
-     *         will be used for documentation, if flags are empty
-     */
-    parameter&
-    label(const doc_string& lbl) {
-        label_ = lbl;
-        return *this;
-    }
-
-    /** @brief sets parameter label;
-     *         will be used for documentation, if flags are empty
-     */
-    parameter&
-    label(doc_string&& lbl) {
-        label_ = lbl;
-        return *this;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns either longest matching prefix of 'arg' in any
-     *         of the flags or the result of the custom match operation
-     */
-    subrange
-    match(const arg_string& arg) const
-    {
-        if(arg.empty()) return subrange{};
-
-        if(flags_.empty()) {
-            return matcher_(arg);
-        }
-        else {
-            if(std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) {
-                return subrange{0,arg.size()};
-            }
-            return str::longest_prefix_match(arg, flags_);
-        }
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief access range of flag strings */
-    const arg_list&
-    flags() const noexcept {
-        return flags_;
-    }
-
-    /** @brief access custom match operation */
-    const match_function&
-    matcher() const noexcept {
-        return matcher_;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief prepend prefix to each flag */
-    inline friend parameter&
-    with_prefix(const arg_string& prefix, parameter& p)
-    {
-        if(prefix.empty() || p.flags().empty()) return p;
-
-        for(auto& f : p.flags_) {
-            if(f.find(prefix) != 0) f.insert(0, prefix);
-        }
-        return p;
-    }
-
-
-    /** @brief prepend prefix to each flag
-     */
-    inline friend parameter&
-    with_prefixes_short_long(
-        const arg_string& shortpfx, const arg_string& longpfx,
-        parameter& p)
-    {
-        if(shortpfx.empty() && longpfx.empty()) return p;
-        if(p.flags().empty()) return p;
-
-        for(auto& f : p.flags_) {
-            if(f.size() == 1) {
-                if(f.find(shortpfx) != 0) f.insert(0, shortpfx);
-            } else {
-                if(f.find(longpfx) != 0) f.insert(0, longpfx);
-            }
-        }
-        return p;
-    }
-
-private:
-    //---------------------------------------------------------------
-    void add_flags(arg_string str) {
-        //empty flags are not allowed
-        str::remove_ws(str);
-        if(!str.empty()) flags_.push_back(std::move(str));
-    }
-
-    //---------------------------------------------------------------
-    void add_flags(const arg_list& strs) {
-        if(strs.empty()) return;
-        flags_.reserve(flags_.size() + strs.size());
-        for(const auto& s : strs) add_flags(s);
-    }
-
-    template<class String1, class String2, class... Strings>
-    void
-    add_flags(String1&& s1, String2&& s2, Strings&&... ss) {
-        flags_.reserve(2 + sizeof...(ss));
-        add_flags(std::forward<String1>(s1));
-        add_flags(std::forward<String2>(s2), std::forward<Strings>(ss)...);
-    }
-
-    arg_list flags_;
-    match_function matcher_;
-    doc_string label_;
-    bool required_ = false;
-};
-
-
-
-
-/*************************************************************************//**
- *
- * @brief makes required non-blocking exact match parameter
- *
- *****************************************************************************/
-template<class String, class... Strings>
-inline parameter
-command(String&& flag, Strings&&... flags)
-{
-    return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
-        .required(true).blocking(true).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes required non-blocking exact match parameter
- *
- *****************************************************************************/
-template<class String, class... Strings>
-inline parameter
-required(String&& flag, Strings&&... flags)
-{
-    return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
-        .required(true).blocking(false).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes optional, non-blocking exact match parameter
- *
- *****************************************************************************/
-template<class String, class... Strings>
-inline parameter
-option(String&& flag, Strings&&... flags)
-{
-    return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
-        .required(false).blocking(false).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes required, blocking, repeatable value parameter;
- *        matches any non-empty string
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-value(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::nonempty}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(true).blocking(true).repeatable(false);
-}
-
-template<class Filter, class... Targets, class = typename std::enable_if<
-    traits::is_callable<Filter,bool(const char*)>::value ||
-    traits::is_callable<Filter,subrange(const char*)>::value>::type>
-inline parameter
-value(Filter&& filter, doc_string label, Targets&&... tgts)
-{
-    return parameter{std::forward<Filter>(filter)}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(true).blocking(true).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes required, blocking, repeatable value parameter;
- *        matches any non-empty string
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-values(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::nonempty}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(true).blocking(true).repeatable(true);
-}
-
-template<class Filter, class... Targets, class = typename std::enable_if<
-    traits::is_callable<Filter,bool(const char*)>::value ||
-    traits::is_callable<Filter,subrange(const char*)>::value>::type>
-inline parameter
-values(Filter&& filter, doc_string label, Targets&&... tgts)
-{
-    return parameter{std::forward<Filter>(filter)}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(true).blocking(true).repeatable(true);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes optional, blocking value parameter;
- *        matches any non-empty string
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-opt_value(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::nonempty}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(false);
-}
-
-template<class Filter, class... Targets, class = typename std::enable_if<
-    traits::is_callable<Filter,bool(const char*)>::value ||
-    traits::is_callable<Filter,subrange(const char*)>::value>::type>
-inline parameter
-opt_value(Filter&& filter, doc_string label, Targets&&... tgts)
-{
-    return parameter{std::forward<Filter>(filter)}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes optional, blocking, repeatable value parameter;
- *        matches any non-empty string
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-opt_values(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::nonempty}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(true);
-}
-
-template<class Filter, class... Targets, class = typename std::enable_if<
-    traits::is_callable<Filter,bool(const char*)>::value ||
-    traits::is_callable<Filter,subrange(const char*)>::value>::type>
-inline parameter
-opt_values(Filter&& filter, doc_string label, Targets&&... tgts)
-{
-    return parameter{std::forward<Filter>(filter)}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(true);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes required, blocking value parameter;
- *        matches any string consisting of alphanumeric characters
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-word(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::alphanumeric}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(true).blocking(true).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes required, blocking, repeatable value parameter;
- *        matches any string consisting of alphanumeric characters
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-words(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::alphanumeric}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(true).blocking(true).repeatable(true);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes optional, blocking value parameter;
- *        matches any string consisting of alphanumeric characters
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-opt_word(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::alphanumeric}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes optional, blocking, repeatable value parameter;
- *        matches any string consisting of alphanumeric characters
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-opt_words(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::alphanumeric}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(true);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes required, blocking value parameter;
- *        matches any string that represents a number
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-number(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::numbers{}}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(true).blocking(true).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes required, blocking, repeatable value parameter;
- *        matches any string that represents a number
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-numbers(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::numbers{}}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(true).blocking(true).repeatable(true);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes optional, blocking value parameter;
- *        matches any string that represents a number
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-opt_number(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::numbers{}}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes optional, blocking, repeatable value parameter;
- *        matches any string that represents a number
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-opt_numbers(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::numbers{}}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(true);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes required, blocking value parameter;
- *        matches any string that represents an integer
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-integer(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::integers{}}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(true).blocking(true).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes required, blocking, repeatable value parameter;
- *        matches any string that represents an integer
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-integers(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::integers{}}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(true).blocking(true).repeatable(true);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes optional, blocking value parameter;
- *        matches any string that represents an integer
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-opt_integer(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::integers{}}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes optional, blocking, repeatable value parameter;
- *        matches any string that represents an integer
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-opt_integers(const doc_string& label, Targets&&... tgts)
-{
-    return parameter{match::integers{}}
-        .label(label)
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(true);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes catch-all value parameter
- *
- *****************************************************************************/
-template<class... Targets>
-inline parameter
-any_other(Targets&&... tgts)
-{
-    return parameter{match::any}
-        .target(std::forward<Targets>(tgts)...)
-        .required(false).blocking(false).repeatable(true);
-}
-
-
-
-
-/*************************************************************************//**
- *
- * @brief group of parameters and/or other groups;
- *        can be configured to act as a group of alternatives (exclusive match)
- *
- *****************************************************************************/
-class group :
-    public detail::token<group>
-{
-    //---------------------------------------------------------------
-    /**
-        * @brief tagged union type that either stores a parameter or a group
-        *        and provides a common interface to them
-        *        could be replaced by std::variant in the future
-        *
-        *        Note to future self: do NOT try again to do this with
-        *        dynamic polymorphism; there are a couple of
-        *        nasty problems associated with it and the implementation
-        *        becomes bloated and needlessly complicated.
-        */
-    template<class Param, class Group>
-    struct child_t {
-        enum class type : char {param, group};
-    public:
-
-        explicit
-        child_t(const Param&  v)          : m_{v},            type_{type::param} {}
-        child_t(      Param&& v) noexcept : m_{std::move(v)}, type_{type::param} {}
-
-        explicit
-        child_t(const Group&  g)          : m_{g},            type_{type::group} {}
-        child_t(      Group&& g) noexcept : m_{std::move(g)}, type_{type::group} {}
-
-        child_t(const child_t& src): type_{src.type_} {
-            switch(type_) {
-                default:
-                case type::param: new(&m_)data{src.m_.param}; break;
-                case type::group: new(&m_)data{src.m_.group}; break;
-            }
-        }
-
-        child_t(child_t&& src) noexcept : type_{src.type_} {
-            switch(type_) {
-                default:
-                case type::param: new(&m_)data{std::move(src.m_.param)}; break;
-                case type::group: new(&m_)data{std::move(src.m_.group)}; break;
-            }
-        }
-
-        child_t& operator = (const child_t& src) {
-            destroy_content();
-            type_ = src.type_;
-            switch(type_) {
-                default:
-                case type::param: new(&m_)data{src.m_.param}; break;
-                case type::group: new(&m_)data{src.m_.group}; break;
-            }
-            return *this;
-        }
-
-        child_t& operator = (child_t&& src) noexcept {
-            destroy_content();
-            type_ = src.type_;
-            switch(type_) {
-                default:
-                case type::param: new(&m_)data{std::move(src.m_.param)}; break;
-                case type::group: new(&m_)data{std::move(src.m_.group)}; break;
-            }
-            return *this;
-        }
-
-        ~child_t() {
-            destroy_content();
-        }
-
-        const doc_string&
-        doc() const noexcept {
-            switch(type_) {
-                default:
-                case type::param: return m_.param.doc();
-                case type::group: return m_.group.doc();
-            }
-        }
-
-        bool blocking() const noexcept {
-            switch(type_) {
-                case type::param: return m_.param.blocking();
-                case type::group: return m_.group.blocking();
-                default: return false;
-            }
-        }
-        bool repeatable() const noexcept {
-            switch(type_) {
-                case type::param: return m_.param.repeatable();
-                case type::group: return m_.group.repeatable();
-                default: return false;
-            }
-        }
-        bool required() const noexcept {
-            switch(type_) {
-                case type::param: return m_.param.required();
-                case type::group:
-                    return (m_.group.exclusive() && m_.group.all_required() ) ||
-                          (!m_.group.exclusive() && m_.group.any_required()  );
-                default: return false;
-            }
-        }
-        bool exclusive() const noexcept {
-            switch(type_) {
-                case type::group: return m_.group.exclusive();
-                case type::param:
-                default: return false;
-            }
-        }
-        std::size_t param_count() const noexcept {
-            switch(type_) {
-                case type::group: return m_.group.param_count();
-                case type::param:
-                default: return std::size_t(1);
-            }
-        }
-        std::size_t depth() const noexcept {
-            switch(type_) {
-                case type::group: return m_.group.depth();
-                case type::param:
-                default: return std::size_t(0);
-            }
-        }
-
-        void execute_actions(const arg_string& arg) const {
-            switch(type_) {
-                default:
-                case type::group: return;
-                case type::param: m_.param.execute_actions(arg); break;
-            }
-
-        }
-
-        void notify_repeated(arg_index idx) const {
-            switch(type_) {
-                default:
-                case type::group: return;
-                case type::param: m_.param.notify_repeated(idx); break;
-            }
-        }
-        void notify_missing(arg_index idx) const {
-            switch(type_) {
-                default:
-                case type::group: return;
-                case type::param: m_.param.notify_missing(idx); break;
-            }
-        }
-        void notify_blocked(arg_index idx) const {
-            switch(type_) {
-                default:
-                case type::group: return;
-                case type::param: m_.param.notify_blocked(idx); break;
-            }
-        }
-        void notify_conflict(arg_index idx) const {
-            switch(type_) {
-                default:
-                case type::group: return;
-                case type::param: m_.param.notify_conflict(idx); break;
-            }
-        }
-
-        bool is_param() const noexcept { return type_ == type::param; }
-        bool is_group() const noexcept { return type_ == type::group; }
-
-        Param& as_param() noexcept { return m_.param; }
-        Group& as_group() noexcept { return m_.group; }
-
-        const Param& as_param() const noexcept { return m_.param; }
-        const Group& as_group() const noexcept { return m_.group; }
-
-    private:
-        void destroy_content() {
-            switch(type_) {
-                default:
-                case type::param: m_.param.~Param(); break;
-                case type::group: m_.group.~Group(); break;
-            }
-        }
-
-        union data {
-            data() {}
-
-            data(const Param&  v)          : param{v} {}
-            data(      Param&& v) noexcept : param{std::move(v)} {}
-
-            data(const Group&  g)          : group{g} {}
-            data(      Group&& g) noexcept : group{std::move(g)} {}
-            ~data() {}
-
-            Param param;
-            Group group;
-        };
-
-        data m_;
-        type type_;
-    };
-
-
-public:
-    //---------------------------------------------------------------
-    using child      = child_t<parameter,group>;
-    using value_type = child;
-
-private:
-    using children_store = std::vector<child>;
-
-public:
-    using const_iterator = children_store::const_iterator;
-    using iterator       = children_store::iterator;
-    using size_type      = children_store::size_type;
-
-
-    //---------------------------------------------------------------
-    /**
-     * @brief recursively iterates over all nodes
-     */
-    class depth_first_traverser
-    {
-    public:
-        //-----------------------------------------------------
-        struct context {
-            context() = default;
-            context(const group& p):
-                parent{&p}, cur{p.begin()}, end{p.end()}
-            {}
-            const group* parent = nullptr;
-            const_iterator cur;
-            const_iterator end;
-        };
-        using context_list = std::vector<context>;
-
-        //-----------------------------------------------------
-        class memento {
-            friend class depth_first_traverser;
-            int level_;
-            context context_;
-        public:
-            int level() const noexcept { return level_; }
-            const child* param() const noexcept { return &(*context_.cur); }
-        };
-
-        depth_first_traverser() = default;
-
-        explicit
-        depth_first_traverser(const group& cur): stack_{} {
-            if(!cur.empty()) stack_.emplace_back(cur);
-        }
-
-        explicit operator bool() const noexcept {
-            return !stack_.empty();
-        }
-
-        int level() const noexcept {
-            return int(stack_.size());
-        }
-
-        bool is_first_in_group() const noexcept {
-            if(stack_.empty()) return false;
-            return (stack_.back().cur == stack_.back().parent->begin());
-        }
-
-        bool is_last_in_group() const noexcept {
-            if(stack_.empty()) return false;
-            return (stack_.back().cur+1 == stack_.back().end);
-        }
-
-        bool is_last_in_path() const noexcept {
-            if(stack_.empty()) return false;
-            for(const auto& t : stack_) {
-                if(t.cur+1 != t.end) return false;
-            }
-            const auto& top = stack_.back();
-            //if we have to descend into group on next ++ => not last in path
-            if(top.cur->is_group()) return false;
-            return true;
-        }
-
-        /** @brief inside a group of alternatives >= minlevel */
-        bool is_alternative(int minlevel = 0) const noexcept {
-            if(stack_.empty()) return false;
-            if(minlevel > 0) minlevel -= 1;
-            if(minlevel >= int(stack_.size())) return false;
-            return std::any_of(stack_.begin() + minlevel, stack_.end(),
-                [](const context& c) { return c.parent->exclusive(); });
-        }
-
-        /** @brief repeatable or inside a repeatable group >= minlevel */
-        bool is_repeatable(int minlevel = 0) const noexcept {
-            if(stack_.empty()) return false;
-            if(stack_.back().cur->repeatable()) return true;
-            if(minlevel > 0) minlevel -= 1;
-            if(minlevel >= int(stack_.size())) return false;
-            return std::any_of(stack_.begin() + minlevel, stack_.end(),
-                [](const context& c) { return c.parent->repeatable(); });
-        }
-        /** @brief inside group with joinable flags */
-        bool joinable() const noexcept {
-            if(stack_.empty()) return false;
-            return std::any_of(stack_.begin(), stack_.end(),
-                [](const context& c) { return c.parent->joinable(); });
-        }
-
-        const context_list&
-        stack() const {
-            return stack_;
-        }
-
-        /** @brief innermost repeat group */
-        const group*
-        repeat_group() const noexcept {
-            auto i = std::find_if(stack_.rbegin(), stack_.rend(),
-                [](const context& c) { return c.parent->repeatable(); });
-
-            return i != stack_.rend() ? i->parent : nullptr;
-        }
-
-        /** @brief outermost join group */
-        const group*
-        join_group() const noexcept {
-            auto i = std::find_if(stack_.begin(), stack_.end(),
-                [](const context& c) { return c.parent->joinable(); });
-            return i != stack_.end() ? i->parent : nullptr;
-        }
-
-        const group* root() const noexcept {
-            return stack_.empty() ? nullptr : stack_.front().parent;
-        }
-
-        /** @brief common flag prefix of all flags in current group */
-        arg_string common_flag_prefix() const noexcept {
-            if(stack_.empty()) return "";
-            auto g = join_group();
-            return g ? g->common_flag_prefix() : arg_string("");
-        }
-
-        const child&
-        operator * () const noexcept {
-            return *stack_.back().cur;
-        }
-
-        const child*
-        operator -> () const noexcept {
-            return &(*stack_.back().cur);
-        }
-
-        const group&
-        parent() const noexcept {
-            return *(stack_.back().parent);
-        }
-
-
-        /** @brief go to next element of depth first search */
-        depth_first_traverser&
-        operator ++ () {
-            if(stack_.empty()) return *this;
-            //at group -> decend into group
-            if(stack_.back().cur->is_group()) {
-                stack_.emplace_back(stack_.back().cur->as_group());
-            }
-            else {
-                next_sibling();
-            }
-            return *this;
-        }
-
-        /** @brief go to next sibling of current */
-        depth_first_traverser&
-        next_sibling() {
-            if(stack_.empty()) return *this;
-            ++stack_.back().cur;
-            //at the end of current group?
-            while(stack_.back().cur == stack_.back().end) {
-                //go to parent
-                stack_.pop_back();
-                if(stack_.empty()) return *this;
-                //go to next sibling in parent
-                ++stack_.back().cur;
-            }
-            return *this;
-        }
-
-        /** @brief go to next position after siblings of current */
-        depth_first_traverser&
-        next_after_siblings() {
-            if(stack_.empty()) return *this;
-            stack_.back().cur = stack_.back().end-1;
-            next_sibling();
-            return *this;
-        }
-
-        /** @brief skips to next alternative in innermost group
-        */
-        depth_first_traverser&
-        next_alternative() {
-            if(stack_.empty()) return *this;
-
-            //find first exclusive group (from the top of the stack!)
-            auto i = std::find_if(stack_.rbegin(), stack_.rend(),
-                [](const context& c) { return c.parent->exclusive(); });
-            if(i == stack_.rend()) return *this;
-
-            stack_.erase(i.base(), stack_.end());
-            next_sibling();
-            return *this;
-        }
-
-        /**
-         * @brief
-         */
-        depth_first_traverser&
-        back_to_parent() {
-            if(stack_.empty()) return *this;
-            stack_.pop_back();
-            return *this;
-        }
-
-        /** @brief don't visit next siblings, go back to parent on next ++
-         *         note: renders siblings unreachable for *this
-         **/
-        depth_first_traverser&
-        skip_siblings() {
-            if(stack_.empty()) return *this;
-            //future increments won't visit subsequent siblings:
-            stack_.back().end = stack_.back().cur+1;
-            return *this;
-        }
-
-        /** @brief skips all other alternatives in surrounding exclusive groups
-         *         on next ++
-         *         note: renders alternatives unreachable for *this
-        */
-        depth_first_traverser&
-        skip_alternatives() {
-            if(stack_.empty()) return *this;
-
-            //exclude all other alternatives in surrounding groups
-            //by making their current position the last one
-            for(auto& c : stack_) {
-                if(c.parent && c.parent->exclusive() && c.cur < c.end)
-                    c.end = c.cur+1;
-            }
-
-            return *this;
-        }
-
-        void invalidate() {
-            stack_.clear();
-        }
-
-        inline friend bool operator == (const depth_first_traverser& a,
-                                        const depth_first_traverser& b)
-        {
-            if(a.stack_.empty() || b.stack_.empty()) return false;
-
-            //parents not the same -> different position
-            if(a.stack_.back().parent != b.stack_.back().parent) return false;
-
-            bool aEnd = a.stack_.back().cur == a.stack_.back().end;
-            bool bEnd = b.stack_.back().cur == b.stack_.back().end;
-            //either both at the end of the same parent => same position
-            if(aEnd && bEnd) return true;
-            //or only one at the end => not at the same position
-            if(aEnd || bEnd) return false;
-            return std::addressof(*a.stack_.back().cur) ==
-                   std::addressof(*b.stack_.back().cur);
-        }
-        inline friend bool operator != (const depth_first_traverser& a,
-                                        const depth_first_traverser& b)
-        {
-            return !(a == b);
-        }
-
-        memento
-        undo_point() const {
-            memento m;
-            m.level_ = int(stack_.size());
-            if(!stack_.empty()) m.context_ = stack_.back();
-            return m;
-        }
-
-        void undo(const memento& m) {
-            if(m.level_ < 1) return;
-            if(m.level_ <= int(stack_.size())) {
-                stack_.erase(stack_.begin() + m.level_, stack_.end());
-                stack_.back() = m.context_;
-            }
-            else if(stack_.empty() && m.level_ == 1) {
-                stack_.push_back(m.context_);
-            }
-        }
-
-    private:
-        context_list stack_;
-    };
-
-
-    //---------------------------------------------------------------
-    group() = default;
-
-    template<class Param, class... Params>
-    explicit
-    group(doc_string docstr, Param param, Params... params):
-        children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
-    {
-        doc(std::move(docstr));
-        push_back(std::move(param), std::move(params)...);
-    }
-
-    template<class... Params>
-    explicit
-    group(parameter param, Params... params):
-        children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
-    {
-        push_back(std::move(param), std::move(params)...);
-    }
-
-    template<class P2, class... Ps>
-    explicit
-    group(group p1, P2 p2, Ps... ps):
-        children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
-    {
-        push_back(std::move(p1), std::move(p2), std::move(ps)...);
-    }
-
-
-    //-----------------------------------------------------
-    group(const group&) = default;
-    group(group&&) = default;
-
-
-    //---------------------------------------------------------------
-    group& operator = (const group&) = default;
-    group& operator = (group&&) = default;
-
-
-    //---------------------------------------------------------------
-    /** @brief determines if a command line argument can be matched by a
-     *         combination of (partial) matches through any number of children
-     */
-    group& joinable(bool yes) {
-        joinable_ = yes;
-        return *this;
-    }
-
-    /** @brief returns if a command line argument can be matched by a
-     *         combination of (partial) matches through any number of children
-     */
-    bool joinable() const noexcept {
-        return joinable_;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief turns explicit scoping on or off
-     *         operators , & | and other combinating functions will
-     *         not merge groups that are marked as scoped
-     */
-    group& scoped(bool yes) {
-        scoped_ = yes;
-        return *this;
-    }
-
-    /** @brief returns true if operators , & | and other combinating functions
-     *         will merge groups and false otherwise
-     */
-    bool scoped() const noexcept
-    {
-        return scoped_;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief determines if children are mutually exclusive alternatives */
-    group& exclusive(bool yes) {
-        exclusive_ = yes;
-        return *this;
-    }
-    /** @brief returns if children are mutually exclusive alternatives */
-    bool exclusive() const noexcept {
-        return exclusive_;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns true, if any child is required to match */
-    bool any_required() const
-    {
-        return std::any_of(children_.begin(), children_.end(),
-            [](const child& n){ return n.required(); });
-    }
-    /** @brief returns true, if all children are required to match */
-    bool all_required() const
-    {
-        return std::all_of(children_.begin(), children_.end(),
-            [](const child& n){ return n.required(); });
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns true if any child is optional (=non-required) */
-    bool any_optional() const {
-        return !all_required();
-    }
-    /** @brief returns true if all children are optional (=non-required) */
-    bool all_optional() const {
-        return !any_required();
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns if the entire group is blocking / positional */
-    bool blocking() const noexcept {
-        return token<group>::blocking() || (exclusive() && all_blocking());
-    }
-    //-----------------------------------------------------
-    /** @brief determines if the entire group is blocking / positional */
-    group& blocking(bool yes) {
-        return token<group>::blocking(yes);
-    }
-
-    //---------------------------------------------------------------
-    /** @brief returns true if any child is blocking */
-    bool any_blocking() const
-    {
-        return std::any_of(children_.begin(), children_.end(),
-            [](const child& n){ return n.blocking(); });
-    }
-    //---------------------------------------------------------------
-    /** @brief returns true if all children is blocking */
-    bool all_blocking() const
-    {
-        return std::all_of(children_.begin(), children_.end(),
-            [](const child& n){ return n.blocking(); });
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns if any child is a value parameter (recursive) */
-    bool any_flagless() const
-    {
-        return std::any_of(children_.begin(), children_.end(),
-            [](const child& p){
-                return p.is_param() && p.as_param().flags().empty();
-            });
-    }
-    /** @brief returns if all children are value parameters (recursive) */
-    bool all_flagless() const
-    {
-        return std::all_of(children_.begin(), children_.end(),
-            [](const child& p){
-                return p.is_param() && p.as_param().flags().empty();
-            });
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief adds child parameter at the end */
-    group&
-    push_back(const parameter& v) {
-        children_.emplace_back(v);
-        return *this;
-    }
-    //-----------------------------------------------------
-    /** @brief adds child parameter at the end */
-    group&
-    push_back(parameter&& v) {
-        children_.emplace_back(std::move(v));
-        return *this;
-    }
-    //-----------------------------------------------------
-    /** @brief adds child group at the end */
-    group&
-    push_back(const group& g) {
-        children_.emplace_back(g);
-        return *this;
-    }
-    //-----------------------------------------------------
-    /** @brief adds child group at the end */
-    group&
-    push_back(group&& g) {
-        children_.emplace_back(std::move(g));
-        return *this;
-    }
-
-
-    //-----------------------------------------------------
-    /** @brief adds children (groups and/or parameters) */
-    template<class Param1, class Param2, class... Params>
-    group&
-    push_back(Param1&& param1, Param2&& param2, Params&&... params)
-    {
-        children_.reserve(children_.size() + 2 + sizeof...(params));
-        push_back(std::forward<Param1>(param1));
-        push_back(std::forward<Param2>(param2), std::forward<Params>(params)...);
-        return *this;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief adds child parameter at the beginning */
-    group&
-    push_front(const parameter& v) {
-        children_.emplace(children_.begin(), v);
-        return *this;
-    }
-    //-----------------------------------------------------
-    /** @brief adds child parameter at the beginning */
-    group&
-    push_front(parameter&& v) {
-        children_.emplace(children_.begin(), std::move(v));
-        return *this;
-    }
-    //-----------------------------------------------------
-    /** @brief adds child group at the beginning */
-    group&
-    push_front(const group& g) {
-        children_.emplace(children_.begin(), g);
-        return *this;
-    }
-    //-----------------------------------------------------
-    /** @brief adds child group at the beginning */
-    group&
-    push_front(group&& g) {
-        children_.emplace(children_.begin(), std::move(g));
-        return *this;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief adds all children of other group at the end */
-    group&
-    merge(group&& g)
-    {
-        children_.insert(children_.end(),
-                      std::make_move_iterator(g.begin()),
-                      std::make_move_iterator(g.end()));
-        return *this;
-    }
-    //-----------------------------------------------------
-    /** @brief adds all children of several other groups at the end */
-    template<class... Groups>
-    group&
-    merge(group&& g1, group&& g2, Groups&&... gs)
-    {
-        merge(std::move(g1));
-        merge(std::move(g2), std::forward<Groups>(gs)...);
-        return *this;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief indexed, nutable access to child */
-    child& operator [] (size_type index) noexcept {
-        return children_[index];
-    }
-    /** @brief indexed, non-nutable access to child */
-    const child& operator [] (size_type index) const noexcept {
-        return children_[index];
-    }
-
-    //---------------------------------------------------------------
-    /** @brief mutable access to first child */
-          child& front()       noexcept { return children_.front(); }
-    /** @brief non-mutable access to first child */
-    const child& front() const noexcept { return children_.front(); }
-    //-----------------------------------------------------
-    /** @brief mutable access to last child */
-          child& back()       noexcept { return children_.back(); }
-    /** @brief non-mutable access to last child */
-    const child& back() const noexcept { return children_.back(); }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns true, if group has no children, false otherwise */
-    bool empty() const noexcept     { return children_.empty(); }
-
-    /** @brief returns number of children */
-    size_type size() const noexcept { return children_.size(); }
-
-    /** @brief returns number of nested levels; 1 for a flat group */
-    size_type depth() const {
-        size_type n = 0;
-        for(const auto& c : children_) {
-            auto l = 1 + c.depth();
-            if(l > n) n = l;
-        }
-        return n;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns mutating iterator to position of first element */
-          iterator  begin()       noexcept { return children_.begin(); }
-    /** @brief returns non-mutating iterator to position of first element */
-    const_iterator  begin() const noexcept { return children_.begin(); }
-    /** @brief returns non-mutating iterator to position of first element */
-    const_iterator cbegin() const noexcept { return children_.begin(); }
-
-    /** @brief returns mutating iterator to position one past the last element */
-          iterator  end()         noexcept { return children_.end(); }
-    /** @brief returns non-mutating iterator to position one past the last element */
-    const_iterator  end()   const noexcept { return children_.end(); }
-    /** @brief returns non-mutating iterator to position one past the last element */
-    const_iterator cend()   const noexcept { return children_.end(); }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns augmented iterator for depth first searches
-     *  @details taverser knows end of iteration and can skip over children
-     */
-    depth_first_traverser
-    begin_dfs() const noexcept {
-        return depth_first_traverser{*this};
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns recursive parameter count */
-    size_type param_count() const {
-        size_type c = 0;
-        for(const auto& n : children_) {
-            c += n.param_count();
-        }
-        return c;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns range of all flags (recursive) */
-    arg_list all_flags() const
-    {
-        std::vector<arg_string> all;
-        gather_flags(children_, all);
-        return all;
-    }
-
-    /** @brief returns true, if no flag occurs as true
-     *         prefix of any other flag (identical flags will be ignored) */
-    bool flags_are_prefix_free() const
-    {
-        const auto fs = all_flags();
-
-        using std::begin; using std::end;
-        for(auto i = begin(fs), e = end(fs); i != e; ++i) {
-            if(!i->empty()) {
-                for(auto j = i+1; j != e; ++j) {
-                    if(!j->empty() && *i != *j) {
-                        if(i->find(*j) == 0) return false;
-                        if(j->find(*i) == 0) return false;
-                    }
-                }
-            }
-        }
-
-        return true;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns longest common prefix of all flags */
-    arg_string common_flag_prefix() const
-    {
-        arg_list prefixes;
-        gather_prefixes(children_, prefixes);
-        return str::longest_common_prefix(prefixes);
-    }
-
-
-private:
-    //---------------------------------------------------------------
-    static void
-    gather_flags(const children_store& nodes, arg_list& all)
-    {
-        for(const auto& p : nodes) {
-            if(p.is_group()) {
-                gather_flags(p.as_group().children_, all);
-            }
-            else {
-                const auto& pf = p.as_param().flags();
-                using std::begin;
-                using std::end;
-                if(!pf.empty()) all.insert(end(all), begin(pf), end(pf));
-            }
-        }
-    }
-    //---------------------------------------------------------------
-    static void
-    gather_prefixes(const children_store& nodes, arg_list& all)
-    {
-        for(const auto& p : nodes) {
-            if(p.is_group()) {
-                gather_prefixes(p.as_group().children_, all);
-            }
-            else if(!p.as_param().flags().empty()) {
-                auto pfx = str::longest_common_prefix(p.as_param().flags());
-                if(!pfx.empty()) all.push_back(std::move(pfx));
-            }
-        }
-    }
-
-    //---------------------------------------------------------------
-    children_store children_;
-    bool exclusive_ = false;
-    bool joinable_ = false;
-    bool scoped_ = false;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief group or parameter
- *
- *****************************************************************************/
-using pattern = group::child;
-
-
-
-/*************************************************************************//**
- *
- * @brief makes a group of parameters and/or groups
- *
- *****************************************************************************/
-inline group
-operator , (parameter a, parameter b)
-{
-    return group{std::move(a), std::move(b)}.scoped(false);
-}
-
-//---------------------------------------------------------
-inline group
-operator , (parameter a, group b)
-{
-    return !b.scoped() && !b.blocking() && !b.exclusive() && !b.repeatable()
-        && !b.joinable() && (b.doc().empty() || b.doc() == a.doc())
-       ? b.push_front(std::move(a))
-       : group{std::move(a), std::move(b)}.scoped(false);
-}
-
-//---------------------------------------------------------
-inline group
-operator , (group a, parameter b)
-{
-    return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
-        && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
-       ? a.push_back(std::move(b))
-       : group{std::move(a), std::move(b)}.scoped(false);
-}
-
-//---------------------------------------------------------
-inline group
-operator , (group a, group b)
-{
-    return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
-        && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
-       ? a.push_back(std::move(b))
-       : group{std::move(a), std::move(b)}.scoped(false);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes a group of alternative parameters or groups
- *
- *****************************************************************************/
-template<class Param, class... Params>
-inline group
-one_of(Param param, Params... params)
-{
-    return group{std::move(param), std::move(params)...}.exclusive(true);
-}
-
-
-/*************************************************************************//**
- *
- * @brief makes a group of alternative parameters or groups
- *
- *****************************************************************************/
-inline group
-operator | (parameter a, parameter b)
-{
-    return group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
-}
-
-//-------------------------------------------------------------------
-inline group
-operator | (parameter a, group b)
-{
-    return !b.scoped() && !b.blocking() && b.exclusive() && !b.repeatable()
-        && !b.joinable()
-        && (b.doc().empty() || b.doc() == a.doc())
-        ? b.push_front(std::move(a))
-        : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
-}
-
-//-------------------------------------------------------------------
-inline group
-operator | (group a, parameter b)
-{
-    return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable()
-        && a.blocking() == b.blocking()
-        && (a.doc().empty() || a.doc() == b.doc())
-        ? a.push_back(std::move(b))
-        : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
-}
-
-inline group
-operator | (group a, group b)
-{
-    return !a.scoped() && a.exclusive() &&!a.repeatable() && !a.joinable()
-        && a.blocking() == b.blocking()
-        && (a.doc().empty() || a.doc() == b.doc())
-        ? a.push_back(std::move(b))
-        : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
-}
-
-
-
-namespace detail {
-
-inline void set_blocking(bool) {}
-
-template<class P, class... Ps>
-void set_blocking(bool yes, P& p, Ps&... ps) {
-    p.blocking(yes);
-    set_blocking(yes, ps...);
-}
-
-} // namespace detail
-
-
-/*************************************************************************//**
- *
- * @brief makes a parameter/group sequence by making all input objects blocking
- *
- *****************************************************************************/
-template<class Param, class... Params>
-inline group
-in_sequence(Param param, Params... params)
-{
-    detail::set_blocking(true, param, params...);
-    return group{std::move(param), std::move(params)...}.scoped(true);
-}
-
-
-/*************************************************************************//**
- *
- * @brief makes a parameter/group sequence by making all input objects blocking
- *
- *****************************************************************************/
-inline group
-operator & (parameter a, parameter b)
-{
-    a.blocking(true);
-    b.blocking(true);
-    return group{std::move(a), std::move(b)}.scoped(true);
-}
-
-//---------------------------------------------------------
-inline group
-operator & (parameter a, group b)
-{
-    a.blocking(true);
-    return group{std::move(a), std::move(b)}.scoped(true);
-}
-
-//---------------------------------------------------------
-inline group
-operator & (group a, parameter b)
-{
-    b.blocking(true);
-    if(a.all_blocking() && !a.exclusive() && !a.repeatable() && !a.joinable()
-        && (a.doc().empty() || a.doc() == b.doc()))
-    {
-        return a.push_back(std::move(b));
-    }
-    else {
-        if(!a.all_blocking()) a.blocking(true);
-        return group{std::move(a), std::move(b)}.scoped(true);
-    }
-}
-
-inline group
-operator & (group a, group b)
-{
-    if(!b.all_blocking()) b.blocking(true);
-    if(a.all_blocking() && !a.exclusive() && !a.repeatable()
-        && !a.joinable() && (a.doc().empty() || a.doc() == b.doc()))
-    {
-        return a.push_back(std::move(b));
-    }
-    else {
-        if(!a.all_blocking()) a.blocking(true);
-        return group{std::move(a), std::move(b)}.scoped(true);
-    }
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes a group of parameters and/or groups
- *        where all single char flag params ("-a", "b", ...) are joinable
- *
- *****************************************************************************/
-inline group&
-joinable(group& param) {
-    return param.joinable(true);
-}
-
-inline group&&
-joinable(group&& param) {
-    return std::move(param.joinable(true));
-}
-
-//-------------------------------------------------------------------
-template<class... Params>
-inline group
-joinable(parameter param, Params... params)
-{
-    return group{std::move(param), std::move(params)...}.joinable(true);
-}
-
-template<class P2, class... Ps>
-inline group
-joinable(group p1, P2 p2, Ps... ps)
-{
-    return group{std::move(p1), std::move(p2), std::move(ps)...}.joinable(true);
-}
-
-template<class Param, class... Params>
-inline group
-joinable(doc_string docstr, Param param, Params... params)
-{
-    return group{std::move(param), std::move(params)...}
-                .joinable(true).doc(std::move(docstr));
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes a repeatable copy of a parameter
- *
- *****************************************************************************/
-inline parameter
-repeatable(parameter p) {
-    return p.repeatable(true);
-}
-
-/*************************************************************************//**
- *
- * @brief makes a repeatable copy of a group
- *
- *****************************************************************************/
-inline group
-repeatable(group g) {
-    return g.repeatable(true);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief makes a group of parameters and/or groups
- *        that is repeatable as a whole
- *        Note that a repeatable group consisting entirely of non-blocking
- *        children is equivalent to a non-repeatable group of
- *        repeatable children.
- *
- *****************************************************************************/
-template<class P2, class... Ps>
-inline group
-repeatable(parameter p1, P2 p2, Ps... ps)
-{
-    return group{std::move(p1), std::move(p2),
-                 std::move(ps)...}.repeatable(true);
-}
-
-template<class P2, class... Ps>
-inline group
-repeatable(group p1, P2 p2, Ps... ps)
-{
-    return group{std::move(p1), std::move(p2),
-                 std::move(ps)...}.repeatable(true);
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief recursively prepends a prefix to all flags
- *
- *****************************************************************************/
-inline parameter&&
-with_prefix(const arg_string& prefix, parameter&& p) {
-    return std::move(with_prefix(prefix, p));
-}
-
-
-//-------------------------------------------------------------------
-inline group&
-with_prefix(const arg_string& prefix, group& params)
-{
-    for(auto& p : params) {
-        if(p.is_group()) {
-            with_prefix(prefix, p.as_group());
-        } else {
-            with_prefix(prefix, p.as_param());
-        }
-    }
-    return params;
-}
-
-
-inline group&&
-with_prefix(const arg_string& prefix, group&& params)
-{
-    return std::move(with_prefix(prefix, params));
-}
-
-
-template<class Param, class... Params>
-inline group
-with_prefix(arg_string prefix, Param&& param, Params&&... params)
-{
-    return with_prefix(prefix, group{std::forward<Param>(param),
-                                     std::forward<Params>(params)...});
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief recursively prepends a prefix to all flags
- *
- * @param shortpfx : used for single-letter flags
- * @param longpfx  : used for flags with length > 1
- *
- *****************************************************************************/
-inline parameter&&
-with_prefixes_short_long(const arg_string& shortpfx, const arg_string& longpfx,
-                         parameter&& p)
-{
-    return std::move(with_prefixes_short_long(shortpfx, longpfx, p));
-}
-
-
-//-------------------------------------------------------------------
-inline group&
-with_prefixes_short_long(const arg_string& shortFlagPrefix,
-                         const arg_string& longFlagPrefix,
-                         group& params)
-{
-    for(auto& p : params) {
-        if(p.is_group()) {
-            with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_group());
-        } else {
-            with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_param());
-        }
-    }
-    return params;
-}
-
-
-inline group&&
-with_prefixes_short_long(const arg_string& shortFlagPrefix,
-                         const arg_string& longFlagPrefix,
-                         group&& params)
-{
-    return std::move(with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
-                                              params));
-}
-
-
-template<class Param, class... Params>
-inline group
-with_prefixes_short_long(const arg_string& shortFlagPrefix,
-                         const arg_string& longFlagPrefix,
-                         Param&& param, Params&&... params)
-{
-    return with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
-                                    group{std::forward<Param>(param),
-                                          std::forward<Params>(params)...});
-}
-
-
-
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief parsing implementation details
- *
- *****************************************************************************/
-namespace detail {
-
-
-/*************************************************************************//**
- *
- * @brief DFS traverser that keeps track of 'scopes'
- *        scope = all parameters that are either bounded by
- *        two blocking parameters on the same depth level
- *        or the beginning/end of the outermost group
- *
- *****************************************************************************/
-class scoped_dfs_traverser
-{
-public:
-    using dfs_traverser = group::depth_first_traverser;
-
-    scoped_dfs_traverser() = default;
-
-    explicit
-    scoped_dfs_traverser(const group& g):
-        pos_{g}, lastMatch_{}, posAfterLastMatch_{}, scopes_{},
-        curMatched_{false}, ignoreBlocks_{false},
-        repeatGroupStarted_{false}, repeatGroupContinues_{false}
-    {}
-
-    const dfs_traverser& base() const noexcept { return pos_; }
-    const dfs_traverser& last_match() const noexcept { return lastMatch_; }
-
-    const group& parent() const noexcept { return pos_.parent(); }
-    const group* repeat_group() const noexcept { return pos_.repeat_group(); }
-    const group* join_group() const noexcept { return pos_.join_group(); }
-
-    const pattern* operator ->() const noexcept { return pos_.operator->(); }
-    const pattern& operator *() const noexcept { return *pos_; }
-
-    const pattern* ptr() const noexcept { return pos_.operator->(); }
-
-    explicit operator bool() const noexcept { return bool(pos_); }
-
-    bool joinable() const noexcept { return pos_.joinable(); }
-    arg_string common_flag_prefix() const { return pos_.common_flag_prefix(); }
-
-    void ignore_blocking(bool yes) { ignoreBlocks_ = yes; }
-
-    void invalidate() { pos_.invalidate(); curMatched_ = false; }
-    bool matched() const noexcept { return curMatched_; }
-
-    bool start_of_repeat_group() const noexcept { return repeatGroupStarted_; }
-
-    //-----------------------------------------------------
-    scoped_dfs_traverser&
-    next_sibling() { pos_.next_sibling(); return *this; }
-
-    scoped_dfs_traverser&
-    next_alternative() { pos_.next_alternative(); return *this; }
-
-    scoped_dfs_traverser&
-    next_after_siblings() { pos_.next_after_siblings(); return *this; }
-
-    //-----------------------------------------------------
-    scoped_dfs_traverser&
-    operator ++ ()
-    {
-        if(!pos_) return *this;
-
-        if(pos_.is_last_in_path()) {
-            return_to_outermost_scope();
-            return *this;
-        }
-
-        //current pattern can block if it didn't match already
-        if(!ignoreBlocks_ && !matched()) {
-            //current group can block if we didn't have any match in it
-            if(pos_.is_last_in_group() && pos_.parent().blocking()
-                && (!posAfterLastMatch_ || &(posAfterLastMatch_.parent()) != &(pos_.parent())))
-            {
-                //ascend to parent's level
-                ++pos_;
-                //skip all siblings of parent group
-                pos_.next_after_siblings();
-                if(!pos_) return_to_outermost_scope();
-            }
-            else if(pos_->blocking() && !pos_->is_group()) {
-                if(pos_.parent().exclusive()) { //is_alternative(pos_.level())) {
-                    pos_.next_alternative();
-                } else {
-                    //no match => skip siblings of blocking param
-                    pos_.next_after_siblings();
-                }
-                if(!pos_) return_to_outermost_scope();
-            } else {
-                ++pos_;
-            }
-        } else {
-            ++pos_;
-        }
-        check_left_scope();
-        return *this;
-    }
-
-    //-----------------------------------------------------
-    void next_after_match(scoped_dfs_traverser match)
-    {
-        if(!match || ignoreBlocks_) return;
-
-        check_repeat_group_start(match);
-
-        lastMatch_ = match.base();
-
-        if(!match->blocking() && match.base().parent().blocking()) {
-            match.pos_.back_to_parent();
-        }
-
-        //if match is not in current position & current position is blocking
-        //=> current position has to be advanced by one so that it is
-        //no longer reachable within current scope
-        //(can happen for repeatable, blocking parameters)
-        if(match.base() != pos_ && pos_->blocking()) pos_.next_sibling();
-
-        if(match->blocking()) {
-            if(match.pos_.is_alternative()) {
-                //discard other alternatives
-                match.pos_.skip_alternatives();
-            }
-
-            if(is_last_in_current_scope(match.pos_)) {
-                //if current param is not repeatable -> back to previous scope
-                if(!match->repeatable() && !match->is_group()) {
-                    curMatched_ = false;
-                    pos_ = std::move(match.pos_);
-                    if(!scopes_.empty()) pos_.undo(scopes_.top());
-                }
-                else { //stay at match position
-                    curMatched_ = true;
-                    pos_ = std::move(match.pos_);
-                }
-            }
-            else { //not last in current group
-                //if current param is not repeatable, go directly to next
-                if(!match->repeatable() && !match->is_group()) {
-                    curMatched_ = false;
-                    ++match.pos_;
-                } else {
-                    curMatched_ = true;
-                }
-
-                if(match.pos_.level() > pos_.level()) {
-                    scopes_.push(pos_.undo_point());
-                    pos_ = std::move(match.pos_);
-                }
-                else if(match.pos_.level() < pos_.level()) {
-                    return_to_level(match.pos_.level());
-                }
-                else {
-                    pos_ = std::move(match.pos_);
-                }
-            }
-            posAfterLastMatch_ = pos_;
-        }
-        else {
-            if(match.pos_.level() < pos_.level()) {
-                return_to_level(match.pos_.level());
-            }
-            posAfterLastMatch_ = pos_;
-        }
-        repeatGroupContinues_ = repeat_group_continues();
-    }
-
-private:
-    //-----------------------------------------------------
-    bool is_last_in_current_scope(const dfs_traverser& pos)
-    {
-        if(scopes_.empty()) return pos.is_last_in_path();
-        //check if we would leave the current scope on ++
-        auto p = pos;
-        ++p;
-        return p.level() < scopes_.top().level();
-    }
-
-    //-----------------------------------------------------
-    void check_repeat_group_start(const scoped_dfs_traverser& newMatch)
-    {
-        const auto newrg = newMatch.repeat_group();
-        if(!newrg) {
-            repeatGroupStarted_ = false;
-        }
-        else if(lastMatch_.repeat_group() != newrg) {
-            repeatGroupStarted_ = true;
-        }
-        else if(!repeatGroupContinues_ || !newMatch.repeatGroupContinues_) {
-            repeatGroupStarted_ = true;
-        }
-        else {
-            //special case: repeat group is outermost group
-            //=> we can never really 'leave' and 'reenter' it
-            //but if the current scope is the first element, then we are
-            //conceptually at a position 'before' the group
-            repeatGroupStarted_ = scopes_.empty() || (
-                    newrg == pos_.root() &&
-                    scopes_.top().param() == &(*pos_.root()->begin()) );
-        }
-        repeatGroupContinues_ = repeatGroupStarted_;
-    }
-
-    //-----------------------------------------------------
-    bool repeat_group_continues()
-    {
-        if(!repeatGroupContinues_) return false;
-        const auto curRepGroup = pos_.repeat_group();
-        if(!curRepGroup) return false;
-        if(curRepGroup != lastMatch_.repeat_group()) return false;
-        if(!posAfterLastMatch_) return false;
-        return true;
-    }
-
-    //-----------------------------------------------------
-    void check_left_scope()
-    {
-        if(posAfterLastMatch_) {
-            if(pos_.level() < posAfterLastMatch_.level()) {
-                while(!scopes_.empty() && scopes_.top().level() >= pos_.level()) {
-                    pos_.undo(scopes_.top());
-                    scopes_.pop();
-                }
-                posAfterLastMatch_.invalidate();
-            }
-        }
-        while(!scopes_.empty() && scopes_.top().level() > pos_.level()) {
-            pos_.undo(scopes_.top());
-            scopes_.pop();
-        }
-        repeatGroupContinues_ = repeat_group_continues();
-    }
-
-    //-----------------------------------------------------
-    void return_to_outermost_scope()
-    {
-        posAfterLastMatch_.invalidate();
-
-        if(scopes_.empty()) {
-            pos_.invalidate();
-            repeatGroupContinues_ = false;
-            return;
-        }
-
-        while(!scopes_.empty() && (!pos_ || pos_.level() >= 1)) {
-            pos_.undo(scopes_.top());
-            scopes_.pop();
-        }
-        while(!scopes_.empty()) scopes_.pop();
-
-        repeatGroupContinues_ = repeat_group_continues();
-    }
-
-    //-----------------------------------------------------
-    void return_to_level(int level)
-    {
-        if(pos_.level() <= level) return;
-        while(!scopes_.empty() && pos_.level() > level) {
-            pos_.undo(scopes_.top());
-            scopes_.pop();
-        }
-    };
-
-    dfs_traverser pos_;
-    dfs_traverser lastMatch_;
-    dfs_traverser posAfterLastMatch_;
-    std::stack<dfs_traverser::memento> scopes_;
-    bool curMatched_ = false;
-    bool ignoreBlocks_ = false;
-    bool repeatGroupStarted_ = false;
-    bool repeatGroupContinues_ = false;
-};
-
-
-
-
-/*****************************************************************************
- *
- * some parameter property predicates
- *
- *****************************************************************************/
-struct select_all {
-    bool operator () (const parameter&) const noexcept { return true; }
-};
-
-struct select_flags {
-    bool operator () (const parameter& p) const noexcept {
-        return !p.flags().empty();
-    }
-};
-
-struct select_values {
-    bool operator () (const parameter& p) const noexcept {
-        return p.flags().empty();
-    }
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief result of a matching operation
- *
- *****************************************************************************/
-class match_t {
-public:
-    match_t() = default;
-    match_t(arg_string s, scoped_dfs_traverser p):
-        str_{std::move(s)}, pos_{std::move(p)}
-    {}
-
-    const arg_string& str() const noexcept { return str_; }
-    const scoped_dfs_traverser& pos() const noexcept { return pos_; }
-
-    explicit operator bool() const noexcept { return !str_.empty(); }
-
-private:
-    arg_string str_;
-    scoped_dfs_traverser pos_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief finds the first parameter that matches a given string
- *        candidate parameters are traversed using a scoped DFS traverser
- *
- *****************************************************************************/
-template<class Predicate>
-match_t
-full_match(scoped_dfs_traverser pos, const arg_string& arg,
-           const Predicate& select)
-{
-    if(arg.empty()) return match_t{};
-
-    while(pos) {
-        if(pos->is_param()) {
-            const auto& param = pos->as_param();
-            if(select(param)) {
-                const auto match = param.match(arg);
-                if(match && match.length() == arg.size()) {
-                    return match_t{arg, std::move(pos)};
-                }
-            }
-        }
-        ++pos;
-    }
-    return match_t{};
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief finds the first parameter that matches any (non-empty) prefix
- *        of a given string;
- *        candidate parameters are traversed using a scoped DFS traverser
- *
- *****************************************************************************/
-template<class Predicate>
-match_t
-prefix_match(scoped_dfs_traverser pos, const arg_string& arg,
-             const Predicate& select)
-{
-    if(arg.empty()) return match_t{};
-
-    while(pos) {
-        if(pos->is_param()) {
-            const auto& param = pos->as_param();
-            if(select(param)) {
-                const auto match = param.match(arg);
-                if(match.prefix()) {
-                    if(match.length() == arg.size()) {
-                        return match_t{arg, std::move(pos)};
-                    }
-                    else {
-                        return match_t{arg.substr(match.at(), match.length()),
-                                       std::move(pos)};
-                    }
-                }
-            }
-        }
-        ++pos;
-    }
-    return match_t{};
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief finds the first parameter that partially matches a given string;
- *        candidate parameters are traversed using a scoped DFS traverser
- *
- *****************************************************************************/
-template<class Predicate>
-match_t
-partial_match(scoped_dfs_traverser pos, const arg_string& arg,
-              const Predicate& select)
-{
-    if(arg.empty()) return match_t{};
-
-    while(pos) {
-        if(pos->is_param()) {
-            const auto& param = pos->as_param();
-            if(select(param)) {
-                const auto match = param.match(arg);
-                if(match) {
-                    return match_t{arg.substr(match.at(), match.length()),
-                                   std::move(pos)};
-                }
-            }
-        }
-        ++pos;
-    }
-    return match_t{};
-}
-
-} //namespace detail
-
-
-
-
-
-
-/***************************************************************//**
- *
- * @brief default command line arguments parser
- *
- *******************************************************************/
-class parser
-{
-public:
-    using dfs_traverser = group::depth_first_traverser;
-    using scoped_dfs_traverser = detail::scoped_dfs_traverser;
-
-
-    /*****************************************************//**
-     * @brief arg -> parameter mapping
-     *********************************************************/
-    class arg_mapping {
-    public:
-        friend class parser;
-
-        explicit
-        arg_mapping(arg_index idx, arg_string s,
-                    const dfs_traverser& match)
-        :
-            index_{idx}, arg_{std::move(s)}, match_{match},
-            repeat_{0}, startsRepeatGroup_{false},
-            blocked_{false}, conflict_{false}
-        {}
-
-        explicit
-        arg_mapping(arg_index idx, arg_string s) :
-            index_{idx}, arg_{std::move(s)}, match_{},
-            repeat_{0}, startsRepeatGroup_{false},
-            blocked_{false}, conflict_{false}
-        {}
-
-        arg_index index() const noexcept { return index_; }
-        const arg_string& arg() const noexcept { return arg_; }
-
-        const parameter* param() const noexcept {
-            return match_ && match_->is_param()
-                ? &(match_->as_param()) : nullptr;
-        }
-
-        std::size_t repeat() const noexcept { return repeat_; }
-
-        bool blocked() const noexcept { return blocked_; }
-        bool conflict() const noexcept { return conflict_; }
-
-        bool bad_repeat() const noexcept {
-            if(!param()) return false;
-            return repeat_ > 0 && !param()->repeatable()
-                && !match_.repeat_group();
-        }
-
-        bool any_error() const noexcept {
-            return !match_ || blocked() || conflict() || bad_repeat();
-        }
-
-    private:
-        arg_index index_;
-        arg_string arg_;
-        dfs_traverser match_;
-        std::size_t repeat_;
-        bool startsRepeatGroup_;
-        bool blocked_;
-        bool conflict_;
-    };
-
-    /*****************************************************//**
-     * @brief references a non-matched, required parameter
-     *********************************************************/
-    class missing_event {
-    public:
-        explicit
-        missing_event(const parameter* p, arg_index after):
-            param_{p}, aftIndex_{after}
-        {}
-
-        const parameter* param() const noexcept { return param_; }
-
-        arg_index after_index() const noexcept { return aftIndex_; }
-
-    private:
-        const parameter* param_;
-        arg_index aftIndex_;
-    };
-
-    //-----------------------------------------------------
-    using missing_events = std::vector<missing_event>;
-    using arg_mappings   = std::vector<arg_mapping>;
-
-
-private:
-    struct miss_candidate {
-        miss_candidate(dfs_traverser p, arg_index idx,
-                       bool firstInRepeatGroup = false):
-            pos{std::move(p)}, index{idx},
-            startsRepeatGroup{firstInRepeatGroup}
-        {}
-
-        dfs_traverser pos;
-        arg_index index;
-        bool startsRepeatGroup;
-    };
-    using miss_candidates = std::vector<miss_candidate>;
-
-
-public:
-    //---------------------------------------------------------------
-    /** @brief initializes parser with a command line interface
-     *  @param offset = argument index offset used for reports
-     * */
-    explicit
-    parser(const group& root, arg_index offset = 0):
-        root_{&root}, pos_{root},
-        index_{offset-1}, eaten_{0},
-        args_{}, missCand_{}, blocked_{false}
-    {
-        for_each_potential_miss(dfs_traverser{root},
-            [this](const dfs_traverser& p){
-                missCand_.emplace_back(p, index_);
-            });
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief processes one command line argument */
-    bool operator() (const arg_string& arg)
-    {
-        ++eaten_;
-        ++index_;
-
-        if(!valid() || arg.empty()) return false;
-
-        if(!blocked_ && try_match(arg)) return true;
-
-        if(try_match_blocked(arg)) return false;
-
-        //skipping of blocking & required patterns is not allowed
-        if(!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) {
-            blocked_ = true;
-            return false;
-        }
-
-        add_nomatch(arg);
-        return false;
-    }
-
-
-    //---------------------------------------------------------------
-    /** @brief returns range of argument -> parameter mappings */
-    const arg_mappings& args() const {
-        return args_;
-    }
-
-    /** @brief returns list of missing events */
-    missing_events missed() const {
-        missing_events misses;
-        misses.reserve(missCand_.size());
-        for(auto i = missCand_.begin(); i != missCand_.end(); ++i) {
-            misses.emplace_back(&(i->pos->as_param()), i->index);
-        }
-        return misses;
-    }
-
-    /** @brief returns number of processed command line arguments */
-    arg_index parse_count() const noexcept { return eaten_; }
-
-    /** @brief returns false if previously processed command line arguments
-     *         lead to an invalid / inconsistent parsing result
-     */
-    bool valid() const noexcept { return bool(pos_); }
-
-    /** @brief returns false if previously processed command line arguments
-     *         lead to an invalid / inconsistent parsing result
-     */
-    explicit operator bool() const noexcept { return valid(); }
-
-
-private:
-    //---------------------------------------------------------------
-    using match_t = detail::match_t;
-
-
-    //---------------------------------------------------------------
-    /** @brief try to match argument with unreachable parameter */
-    bool try_match_blocked(const arg_string& arg)
-    {
-        //try to match ahead (using temporary parser)
-        if(pos_) {
-            auto ahead = *this;
-            if(try_match_blocked(std::move(ahead), arg)) return true;
-        }
-
-        //try to match from the beginning (using temporary parser)
-        if(root_) {
-            parser all{*root_, index_+1};
-            if(try_match_blocked(std::move(all), arg)) return true;
-        }
-
-        return false;
-    }
-
-    //---------------------------------------------------------------
-    bool try_match_blocked(parser&& parse, const arg_string& arg)
-    {
-        const auto nold = int(parse.args_.size());
-
-        parse.pos_.ignore_blocking(true);
-
-        if(!parse.try_match(arg)) return false;
-
-        for(auto i = parse.args_.begin() + nold; i != parse.args_.end(); ++i) {
-            args_.push_back(*i);
-            args_.back().blocked_ = true;
-        }
-        return true;
-    }
-
-    //---------------------------------------------------------------
-    /** @brief try to find a parameter/pattern that matches 'arg' */
-    bool try_match(const arg_string& arg)
-    {
-        //Note: flag-params will always take precedence over value-params
-        if(try_match_full(arg, detail::select_flags{})) return true;
-        if(try_match_joined_flags(arg)) return true;
-        if(try_match_joined_sequence(arg, detail::select_flags{})) return true;
-        if(try_match_full(arg, detail::select_values{})) return true;
-        if(try_match_joined_sequence(arg, detail::select_all{})) return true;
-        if(try_match_joined_params(arg)) return true;
-        return false;
-    }
-
-    //---------------------------------------------------------------
-    template<class Predicate>
-    bool try_match_full(const arg_string& arg, const Predicate& select)
-    {
-        auto match = detail::full_match(pos_, arg, select);
-
-        if(!match) return false;
-
-        add_match(match);
-        return true;
-    }
-
-    //---------------------------------------------------------------
-    template<class Predicate>
-    bool try_match_joined_sequence(arg_string arg, const Predicate& acceptFirst)
-    {
-        auto fstMatch = detail::prefix_match(pos_, arg, acceptFirst);
-
-        if(!fstMatch) return false;
-
-        if(fstMatch.str().size() == arg.size()) {
-            add_match(fstMatch);
-            return true;
-        }
-
-        if(!fstMatch.pos()->blocking()) return false;
-
-        auto pos = fstMatch.pos();
-        pos.ignore_blocking(true);
-        const auto parent = &pos.parent();
-        if(!pos->repeatable()) ++pos;
-
-        arg.erase(0, fstMatch.str().size());
-        std::vector<match_t> matches { std::move(fstMatch) };
-
-        while(!arg.empty() && pos &&
-              pos->blocking() && pos->is_param() &&
-              (&pos.parent() == parent))
-        {
-            auto match = pos->as_param().match(arg);
-
-            if(match.prefix()) {
-                matches.emplace_back(arg.substr(0,match.length()), pos);
-                arg.erase(0, match.length());
-                if(!pos->repeatable()) ++pos;
-            }
-            else {
-                if(!pos->repeatable()) return false;
-                ++pos;
-            }
-
-        }
-
-        if(!arg.empty() || matches.empty()) return false;
-
-        for(const auto& m : matches) add_match(m);
-        return true;
-    }
-
-    //-----------------------------------------------------
-    bool try_match_joined_flags(const arg_string& arg)
-    {
-        return try_match_joined([&](const group& g) {
-            if(try_match_joined(g, arg, detail::select_flags{},
-                                g.common_flag_prefix()) )
-            {
-                return true;
-            }
-            return false;
-        });
-    }
-
-    //---------------------------------------------------------------
-    bool try_match_joined_params(const arg_string& arg)
-    {
-        return try_match_joined([&](const group& g) {
-            if(try_match_joined(g, arg, detail::select_all{}) ) {
-                return true;
-            }
-            return false;
-        });
-    }
-
-    //-----------------------------------------------------
-    template<class Predicate>
-    bool try_match_joined(const group& joinGroup, arg_string arg,
-                          const Predicate& pred,
-                          const arg_string& prefix = "")
-    {
-        parser parse {joinGroup};
-        std::vector<match_t> matches;
-
-        while(!arg.empty()) {
-            auto match = detail::prefix_match(parse.pos_, arg, pred);
-
-            if(!match) return false;
-
-            arg.erase(0, match.str().size());
-            //make sure prefix is always present after the first match
-            //ensures that, e.g., flags "-a" and "-b" will be found in "-ab"
-            if(!arg.empty() && !prefix.empty() && arg.find(prefix) != 0 &&
-                prefix != match.str())
-            {
-                arg.insert(0,prefix);
-            }
-
-            parse.add_match(match);
-            matches.push_back(std::move(match));
-        }
-
-        if(!arg.empty() || matches.empty()) return false;
-
-        if(!parse.missCand_.empty()) return false;
-        for(const auto& a : parse.args_) if(a.any_error()) return false;
-
-        //replay matches onto *this
-        for(const auto& m : matches) add_match(m);
-        return true;
-    }
-
-    //-----------------------------------------------------
-    template<class Predicate>
-    bool try_match_joined(const Predicate& pred)
-    {
-        if(pos_ && pos_.parent().joinable()) {
-            const auto& g = pos_.parent();
-            if(pred(g)) return true;
-            return false;
-        }
-
-        auto pos = pos_;
-        while(pos) {
-            if(pos->is_group() && pos->as_group().joinable()) {
-                const auto& g = pos->as_group();
-                if(pred(g)) return true;
-                pos.next_sibling();
-            }
-            else {
-                ++pos;
-            }
-        }
-        return false;
-    }
-
-
-    //---------------------------------------------------------------
-    void add_nomatch(const arg_string& arg) {
-        args_.emplace_back(index_, arg);
-    }
-
-
-    //---------------------------------------------------------------
-    void add_match(const match_t& match)
-    {
-        const auto& pos = match.pos();
-        if(!pos || !pos->is_param() || match.str().empty()) return;
-
-        pos_.next_after_match(pos);
-
-        arg_mapping newArg{index_, match.str(), pos.base()};
-        newArg.repeat_ = occurrences_of(&pos->as_param());
-        newArg.conflict_ = check_conflicts(pos.base());
-        newArg.startsRepeatGroup_ = pos_.start_of_repeat_group();
-        args_.push_back(std::move(newArg));
-
-        add_miss_candidates_after(pos);
-        clean_miss_candidates_for(pos.base());
-        discard_alternative_miss_candidates(pos.base());
-
-    }
-
-    //-----------------------------------------------------
-    bool check_conflicts(const dfs_traverser& match)
-    {
-        if(pos_.start_of_repeat_group()) return false;
-        bool conflict = false;
-        for(const auto& m : match.stack()) {
-            if(m.parent->exclusive()) {
-                for(auto i = args_.rbegin(); i != args_.rend(); ++i) {
-                    if(!i->blocked()) {
-                        for(const auto& c : i->match_.stack()) {
-                            //sibling within same exclusive group => conflict
-                            if(c.parent == m.parent && c.cur != m.cur) {
-                                conflict = true;
-                                i->conflict_ = true;
-                            }
-                        }
-                    }
-                    //check for conflicts only within current repeat cycle
-                    if(i->startsRepeatGroup_) break;
-                }
-            }
-        }
-        return conflict;
-    }
-
-    //-----------------------------------------------------
-    void clean_miss_candidates_for(const dfs_traverser& match)
-    {
-        auto i = std::find_if(missCand_.rbegin(), missCand_.rend(),
-            [&](const miss_candidate& m) {
-                return &(*m.pos) == &(*match);
-            });
-
-        if(i != missCand_.rend()) {
-            missCand_.erase(prev(i.base()));
-        }
-    }
-
-    //-----------------------------------------------------
-    void discard_alternative_miss_candidates(const dfs_traverser& match)
-    {
-        if(missCand_.empty()) return;
-        //find out, if miss candidate is sibling of one of the same
-        //alternative groups that the current match is a member of
-        //if so, we can discard the miss
-
-        //go through all exclusive groups of matching pattern
-        for(const auto& m : match.stack()) {
-            if(m.parent->exclusive()) {
-                for(auto i = int(missCand_.size())-1; i >= 0; --i) {
-                    bool removed = false;
-                    for(const auto& c : missCand_[i].pos.stack()) {
-                        //sibling within same exclusive group => discard
-                        if(c.parent == m.parent && c.cur != m.cur) {
-                            missCand_.erase(missCand_.begin() + i);
-                            if(missCand_.empty()) return;
-                            removed = true;
-                            break;
-                        }
-                    }
-                    //remove miss candidates only within current repeat cycle
-                    if(i > 0 && removed) {
-                        if(missCand_[i-1].startsRepeatGroup) break;
-                    } else {
-                        if(missCand_[i].startsRepeatGroup) break;
-                    }
-                }
-            }
-        }
-    }
-
-    //-----------------------------------------------------
-    void add_miss_candidates_after(const scoped_dfs_traverser& match)
-    {
-        auto npos = match.base();
-        if(npos.is_alternative()) npos.skip_alternatives();
-        ++npos;
-        //need to add potential misses if:
-        //either new repeat group was started
-        const auto newRepGroup = match.repeat_group();
-        if(newRepGroup) {
-            if(pos_.start_of_repeat_group()) {
-                for_each_potential_miss(std::move(npos),
-                    [&,this](const dfs_traverser& pos) {
-                        //only add candidates within repeat group
-                        if(newRepGroup == pos.repeat_group()) {
-                            missCand_.emplace_back(pos, index_, true);
-                        }
-                    });
-            }
-        }
-        //... or an optional blocking param was hit
-        else if(match->blocking() && !match->required() &&
-            npos.level() >= match.base().level())
-        {
-            for_each_potential_miss(std::move(npos),
-                [&,this](const dfs_traverser& pos) {
-                    //only add new candidates
-                    if(std::find_if(missCand_.begin(), missCand_.end(),
-                        [&](const miss_candidate& c){
-                            return &(*c.pos) == &(*pos);
-                        }) == missCand_.end())
-                    {
-                        missCand_.emplace_back(pos, index_);
-                    }
-                });
-        }
-
-    }
-
-    //-----------------------------------------------------
-    template<class Action>
-    static void
-    for_each_potential_miss(dfs_traverser pos, Action&& action)
-    {
-        const auto level = pos.level();
-        while(pos && pos.level() >= level) {
-            if(pos->is_group() ) {
-                const auto& g = pos->as_group();
-                if(g.all_optional() || (g.exclusive() && g.any_optional())) {
-                    pos.next_sibling();
-                } else {
-                    ++pos;
-                }
-            } else {  //param
-                if(pos->required()) {
-                    action(pos);
-                    ++pos;
-                } else if(pos->blocking()) { //optional + blocking
-                    pos.next_after_siblings();
-                } else {
-                    ++pos;
-                }
-            }
-        }
-    }
-
-
-    //---------------------------------------------------------------
-    std::size_t occurrences_of(const parameter* p) const
-    {
-        auto i = std::find_if(args_.rbegin(), args_.rend(),
-            [p](const arg_mapping& a){ return a.param() == p; });
-
-        if(i != args_.rend()) return i->repeat() + 1;
-        return 0;
-    }
-
-
-    //---------------------------------------------------------------
-    const group* root_;
-    scoped_dfs_traverser pos_;
-    arg_index index_;
-    arg_index eaten_;
-    arg_mappings args_;
-    miss_candidates missCand_;
-    bool blocked_;
-};
-
-
-
-
-/*************************************************************************//**
- *
- * @brief contains argument -> parameter mappings
- *        and missing parameters
- *
- *****************************************************************************/
-class parsing_result
-{
-public:
-    using arg_mapping    = parser::arg_mapping;
-    using arg_mappings   = parser::arg_mappings;
-    using missing_event  = parser::missing_event;
-    using missing_events = parser::missing_events;
-    using iterator       = arg_mappings::const_iterator;
-
-    //-----------------------------------------------------
-    /** @brief default: empty redult */
-    parsing_result() = default;
-
-    parsing_result(arg_mappings arg2param, missing_events misses):
-        arg2param_{std::move(arg2param)}, missing_{std::move(misses)}
-    {}
-
-    //-----------------------------------------------------
-    /** @brief returns number of arguments that could not be mapped to
-     *         a parameter
-     */
-    arg_mappings::size_type
-    unmapped_args_count() const noexcept {
-        return std::count_if(arg2param_.begin(), arg2param_.end(),
-            [](const arg_mapping& a){ return !a.param(); });
-    }
-
-    /** @brief returns if any argument could only be matched by an
-     *         unreachable parameter
-     */
-    bool any_blocked() const noexcept {
-        return std::any_of(arg2param_.begin(), arg2param_.end(),
-            [](const arg_mapping& a){ return a.blocked(); });
-    }
-
-    /** @brief returns if any argument matched more than one parameter
-     *         that were mutually exclusive */
-    bool any_conflict() const noexcept {
-        return std::any_of(arg2param_.begin(), arg2param_.end(),
-            [](const arg_mapping& a){ return a.conflict(); });
-    }
-
-    /** @brief returns if any parameter matched repeatedly although
-     *         it was not allowed to */
-    bool any_bad_repeat() const noexcept {
-        return std::any_of(arg2param_.begin(), arg2param_.end(),
-            [](const arg_mapping& a){ return a.bad_repeat(); });
-    }
-
-    /** @brief returns true if any parsing error / violation of the
-     *         command line interface definition occured */
-    bool any_error() const noexcept {
-        return unmapped_args_count() > 0 || !missing().empty() ||
-               any_blocked() || any_conflict() || any_bad_repeat();
-    }
-
-    /** @brief returns true if no parsing error / violation of the
-     *         command line interface definition occured */
-    explicit operator bool() const noexcept { return !any_error(); }
-
-    /** @brief access to range of missing parameter match events */
-    const missing_events& missing() const noexcept { return missing_; }
-
-    /** @brief returns non-mutating iterator to position of
-     *         first argument -> parameter mapping  */
-    iterator begin() const noexcept { return arg2param_.begin(); }
-    /** @brief returns non-mutating iterator to position one past the
-     *         last argument -> parameter mapping  */
-    iterator end()   const noexcept { return arg2param_.end(); }
-
-private:
-    //-----------------------------------------------------
-    arg_mappings arg2param_;
-    missing_events missing_;
-};
-
-
-
-
-namespace detail {
-namespace {
-
-/*************************************************************************//**
- *
- * @brief correct some common problems
- *        does not - and MUST NOT - change the number of arguments
- *        (no insertion, no deletion)
- *
- *****************************************************************************/
-void sanitize_args(arg_list& args)
-{
-    //e.g. {"-o12", ".34"} -> {"-o", "12.34"}
-
-    if(args.empty()) return;
-
-    for(auto i = begin(args)+1; i != end(args); ++i) {
-        if(i != begin(args) && i->size() > 1 &&
-            i->find('.') == 0 && std::isdigit((*i)[1]) )
-        {
-            //find trailing digits in previous arg
-            using std::prev;
-            auto& prv = *prev(i);
-            auto fstDigit = std::find_if_not(prv.rbegin(), prv.rend(),
-                [](arg_string::value_type c){
-                    return std::isdigit(c);
-                }).base();
-
-            //handle leading sign
-            if(fstDigit > prv.begin() &&
-                (*prev(fstDigit) == '+' || *prev(fstDigit) == '-'))
-            {
-                --fstDigit;
-            }
-
-            //prepend digits from previous arg
-            i->insert(begin(*i), fstDigit, end(prv));
-
-            //erase digits in previous arg
-            prv.erase(fstDigit, end(prv));
-        }
-    }
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief executes actions based on a parsing result
- *
- *****************************************************************************/
-void execute_actions(const parsing_result& res)
-{
-    for(const auto& m : res) {
-        if(m.param()) {
-            const auto& param = *(m.param());
-
-            if(m.repeat() > 0) param.notify_repeated(m.index());
-            if(m.blocked())    param.notify_blocked(m.index());
-            if(m.conflict())   param.notify_conflict(m.index());
-            //main action
-            if(!m.any_error()) param.execute_actions(m.arg());
-        }
-    }
-
-    for(auto m : res.missing()) {
-        if(m.param()) m.param()->notify_missing(m.after_index());
-    }
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief parses input args
- *
- *****************************************************************************/
-static parsing_result
-parse_args(const arg_list& args, const group& cli,
-           arg_index offset = 0)
-{
-    //parse args and store unrecognized arg indices
-    parser parse{cli, offset};
-    for(const auto& arg : args) {
-        parse(arg);
-        if(!parse.valid()) break;
-    }
-
-    return parsing_result{parse.args(), parse.missed()};
-}
-
-/*************************************************************************//**
- *
- * @brief parses input args & executes actions
- *
- *****************************************************************************/
-static parsing_result
-parse_and_execute(const arg_list& args, const group& cli,
-                  arg_index offset = 0)
-{
-    auto result = parse_args(args, cli, offset);
-
-    execute_actions(result);
-
-    return result;
-}
-
-} //anonymous namespace
-} // namespace detail
-
-
-
-
-/*************************************************************************//**
- *
- * @brief parses vector of arg strings and executes actions
- *
- *****************************************************************************/
-inline parsing_result
-parse(arg_list args, const group& cli)
-{
-    detail::sanitize_args(args);
-    return detail::parse_and_execute(args, cli);
-}
-
-
-/*************************************************************************//**
- *
- * @brief parses initializer_list of C-style arg strings and executes actions
- *
- *****************************************************************************/
-inline parsing_result
-parse(std::initializer_list<const char*> arglist, const group& cli)
-{
-    arg_list args;
-    args.reserve(arglist.size());
-    for(auto a : arglist) {
-        if(std::strlen(a) > 0) args.push_back(a);
-    }
-
-    return parse(std::move(args), cli);
-}
-
-
-/*************************************************************************//**
- *
- * @brief parses range of arg strings and executes actions
- *
- *****************************************************************************/
-template<class InputIterator>
-inline parsing_result
-parse(InputIterator first, InputIterator last, const group& cli)
-{
-    return parse(arg_list(first,last), cli);
-}
-
-
-/*************************************************************************//**
- *
- * @brief parses the standard array of command line arguments; omits argv[0]
- *
- *****************************************************************************/
-inline parsing_result
-parse(const int argc, char* argv[], const group& cli, arg_index offset = 1)
-{
-    arg_list args;
-    if(offset < argc) args.assign(argv+offset, argv+argc);
-    detail::sanitize_args(args);
-    return detail::parse_and_execute(args, cli, offset);
-}
-
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief filter predicate for parameters and groups;
- *        Can be used to limit documentation generation to parameter subsets.
- *
- *****************************************************************************/
-class param_filter
-{
-public:
-    /** @brief only allow parameters with given prefix */
-    param_filter& prefix(const arg_string& p) noexcept {
-        prefix_ = p; return *this;
-    }
-    /** @brief only allow parameters with given prefix */
-    param_filter& prefix(arg_string&& p) noexcept {
-        prefix_ = std::move(p); return *this;
-    }
-    const arg_string& prefix()  const noexcept { return prefix_; }
-
-    /** @brief only allow parameters with given requirement status */
-    param_filter& required(tri t)  noexcept { required_ = t; return *this; }
-    tri           required() const noexcept { return required_; }
-
-    /** @brief only allow parameters with given blocking status */
-    param_filter& blocking(tri t)  noexcept { blocking_ = t; return *this; }
-    tri           blocking() const noexcept { return blocking_; }
-
-    /** @brief only allow parameters with given repeatable status */
-    param_filter& repeatable(tri t)  noexcept { repeatable_ = t; return *this; }
-    tri           repeatable() const noexcept { return repeatable_; }
-
-    /** @brief only allow parameters with given docstring status */
-    param_filter& has_doc(tri t)  noexcept { hasDoc_ = t; return *this; }
-    tri           has_doc() const noexcept { return hasDoc_; }
-
-
-    /** @brief returns true, if parameter satisfies all filters */
-    bool operator() (const parameter& p) const noexcept {
-        if(!prefix_.empty()) {
-            if(!std::any_of(p.flags().begin(), p.flags().end(),
-                [&](const arg_string& flag){
-                    return str::has_prefix(flag, prefix_);
-                })) return false;
-        }
-        if(required()   != p.required())     return false;
-        if(blocking()   != p.blocking())     return false;
-        if(repeatable() != p.repeatable())   return false;
-        if(has_doc()    != !p.doc().empty()) return false;
-        return true;
-    }
-
-private:
-    arg_string prefix_;
-    tri required_   = tri::either;
-    tri blocking_   = tri::either;
-    tri repeatable_ = tri::either;
-    tri exclusive_  = tri::either;
-    tri hasDoc_     = tri::yes;
-};
-
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief documentation formatting options
- *
- *****************************************************************************/
-class doc_formatting
-{
-public:
-    using string = doc_string;
-
-    /** @brief determines column where documentation printing starts */
-    doc_formatting& start_column(int col) { startCol_ = col; return *this; }
-    int             start_column() const noexcept { return startCol_; }
-
-    /** @brief determines column where docstrings start */
-    doc_formatting& doc_column(int col) { docCol_ = col; return *this; }
-    int             doc_column() const noexcept  { return docCol_; }
-
-    /** @brief determines indent of documentation lines
-     *         for children of a documented group */
-    doc_formatting& indent_size(int indent) { indentSize_ = indent; return *this; }
-    int             indent_size() const noexcept  { return indentSize_; }
-
-    /** @brief determines string to be used
-     *         if a parameter has no flags and no label  */
-    doc_formatting& empty_label(const string& label) {
-        emptyLabel_ = label;
-        return *this;
-    }
-    const string& empty_label() const noexcept { return emptyLabel_; }
-
-    /** @brief determines string for separating parameters */
-    doc_formatting& param_separator(const string& sep) {
-        paramSep_ = sep;
-        return *this;
-    }
-    const string& param_separator() const noexcept { return paramSep_; }
-
-    /** @brief determines string for separating groups (in usage lines) */
-    doc_formatting& group_separator(const string& sep) {
-        groupSep_ = sep;
-        return *this;
-    }
-    const string& group_separator() const noexcept { return groupSep_; }
-
-    /** @brief determines string for separating alternative parameters */
-    doc_formatting& alternative_param_separator(const string& sep) {
-        altParamSep_ = sep;
-        return *this;
-    }
-    const string& alternative_param_separator() const noexcept { return altParamSep_; }
-
-    /** @brief determines string for separating alternative groups */
-    doc_formatting& alternative_group_separator(const string& sep) {
-        altGroupSep_ = sep;
-        return *this;
-    }
-    const string& alternative_group_separator() const noexcept { return altGroupSep_; }
-
-    /** @brief determines string for separating flags of the same parameter */
-    doc_formatting& flag_separator(const string& sep) {
-        flagSep_ = sep;
-        return *this;
-    }
-    const string& flag_separator() const noexcept { return flagSep_; }
-
-    /** @brief determnines strings surrounding parameter labels */
-    doc_formatting&
-    surround_labels(const string& prefix, const string& postfix) {
-        labelPre_ = prefix;
-        labelPst_ = postfix;
-        return *this;
-    }
-    const string& label_prefix()  const noexcept { return labelPre_; }
-    const string& label_postfix() const noexcept { return labelPst_; }
-
-    /** @brief determnines strings surrounding optional parameters/groups */
-    doc_formatting&
-    surround_optional(const string& prefix, const string& postfix) {
-        optionPre_ = prefix;
-        optionPst_ = postfix;
-        return *this;
-    }
-    const string& optional_prefix()  const noexcept { return optionPre_; }
-    const string& optional_postfix() const noexcept { return optionPst_; }
-
-    /** @brief determnines strings surrounding repeatable parameters/groups */
-    doc_formatting&
-    surround_repeat(const string& prefix, const string& postfix) {
-        repeatPre_ = prefix;
-        repeatPst_ = postfix;
-        return *this;
-    }
-    const string& repeat_prefix()  const noexcept { return repeatPre_; }
-    const string& repeat_postfix() const noexcept { return repeatPst_; }
-
-    /** @brief determnines strings surrounding exclusive groups */
-    doc_formatting&
-    surround_alternatives(const string& prefix, const string& postfix) {
-        alternPre_ = prefix;
-        alternPst_ = postfix;
-        return *this;
-    }
-    const string& alternatives_prefix()  const noexcept { return alternPre_; }
-    const string& alternatives_postfix() const noexcept { return alternPst_; }
-
-    /** @brief determnines strings surrounding alternative flags */
-    doc_formatting&
-    surround_alternative_flags(const string& prefix, const string& postfix) {
-        alternFlagPre_ = prefix;
-        alternFlagPst_ = postfix;
-        return *this;
-    }
-    const string& alternative_flags_prefix()  const noexcept { return alternFlagPre_; }
-    const string& alternative_flags_postfix() const noexcept { return alternFlagPst_; }
-
-    /** @brief determnines strings surrounding non-exclusive groups */
-    doc_formatting&
-    surround_group(const string& prefix, const string& postfix) {
-        groupPre_ = prefix;
-        groupPst_ = postfix;
-        return *this;
-    }
-    const string& group_prefix()  const noexcept { return groupPre_; }
-    const string& group_postfix() const noexcept { return groupPst_; }
-
-    /** @brief determnines strings surrounding joinable groups */
-    doc_formatting&
-    surround_joinable(const string& prefix, const string& postfix) {
-        joinablePre_ = prefix;
-        joinablePst_ = postfix;
-        return *this;
-    }
-    const string& joinable_prefix()  const noexcept { return joinablePre_; }
-    const string& joinable_postfix() const noexcept { return joinablePst_; }
-
-    /** @brief determines maximum number of flags per parameter to be printed
-     *         in detailed parameter documentation lines */
-    doc_formatting& max_flags_per_param_in_doc(int max) {
-        maxAltInDocs_ = max > 0 ? max : 0;
-        return *this;
-    }
-    int max_flags_per_param_in_doc() const noexcept { return maxAltInDocs_; }
-
-    /** @brief determines maximum number of flags per parameter to be printed
-     *         in usage lines */
-    doc_formatting& max_flags_per_param_in_usage(int max) {
-        maxAltInUsage_ = max > 0 ? max : 0;
-        return *this;
-    }
-    int max_flags_per_param_in_usage() const noexcept { return maxAltInUsage_; }
-
-    /** @brief determines number of empty rows after one single-line
-     *         documentation entry */
-    doc_formatting& line_spacing(int lines) {
-        lineSpc_ = lines > 0 ? lines : 0;
-        return *this;
-    }
-    int line_spacing() const noexcept { return lineSpc_; }
-
-    /** @brief determines number of empty rows before and after a paragraph;
-     *         a paragraph is defined by a documented group or if
-     *         a parameter documentation entry used more than one line */
-    doc_formatting& paragraph_spacing(int lines) {
-        paragraphSpc_ = lines > 0 ? lines : 0;
-        return *this;
-    }
-    int paragraph_spacing() const noexcept { return paragraphSpc_; }
-
-    /** @brief determines if alternative flags with a common prefix should
-     *         be printed in a merged fashion */
-    doc_formatting& merge_alternative_flags_with_common_prefix(bool yes = true) {
-        mergeAltCommonPfx_ = yes;
-        return *this;
-    }
-    bool merge_alternative_flags_with_common_prefix() const noexcept {
-        return mergeAltCommonPfx_;
-    }
-
-    /** @brief determines if joinable flags with a common prefix should
-     *         be printed in a merged fashion */
-    doc_formatting& merge_joinable_with_common_prefix(bool yes = true) {
-        mergeJoinableCommonPfx_ = yes;
-        return *this;
-    }
-    bool merge_joinable_with_common_prefix() const noexcept {
-        return mergeJoinableCommonPfx_;
-    }
-
-    /** @brief determines if children of exclusive groups should be printed
-     *         on individual lines if the exceed 'alternatives_min_split_size'
-     */
-    doc_formatting& split_alternatives(bool yes = true) {
-        splitTopAlt_ = yes;
-        return *this;
-    }
-    bool split_alternatives() const noexcept {
-        return splitTopAlt_;
-    }
-
-    /** @brief determines how many children exclusive groups can have before
-     *         their children are printed on individual usage lines */
-    doc_formatting& alternatives_min_split_size(int size) {
-        groupSplitSize_ = size > 0 ? size : 0;
-        return *this;
-    }
-    int alternatives_min_split_size() const noexcept { return groupSplitSize_; }
-
-private:
-    string paramSep_      = string(" ");
-    string groupSep_      = string(" ");
-    string altParamSep_   = string("|");
-    string altGroupSep_   = string(" | ");
-    string flagSep_       = string(", ");
-    string labelPre_      = string("<");
-    string labelPst_      = string(">");
-    string optionPre_     = string("[");
-    string optionPst_     = string("]");
-    string repeatPre_     = string("");
-    string repeatPst_     = string("...");
-    string groupPre_      = string("(");
-    string groupPst_      = string(")");
-    string alternPre_     = string("(");
-    string alternPst_     = string(")");
-    string alternFlagPre_ = string("");
-    string alternFlagPst_ = string("");
-    string joinablePre_   = string("(");
-    string joinablePst_   = string(")");
-    string emptyLabel_    = string("");
-    int startCol_ = 8;
-    int docCol_ = 20;
-    int indentSize_ = 4;
-    int maxAltInUsage_ = 1;
-    int maxAltInDocs_ = 32;
-    int lineSpc_ = 0;
-    int paragraphSpc_ = 1;
-    int groupSplitSize_ = 3;
-    bool splitTopAlt_ = true;
-    bool mergeAltCommonPfx_ = false;
-    bool mergeJoinableCommonPfx_ = true;
-};
-
-
-
-
-/*************************************************************************//**
- *
- * @brief   generates usage lines
- *
- * @details lazily evaluated
- *
- *****************************************************************************/
-class usage_lines
-{
-public:
-    using string = doc_string;
-
-    usage_lines(const group& params, string prefix = "",
-                const doc_formatting& fmt = doc_formatting{})
-    :
-        params_(params), fmt_(fmt), prefix_(std::move(prefix))
-    {
-        if(!prefix_.empty()) prefix_ += ' ';
-        if(fmt_.start_column() > 0) prefix_.insert(0, fmt.start_column(), ' ');
-    }
-
-    usage_lines(const group& params, const doc_formatting& fmt):
-        usage_lines(params, "", fmt)
-    {}
-
-    usage_lines& ommit_outermost_group_surrounders(bool yes) {
-        ommitOutermostSurrounders_ = yes;
-        return *this;
-    }
-    bool ommit_outermost_group_surrounders() const {
-        return ommitOutermostSurrounders_;
-    }
-
-    template<class OStream>
-    inline friend OStream& operator << (OStream& os, const usage_lines& p) {
-        p.print_usage(os);
-        return os;
-    }
-
-    string str() const {
-        std::ostringstream os; os << *this; return os.str();
-    }
-
-
-private:
-    const group& params_;
-    doc_formatting fmt_;
-    string prefix_;
-    bool ommitOutermostSurrounders_ = false;
-
-
-    //-----------------------------------------------------
-    struct context {
-        group::depth_first_traverser pos;
-        std::stack<string> separators;
-        std::stack<string> postfixes;
-        int level = 0;
-        const group* outermost = nullptr;
-        bool linestart = false;
-        bool useOutermost = true;
-        int line = 0;
-
-        bool is_singleton() const noexcept {
-            return linestart && pos.is_last_in_path();
-        }
-        bool is_alternative() const noexcept {
-            return pos.parent().exclusive();
-        }
-    };
-
-
-    /***************************************************************//**
-     *
-     * @brief writes usage text for command line parameters
-     *
-     *******************************************************************/
-    template<class OStream>
-    void print_usage(OStream& os) const
-    {
-        context cur;
-        cur.pos = params_.begin_dfs();
-        cur.linestart = true;
-        cur.level = cur.pos.level();
-        cur.outermost = &params_;
-
-        print_usage(os, cur, prefix_);
-    }
-
-
-    /***************************************************************//**
-     *
-     * @brief writes usage text for command line parameters
-     *
-     * @param prefix   all that goes in front of current things to print
-     *
-     *******************************************************************/
-    template<class OStream>
-    void print_usage(OStream& os, context cur, string prefix) const
-    {
-        if(!cur.pos) return;
-
-        std::ostringstream buf;
-        if(cur.linestart) buf << prefix;
-        const auto initPos = buf.tellp();
-
-        cur.level = cur.pos.level();
-
-        if(cur.useOutermost) {
-            //we cannot start outside of the outermost group
-            //so we have to treat it separately
-            start_group(buf, cur.pos.parent(), cur);
-            if(!cur.pos) {
-                os << buf.str();
-                return;
-            }
-        }
-        else {
-            //don't visit siblings of starter node
-            cur.pos.skip_siblings();
-        }
-        check_end_group(buf, cur);
-
-        do {
-            if(buf.tellp() > initPos) cur.linestart = false;
-            if(!cur.linestart && !cur.pos.is_first_in_group()) {
-                buf << cur.separators.top();
-            }
-            if(cur.pos->is_group()) {
-                start_group(buf, cur.pos->as_group(), cur);
-                if(!cur.pos) {
-                    os << buf.str();
-                    return;
-                }
-            }
-            else {
-                buf << param_label(cur.pos->as_param(), cur);
-                ++cur.pos;
-            }
-            check_end_group(buf, cur);
-        } while(cur.pos);
-
-        os << buf.str();
-    }
-
-
-    /***************************************************************//**
-     *
-     * @brief handles pattern group surrounders and separators
-     *        and alternative splitting
-     *
-     *******************************************************************/
-    void start_group(std::ostringstream& os,
-                     const group& group, context& cur) const
-    {
-        //does cur.pos already point to a member or to group itself?
-        //needed for special treatment of outermost group
-        const bool alreadyInside = &(cur.pos.parent()) == &group;
-
-        auto lbl = joined_label(group, cur);
-        if(!lbl.empty()) {
-            os << lbl;
-            cur.linestart = false;
-            //skip over entire group as its label has already been created
-            if(alreadyInside) {
-                cur.pos.next_after_siblings();
-            } else {
-                cur.pos.next_sibling();
-            }
-        }
-        else {
-            const bool splitAlternatives = group.exclusive() &&
-                fmt_.split_alternatives() &&
-                std::any_of(group.begin(), group.end(),
-                    [this](const pattern& p) {
-                        return int(p.param_count()) >= fmt_.alternatives_min_split_size();
-                    });
-
-            if(splitAlternatives) {
-                cur.postfixes.push("");
-                cur.separators.push("");
-                //recursively print alternative paths in decision-DAG
-                //enter group?
-                if(!alreadyInside) ++cur.pos;
-                cur.linestart = true;
-                cur.useOutermost = false;
-                auto pfx = os.str();
-                os.str("");
-                //print paths in DAG starting at each group member
-                for(std::size_t i = 0; i < group.size(); ++i) {
-                    std::stringstream buf;
-                    cur.outermost = cur.pos->is_group() ? &(cur.pos->as_group()) : nullptr;
-                    print_usage(buf, cur, pfx);
-                    if(buf.tellp() > int(pfx.size())) {
-                        os << buf.str();
-                        if(i < group.size()-1) {
-                            if(cur.line > 0) {
-                                os << string(fmt_.line_spacing(), '\n');
-                            }
-                            ++cur.line;
-                            os << '\n';
-                        }
-                    }
-                    cur.pos.next_sibling(); //do not descend into memebers
-                }
-                cur.pos.invalidate(); //signal end-of-path
-                return;
-            }
-            else {
-                //pre & postfixes, separators
-                auto surround = group_surrounders(group, cur);
-                os << surround.first;
-                cur.postfixes.push(std::move(surround.second));
-                cur.separators.push(group_separator(group, fmt_));
-                //descend into group?
-                if(!alreadyInside) ++cur.pos;
-            }
-        }
-        cur.level = cur.pos.level();
-    }
-
-
-    /***************************************************************//**
-     *
-     *******************************************************************/
-    void check_end_group(std::ostringstream& os, context& cur) const
-    {
-        for(; cur.level > cur.pos.level(); --cur.level) {
-            os << cur.postfixes.top();
-            cur.postfixes.pop();
-            cur.separators.pop();
-        }
-        cur.level = cur.pos.level();
-    }
-
-
-    /***************************************************************//**
-     *
-     * @brief makes usage label for one command line parameter
-     *
-     *******************************************************************/
-    string param_label(const parameter& p, const context& cur) const
-    {
-        const auto& parent = cur.pos.parent();
-
-        const bool startsOptionalSequence =
-            parent.size() > 1 && p.blocking() && cur.pos.is_first_in_group();
-
-        const bool outermost =
-            ommitOutermostSurrounders_ && cur.outermost == &parent;
-
-        const bool showopt = !cur.is_alternative() && !p.required()
-            && !startsOptionalSequence && !outermost;
-
-        const bool showrep = p.repeatable() && !outermost;
-
-        string lbl;
-
-        if(showrep) lbl += fmt_.repeat_prefix();
-        if(showopt) lbl += fmt_.optional_prefix();
-
-        const auto& flags = p.flags();
-        if(!flags.empty()) {
-            const int n = std::min(fmt_.max_flags_per_param_in_usage(),
-                                   int(flags.size()));
-
-            const bool surrAlt = n > 1 && !showopt && !cur.is_singleton();
-
-            if(surrAlt) lbl += fmt_.alternative_flags_prefix();
-            bool sep = false;
-            for(int i = 0; i < n; ++i) {
-                if(sep) {
-                    if(cur.is_singleton())
-                        lbl += fmt_.alternative_group_separator();
-                    else
-                        lbl += fmt_.flag_separator();
-                }
-                lbl += flags[i];
-                sep = true;
-            }
-            if(surrAlt) lbl += fmt_.alternative_flags_postfix();
-        }
-        else {
-             if(!p.label().empty()) {
-                 lbl += fmt_.label_prefix()
-                     + p.label()
-                     + fmt_.label_postfix();
-             } else if(!fmt_.empty_label().empty()) {
-                 lbl += fmt_.label_prefix()
-                     + fmt_.empty_label()
-                     + fmt_.label_postfix();
-             } else {
-                 return "";
-             }
-        }
-
-        if(showopt) lbl += fmt_.optional_postfix();
-        if(showrep) lbl += fmt_.repeat_postfix();
-
-        return lbl;
-    }
-
-
-    /***************************************************************//**
-     *
-     * @brief prints flags in one group in a merged fashion
-     *
-     *******************************************************************/
-    string joined_label(const group& params, const context& cur) const
-    {
-        if(!fmt_.merge_alternative_flags_with_common_prefix() &&
-           !fmt_.merge_joinable_with_common_prefix()) return "";
-
-        const bool flagsonly = std::all_of(params.begin(), params.end(),
-            [](const pattern& p){
-                return p.is_param() && !p.as_param().flags().empty();
-            });
-
-        if(!flagsonly) return "";
-
-        const bool showOpt = params.all_optional() &&
-            !(ommitOutermostSurrounders_ && cur.outermost == &params);
-
-        auto pfx = params.common_flag_prefix();
-        if(pfx.empty()) return "";
-
-        const auto n = pfx.size();
-        if(params.exclusive() &&
-           fmt_.merge_alternative_flags_with_common_prefix())
-        {
-            string lbl;
-            if(showOpt) lbl += fmt_.optional_prefix();
-            lbl += pfx + fmt_.alternatives_prefix();
-            bool first = true;
-            for(const auto& p : params) {
-                if(p.is_param()) {
-                    if(first)
-                        first = false;
-                    else
-                        lbl += fmt_.alternative_param_separator();
-                    lbl += p.as_param().flags().front().substr(n);
-                }
-            }
-            lbl += fmt_.alternatives_postfix();
-            if(showOpt) lbl += fmt_.optional_postfix();
-            return lbl;
-        }
-        //no alternatives, but joinable flags
-        else if(params.joinable() &&
-            fmt_.merge_joinable_with_common_prefix())
-        {
-            const bool allSingleChar = std::all_of(params.begin(), params.end(),
-                [&](const pattern& p){
-                    return p.is_param() &&
-                        p.as_param().flags().front().substr(n).size() == 1;
-                });
-
-            if(allSingleChar) {
-                string lbl;
-                if(showOpt) lbl += fmt_.optional_prefix();
-                lbl += pfx;
-                for(const auto& p : params) {
-                    if(p.is_param())
-                        lbl += p.as_param().flags().front().substr(n);
-                }
-                if(showOpt) lbl += fmt_.optional_postfix();
-                return lbl;
-            }
-        }
-
-        return "";
-    }
-
-
-    /***************************************************************//**
-     *
-     * @return symbols with which to surround a group
-     *
-     *******************************************************************/
-    std::pair<string,string>
-    group_surrounders(const group& group, const context& cur) const
-    {
-        string prefix;
-        string postfix;
-
-        const bool isOutermost = &group == cur.outermost;
-        if(isOutermost && ommitOutermostSurrounders_)
-            return {string{}, string{}};
-
-        if(group.exclusive()) {
-            if(group.all_optional()) {
-                prefix  = fmt_.optional_prefix();
-                postfix = fmt_.optional_postfix();
-                if(group.all_flagless()) {
-                    prefix  += fmt_.label_prefix();
-                    postfix = fmt_.label_prefix() + postfix;
-                }
-            } else if(group.all_flagless()) {
-                prefix  = fmt_.label_prefix();
-                postfix = fmt_.label_postfix();
-            } else if(!cur.is_singleton() || !isOutermost) {
-                prefix  = fmt_.alternatives_prefix();
-                postfix = fmt_.alternatives_postfix();
-            }
-        }
-        else if(group.size() > 1 &&
-                group.front().blocking() && !group.front().required())
-        {
-            prefix  = fmt_.optional_prefix();
-            postfix = fmt_.optional_postfix();
-        }
-        else if(group.size() > 1 && cur.is_alternative() &&
-                &group != cur.outermost)
-        {
-            prefix  = fmt_.group_prefix();
-            postfix = fmt_.group_postfix();
-        }
-        else if(!group.exclusive() &&
-            group.joinable() && !cur.linestart)
-        {
-            prefix  = fmt_.joinable_prefix();
-            postfix = fmt_.joinable_postfix();
-        }
-
-        if(group.repeatable()) {
-            if(prefix.empty()) prefix = fmt_.group_prefix();
-            prefix = fmt_.repeat_prefix() + prefix;
-            if(postfix.empty()) postfix = fmt_.group_postfix();
-            postfix += fmt_.repeat_postfix();
-        }
-
-        return {std::move(prefix), std::move(postfix)};
-    }
-
-
-    /***************************************************************//**
-     *
-     * @return symbol that separates members of a group
-     *
-     *******************************************************************/
-    static string
-    group_separator(const group& group, const doc_formatting& fmt)
-    {
-        const bool only1ParamPerMember = std::all_of(group.begin(), group.end(),
-            [](const pattern& p) { return p.param_count() < 2; });
-
-        if(only1ParamPerMember) {
-            if(group.exclusive()) {
-                return fmt.alternative_param_separator();
-            } else {
-                return fmt.param_separator();
-            }
-        }
-        else { //there is at least one large group inside
-            if(group.exclusive()) {
-                return fmt.alternative_group_separator();
-            } else {
-                return fmt.group_separator();
-            }
-        }
-    }
-};
-
-
-
-
-/*************************************************************************//**
- *
- * @brief   generates parameter and group documentation from docstrings
- *
- * @details lazily evaluated
- *
- *****************************************************************************/
-class documentation
-{
-public:
-    using string = doc_string;
-
-    documentation(const group& cli,
-                  const doc_formatting& fmt = doc_formatting{},
-                  const param_filter& filter = param_filter{})
-    :
-        cli_(cli), fmt_{fmt}, usgFmt_{fmt}, filter_{filter}
-    {
-        //necessary, because we re-use "usage_lines" to generate
-        //labels for documented groups
-        usgFmt_.max_flags_per_param_in_usage(
-            usgFmt_.max_flags_per_param_in_doc());
-    }
-
-    documentation(const group& params,
-                  const param_filter& filter,
-                  const doc_formatting& fmt = doc_formatting{})
-    :
-        documentation(params, fmt, filter)
-    {}
-
-    template<class OStream>
-    inline friend OStream& operator << (OStream& os, const documentation& p) {
-        printed prn = printed::nothing;
-        p.print_doc(os, p.cli_, prn);
-        return os;
-    }
-
-    string str() const {
-        std::ostringstream os; os << *this; return os.str();
-    }
-
-
-private:
-    using dfs_traverser = group::depth_first_traverser;
-    enum class printed { nothing, line, paragraph };
-
-    const group& cli_;
-    doc_formatting fmt_;
-    doc_formatting usgFmt_;
-    param_filter filter_;
-
-
-    /***************************************************************//**
-     *
-     * @brief writes full documentation text for command line parameters
-     *
-     *******************************************************************/
-    template<class OStream>
-    void print_doc(OStream& os, const group& params,
-                   printed& sofar,
-                   int indentLvl = 0) const
-    {
-        if(params.empty()) return;
-
-        //if group itself doesn't have docstring
-        if(params.doc().empty()) {
-            for(const auto& p : params) {
-                print_doc(os, p, sofar, indentLvl);
-            }
-        }
-        else { //group itself does have docstring
-            bool anyDocInside = std::any_of(params.begin(), params.end(),
-                [](const pattern& p){ return !p.doc().empty(); });
-
-            if(anyDocInside) { //group docstring as title, then child entries
-                if(sofar != printed::nothing) {
-                    os << string(fmt_.paragraph_spacing() + 1, '\n');
-                }
-                auto indent = string(fmt_.start_column(), ' ');
-                if(indentLvl > 0) indent += string(fmt_.indent_size() * indentLvl, ' ');
-                os << indent << params.doc() << '\n';
-                sofar = printed::nothing;
-                for(const auto& p : params) {
-                    print_doc(os, p, sofar, indentLvl + 1);
-                }
-                sofar = printed::paragraph;
-            }
-            else { //group label first then group docstring
-                auto lbl = usage_lines(params, usgFmt_)
-                           .ommit_outermost_group_surrounders(true).str();
-
-                str::trim(lbl);
-                print_entry(os, lbl, params.doc(), fmt_, sofar, indentLvl);
-            }
-        }
-    }
-
-
-    /***************************************************************//**
-     *
-     * @brief writes documentation text for one group or parameter
-     *
-     *******************************************************************/
-    template<class OStream>
-    void print_doc(OStream& os, const pattern& ptrn,
-                   printed& sofar, int indentLvl) const
-    {
-        if(ptrn.is_group()) {
-            print_doc(os, ptrn.as_group(), sofar, indentLvl);
-        }
-        else {
-            const auto& p = ptrn.as_param();
-            if(!filter_(p)) return;
-            print_entry(os, param_label(p, fmt_), p.doc(), fmt_, sofar, indentLvl);
-        }
-    }
-
-
-    /*********************************************************************//**
-     *
-     * @brief prints one entry = label + docstring
-     *
-     ************************************************************************/
-    template<class OStream>
-    static void
-    print_entry(OStream& os,
-                const string& label, const string& docstr,
-                const doc_formatting& fmt, printed& sofar, int indentLvl)
-    {
-        if(label.empty()) return;
-
-        auto indent = string(fmt.start_column(), ' ');
-        if(indentLvl > 0) indent += string(fmt.indent_size() * indentLvl, ' ');
-
-        const auto len = int(indent.size() + label.size());
-        const bool oneline = len < fmt.doc_column();
-
-        if(oneline) {
-            if(sofar == printed::line)
-                os << string(fmt.line_spacing() + 1, '\n');
-            else if(sofar == printed::paragraph)
-                os << string(fmt.paragraph_spacing() + 1, '\n');
-        }
-        else if(sofar != printed::nothing) {
-            os << string(fmt.paragraph_spacing() + 1, '\n');
-        }
-
-        sofar = oneline ? printed::line : printed::paragraph;
-
-        os << indent << label;
-
-        if(!docstr.empty()) {
-            if(oneline) {
-                os << string(fmt.doc_column() - len, ' ');
-            } else {
-                os << '\n' << string(fmt.doc_column(), ' ');
-            }
-            os << docstr;
-        }
-    }
-
-
-    /*********************************************************************//**
-     *
-     * @brief makes label for one parameter
-     *
-     ************************************************************************/
-    static doc_string
-    param_label(const parameter& param, const doc_formatting& fmt)
-    {
-        doc_string lbl;
-
-        if(param.repeatable()) lbl += fmt.repeat_prefix();
-
-        const auto& flags = param.flags();
-        if(!flags.empty()) {
-            lbl += flags[0];
-            const int n = std::min(fmt.max_flags_per_param_in_doc(),
-                                   int(flags.size()));
-            for(int i = 1; i < n; ++i) {
-                lbl += fmt.flag_separator() + flags[i];
-            }
-        }
-        else if(!param.label().empty() || !fmt.empty_label().empty()) {
-            lbl += fmt.label_prefix();
-            if(!param.label().empty()) {
-                lbl += param.label();
-            } else {
-                lbl += fmt.empty_label();
-            }
-            lbl += fmt.label_postfix();
-        }
-
-        if(param.repeatable()) lbl += fmt.repeat_postfix();
-
-        return lbl;
-    }
-
-};
-
-
-
-
-/*************************************************************************//**
- *
- * @brief stores strings for man page sections
- *
- *****************************************************************************/
-class man_page
-{
-public:
-    //---------------------------------------------------------------
-    using string = doc_string;
-
-    //---------------------------------------------------------------
-    /** @brief man page section */
-    class section {
-    public:
-        using string = doc_string;
-
-        section(string stitle, string scontent):
-            title_{std::move(stitle)}, content_{std::move(scontent)}
-        {}
-
-        const string& title()   const noexcept { return title_; }
-        const string& content() const noexcept { return content_; }
-
-    private:
-        string title_;
-        string content_;
-    };
-
-private:
-    using section_store = std::vector<section>;
-
-public:
-    //---------------------------------------------------------------
-    using value_type     = section;
-    using const_iterator = section_store::const_iterator;
-    using size_type      = section_store::size_type;
-
-
-    //---------------------------------------------------------------
-    man_page&
-    append_section(string title, string content)
-    {
-        sections_.emplace_back(std::move(title), std::move(content));
-        return *this;
-    }
-    //-----------------------------------------------------
-    man_page&
-    prepend_section(string title, string content)
-    {
-        sections_.emplace(sections_.begin(),
-                          std::move(title), std::move(content));
-        return *this;
-    }
-
-
-    //---------------------------------------------------------------
-    const section& operator [] (size_type index) const noexcept {
-        return sections_[index];
-    }
-
-    //---------------------------------------------------------------
-    size_type size() const noexcept { return sections_.size(); }
-
-    bool empty() const noexcept { return sections_.empty(); }
-
-
-    //---------------------------------------------------------------
-    const_iterator begin() const noexcept { return sections_.begin(); }
-    const_iterator end()   const noexcept { return sections_.end(); }
-
-
-    //---------------------------------------------------------------
-    man_page& program_name(const string& n) {
-        progName_ = n;
-        return *this;
-    }
-    man_page& program_name(string&& n) {
-        progName_ = std::move(n);
-        return *this;
-    }
-    const string& program_name() const noexcept {
-        return progName_;
-    }
-
-
-    //---------------------------------------------------------------
-    man_page& section_row_spacing(int rows) {
-        sectionSpc_ = rows > 0 ? rows : 0;
-        return *this;
-    }
-    int section_row_spacing() const noexcept { return sectionSpc_; }
-
-
-private:
-    int sectionSpc_ = 1;
-    section_store sections_;
-    string progName_;
-};
-
-
-
-/*************************************************************************//**
- *
- * @brief generates man sections from command line parameters
- *        with sections "synopsis" and "options"
- *
- *****************************************************************************/
-inline man_page
-make_man_page(const group& params,
-              doc_string progname = "",
-              const doc_formatting& fmt = doc_formatting{})
-{
-    man_page man;
-    man.append_section("SYNOPSIS", usage_lines(params,progname,fmt).str());
-    man.append_section("OPTIONS", documentation(params,fmt).str());
-    return man;
-}
-
-
-
-/*************************************************************************//**
- *
- * @brief   generates man page based on command line parameters
- *
- *****************************************************************************/
-template<class OStream>
-OStream&
-operator << (OStream& os, const man_page& man)
-{
-    bool first = true;
-    const auto secSpc = doc_string(man.section_row_spacing() + 1, '\n');
-    for(const auto& section : man) {
-        if(!section.content().empty()) {
-            if(first) first = false; else os << secSpc;
-            if(!section.title().empty()) os << section.title() << '\n';
-            os << section.content();
-        }
-    }
-    os << '\n';
-    return os;
-}
-
-
-
-
-
-/*************************************************************************//**
- *
- * @brief printing methods for debugging command line interfaces
- *
- *****************************************************************************/
-namespace debug {
-
-
-/*************************************************************************//**
- *
- * @brief prints first flag or value label of a parameter
- *
- *****************************************************************************/
-inline doc_string doc_label(const parameter& p)
-{
-    if(!p.flags().empty()) return p.flags().front();
-    if(!p.label().empty()) return p.label();
-    return doc_string{"<?>"};
-}
-
-inline doc_string doc_label(const group&)
-{
-    return "<group>";
-}
-
-inline doc_string doc_label(const pattern& p)
-{
-    return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param());
-}
-
-
-/*************************************************************************//**
- *
- * @brief prints parsing result
- *
- *****************************************************************************/
-template<class OStream>
-void print(OStream& os, const parsing_result& result)
-{
-    for(const auto& m : result) {
-        os << "#" << m.index() << " " << m.arg() << " -> ";
-        auto p = m.param();
-        if(p) {
-            os << doc_label(*p) << " \t";
-            if(m.repeat() > 0) {
-                os << (m.bad_repeat() ? "[bad repeat " : "[repeat ")
-                   <<  m.repeat() << "]";
-            }
-            if(m.blocked())  os << " [blocked]";
-            if(m.conflict()) os << " [conflict]";
-            os << '\n';
-        }
-        else {
-            os << " [unmapped]\n";
-        }
-    }
-
-    for(const auto& m : result.missing()) {
-        auto p = m.param();
-        if(p) {
-            os << doc_label(*p) << " \t";
-            os << " [missing after " << m.after_index() << "]\n";
-        }
-    }
-}
-
-
-/*************************************************************************//**
- *
- * @brief prints parameter label and some properties
- *
- *****************************************************************************/
-template<class OStream>
-void print(OStream& os, const parameter& p)
-{
-    if(p.blocking()) os << '!';
-    if(!p.required()) os << '[';
-    os << doc_label(p);
-    if(p.repeatable()) os << "...";
-    if(!p.required()) os << "]";
-}
-
-
-//-------------------------------------------------------------------
-template<class OStream>
-void print(OStream& os, const group& g, int level = 0);
-
-
-/*************************************************************************//**
- *
- * @brief prints group or parameter; uses indentation
- *
- *****************************************************************************/
-template<class OStream>
-void print(OStream& os, const pattern& param, int level = 0)
-{
-    if(param.is_group()) {
-        print(os, param.as_group(), level);
-    }
-    else {
-        os << doc_string(4*level, ' ');
-        print(os, param.as_param());
-    }
-}
-
-
-/*************************************************************************//**
- *
- * @brief prints group and its contents; uses indentation
- *
- *****************************************************************************/
-template<class OStream>
-void print(OStream& os, const group& g, int level)
-{
-    auto indent = doc_string(4*level, ' ');
-    os << indent;
-    if(g.blocking()) os << '!';
-    if(g.joinable()) os << 'J';
-    os << (g.exclusive() ? "(|\n" : "(\n");
-    for(const auto& p : g) {
-        print(os, p, level+1);
-    }
-    os << '\n' << indent << (g.exclusive() ? "|)" : ")");
-    if(g.repeatable()) os << "...";
-    os << '\n';
-}
-
-
-} // namespace debug
-} //namespace clipp
-
-#endif
-
diff --git a/include/exception.h b/include/exception.h
deleted file mode 100644
index b630bd008a0d8855211eca5ee0af60650aec1314..0000000000000000000000000000000000000000
--- a/include/exception.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef UTIL_EXCEPTION_H
-#define UTIL_EXCEPTION_H
-
-#include <exception>
-#include <string>
-
-namespace eic::util {
-  class Exception : public std::exception {
-  public:
-    Exception(std::string_view msg, std::string_view type = "exception") : msg_{msg}, type_{type} {}
-
-    virtual const char* what() const throw() { return msg_.c_str(); }
-    virtual const char* type() const throw() { return type_.c_str(); }
-    virtual ~Exception() throw() {}
-
-  private:
-    std::string msg_;
-    std::string type_;
-  };
-} // namespace eic::util
-
-#endif
diff --git a/include/mt.h b/include/mt.h
deleted file mode 100644
index 198050c6ddc37e68518761b8ccc410e0d71ea123..0000000000000000000000000000000000000000
--- a/include/mt.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef MT_H
-#define MT_H
-
-// Defines the number of threads to run within the ROOT analysis scripts.
-// TODO: make this a file configured by the CI scripts so we can specify
-//       the number of threads (and the number of processes) at a global
-//       level
-
-constexpr const int kNumThreads = 8;
-
-#endif
diff --git a/include/plot.h b/include/plot.h
deleted file mode 100644
index c198616325d54d73ae5e4c08f5ba7113ae77f817..0000000000000000000000000000000000000000
--- a/include/plot.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef PLOT_H
-#define PLOT_H
-
-#include <TCanvas.h>
-#include <TColor.h>
-#include <TPad.h>
-#include <TPaveText.h>
-#include <TStyle.h>
-#include <fmt/core.h>
-#include <vector>
-
-namespace plot {
-
-  const int kMpBlue   = TColor::GetColor(0x1f, 0x77, 0xb4);
-  const int kMpOrange = TColor::GetColor(0xff, 0x7f, 0x0e);
-  const int kMpGreen  = TColor::GetColor(0x2c, 0xa0, 0x2c);
-  const int kMpRed    = TColor::GetColor(0xd6, 0x27, 0x28);
-  const int kMpPurple = TColor::GetColor(0x94, 0x67, 0xbd);
-  const int kMpBrown  = TColor::GetColor(0x8c, 0x56, 0x4b);
-  const int kMpPink   = TColor::GetColor(0xe3, 0x77, 0xc2);
-  const int kMpGrey   = TColor::GetColor(0x7f, 0x7f, 0x7f);
-  const int kMpMoss   = TColor::GetColor(0xbc, 0xbd, 0x22);
-  const int kMpCyan   = TColor::GetColor(0x17, 0xbe, 0xcf);
-
-  const std::vector<int> kPalette = {kMpBlue,  kMpOrange, kMpGreen, kMpRed,  kMpPurple,
-                                     kMpBrown, kMpPink,   kMpGrey,  kMpMoss, kMpCyan};
-
-  void draw_label(int ebeam, int pbeam, const std::string_view detector)
-  {
-    auto t = new TPaveText(.15, 0.800, .7, .925, "NB NDC");
-    t->SetFillColorAlpha(kWhite, 0.4);
-    t->SetTextFont(43);
-    t->SetTextSize(25);
-    t->AddText(fmt::format("#bf{{{} }}SIMULATION", detector).c_str());
-    t->AddText(fmt::format("{} GeV on {} GeV", ebeam, pbeam).c_str());
-    t->SetTextAlign(12);
-    t->Draw();
-  }
-
-} // namespace plot
-
-#endif
diff --git a/include/util.h b/include/util.h
deleted file mode 100644
index 6a24b293c26963b4999fc63df821ab3418c694a8..0000000000000000000000000000000000000000
--- a/include/util.h
+++ /dev/null
@@ -1,161 +0,0 @@
-#ifndef UTIL_H
-#define UTIL_H
-
-// TODO: should probably be moved to a global benchmark utility library
-
-#include <algorithm>
-#include <cmath>
-#include <exception>
-#include <fmt/core.h>
-#include <limits>
-#include <string>
-#include <vector>
-
-#include <Math/Vector4D.h>
-
-#include "dd4pod/Geant4ParticleCollection.h"
-#include "eicd/TrackParametersCollection.h"
-
-namespace util {
-
-  // Exception definition for unknown particle errors
-  // FIXME: A utility exception base class should be included in the analysis
-  //        utility library, so we can skip most of this boilerplate
-  class unknown_particle_error : public std::exception {
-  public:
-    unknown_particle_error(std::string_view particle) : m_particle{particle} {}
-    virtual const char* what() const throw()
-    {
-      return fmt::format("Unknown particle type: {}", m_particle).c_str();
-    }
-    virtual const char* type() const throw() { return "unknown_particle_error"; }
-
-  private:
-    const std::string m_particle;
-  };
-
-  // Simple function to return the appropriate PDG mass for the particles
-  // we care about for this process.
-  // FIXME: consider something more robust (maybe based on hepPDT) to the
-  //        analysis utility library
-  inline double get_pdg_mass(std::string_view part)
-  {
-    if (part == "electron") {
-      return 0.0005109989461;
-    } else if (part == "muon") {
-      return .1056583745;
-    } else if (part == "jpsi") {
-      return 3.0969;
-    } else if (part == "upsilon") {
-      return 9.49630;
-    } else if (part == "proton"){
-      return 0.938272;
-    } else {
-      throw unknown_particle_error{part};
-    }
-  }
-
-  // Get a vector of 4-momenta from raw tracking info, using an externally
-  // provided particle mass assumption.
-  inline auto momenta_from_tracking(const std::vector<eic::TrackParametersData>& tracks,
-                                    const double                                 mass)
-  {
-    std::vector<ROOT::Math::PxPyPzMVector> momenta{tracks.size()};
-    // transform our raw tracker info into proper 4-momenta
-    std::transform(tracks.begin(), tracks.end(), momenta.begin(), [mass](const auto& track) {
-      // make sure we don't divide by zero
-      if (fabs(track.qOverP) < 1e-9) {
-        return ROOT::Math::PxPyPzMVector{};
-      }
-      const double p  = fabs(1. / track.qOverP);
-      const double px = p * cos(track.phi) * sin(track.theta);
-      const double py = p * sin(track.phi) * sin(track.theta);
-      const double pz = p * cos(track.theta);
-      return ROOT::Math::PxPyPzMVector{px, py, pz, mass};
-    });
-    return momenta;
-  }
-
-  // Get a vector of 4-momenta from the simulation data.
-  // TODO: Add PID selector (maybe using ranges?)
-  inline auto momenta_from_simulation(const std::vector<dd4pod::Geant4ParticleData>& parts)
-  {
-    std::vector<ROOT::Math::PxPyPzMVector> momenta{parts.size()};
-    // transform our simulation particle data into 4-momenta
-    std::transform(parts.begin(), parts.end(), momenta.begin(), [](const auto& part) {
-      return ROOT::Math::PxPyPzMVector{part.psx, part.psy, part.psz, part.mass};
-    });
-    return momenta;
-  }
-
-  // Find the decay pair candidates from a vector of particles (parts),
-  // with invariant mass closest to a desired value (pdg_mass)
-  inline std::pair<ROOT::Math::PxPyPzMVector, ROOT::Math::PxPyPzMVector>
-  find_decay_pair(const std::vector<ROOT::Math::PxPyPzMVector>& parts, const double pdg_mass)
-  {
-    int    first     = -1;
-    int    second    = -1;
-    double best_mass = -1;
-
-    // go through all particle combinatorics, calculate the invariant mass
-    // for each combination, and remember which combination is the closest
-    // to the desired pdg_mass
-    for (size_t i = 0; i < parts.size(); ++i) {
-      for (size_t j = i + 1; j < parts.size(); ++j) {
-        const double new_mass{(parts[i] + parts[j]).mass()};
-        if (fabs(new_mass - pdg_mass) < fabs(best_mass - pdg_mass)) {
-          first     = i;
-          second    = j;
-          best_mass = new_mass;
-        }
-      }
-    }
-    if (first < 0) {
-      return {{}, {}};
-    }
-    return {parts[first], parts[second]};
-  }
-
-  // Calculate the magnitude of the momentum of a vector of 4-vectors
-  inline auto mom(const std::vector<ROOT::Math::PxPyPzMVector>& momenta)
-  {
-    std::vector<double> P(momenta.size());
-    // transform our raw tracker info into proper 4-momenta
-    std::transform(momenta.begin(), momenta.end(), P.begin(),
-                   [](const auto& mom) { return mom.P(); });
-    return P;
-  }
-  // Calculate the transverse momentum of a vector of 4-vectors
-  inline auto pt(const std::vector<ROOT::Math::PxPyPzMVector>& momenta)
-  {
-    std::vector<double> pt(momenta.size());
-    // transform our raw tracker info into proper 4-momenta
-    std::transform(momenta.begin(), momenta.end(), pt.begin(),
-                   [](const auto& mom) { return mom.pt(); });
-    return pt;
-  }
-
-  // Calculate the azimuthal angle phi of a vector of 4-vectors
-  inline auto phi(const std::vector<ROOT::Math::PxPyPzMVector>& momenta)
-  {
-    std::vector<double> phi(momenta.size());
-    // transform our raw tracker info into proper 4-momenta
-    std::transform(momenta.begin(), momenta.end(), phi.begin(),
-                   [](const auto& mom) { return mom.phi(); });
-    return phi;
-  }
-  // Calculate the pseudo-rapidity of a vector of particles
-  inline auto eta(const std::vector<ROOT::Math::PxPyPzMVector>& momenta)
-  {
-    std::vector<double> eta(momenta.size());
-    // transform our raw tracker info into proper 4-momenta
-    std::transform(momenta.begin(), momenta.end(), eta.begin(),
-                   [](const auto& mom) { return mom.eta(); });
-    return eta;
-  }
-
-  //=========================================================================================================
-
-} // namespace util
-
-#endif
diff --git a/options/.gitignore b/options/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/options/env.sh b/options/env.sh
deleted file mode 100755
index 4b7b1e7660dae23b5a9ad23661236024b2b96b30..0000000000000000000000000000000000000000
--- a/options/env.sh
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/bin/bash
-
-## =============================================================================
-## Global configuration variables for the benchmark scripts
-## The script defines the following environment variables that are meant to
-## be overriden by the Gitlab continuous integration (CI)
-##
-##  - JUGGLER_DETECTOR:       detector package to be used for the benchmark
-##  - JUGGLER_N_EVENTS:       #events processed by simulation/reconstruction
-##  - JUGGLER_INSTALL_PREFIX: location where Juggler (digi/recon) is installed
-##  - JUGGLER_N_THREADS:      Number of threads/processes to spawn in parallel
-##  - JUGGLER_RNG_SEED:       Random seed for the RNG
-##
-## It also defines the following additional variables for internal usage
-##  - LOCAL_PREFIX:           prefix for packages installed during the benchmark
-##  - DETECTOR_PREFIX:        prefix for the detector definitions
-##  - DETECTOR_PATH:          actual path with the detector definitions
-##
-## Finally, it makes sure LOCAL_PREFIX and JUGGLER_PREFIX are added to PATH
-## and LD_LIBRARY_PATH
-## =============================================================================
-
-echo "Setting up the Physics Benchmarks environment"
-
-## =============================================================================
-## Default variable definitions, normally these should be set
-## by the CI. In case of local development you may want to change these
-## in case you would like to modify the detector package or
-## number of events to be analyzed during the benchmark
-
-## Detector package to be used during the benchmark process
-if [ ! -n  "${JUGGLER_DETECTOR}" ] ; then 
-  export JUGGLER_DETECTOR="topside"
-fi
-
-if [ ! -n  "${JUGGLER_DETECTOR_VERSION}" ] ; then 
-  export JUGGLER_DETECTOR_VERSION="master"
-fi
-
-
-## Number of events that will be processed by the reconstruction
-if [ ! -n  "${JUGGLER_N_EVENTS}" ] ; then 
-  export JUGGLER_N_EVENTS=100
-fi
-
-## Maximum number of threads or processes a single pipeline should use
-## (this is not enforced, but the different pipeline scripts should use
-##  this to guide the number of parallel processes or threads they 
-##  spawn).
-if [ ! -n "${JUGGLER_N_THREADS}" ]; then
-  export JUGGLER_N_THREADS=10
-fi
-
-## Random seed for event generation, should typically not be changed for
-## reproductability.
-if [ ! -n "${JUGGLER_RNG_SEED}" ]; then
-  export JUGGLER_RNG_SEED=1
-fi
-
-## Install prefix for juggler, needed to locate the Juggler xenv files.
-## Also used by the CI as install prefix for other packages where needed.
-## You should not have to touch this. Note that for local usage a different 
-## prefix structure is automatically used.
-if [ ! -n  "${JUGGLER_INSTALL_PREFIX}" ] ; then 
-  export JUGGLER_INSTALL_PREFIX="/usr/local"
-fi
-## Ensure the juggler prefix is an absolute path
-export JUGGLER_INSTALL_PREFIX=`realpath ${JUGGLER_INSTALL_PREFIX}`
-
-
-## Location of local data for pass data from job to job within pipeline.
-## Not saved as artifacts.
-if [ ! -n  "${LOCAL_DATA_PATH}" ] ; then 
-  export LOCAL_DATA_PATH="/scratch/${CI_PROJECT_NAME}_${CI_PIPELINE_ID}"
-fi
-
-## =============================================================================
-## Other utility variables that govern how some of the dependent packages
-## are built and installed. You should not have to change these.
-
-## local prefix to be used for local storage of packages
-## downloaded/installed during the benchmark process
-LOCAL_PREFIX=".local"
-mkdir -p ${LOCAL_PREFIX}
-export LOCAL_PREFIX=`realpath ${LOCAL_PREFIX}`
-
-## detector prefix: prefix for the detector definitions
-export DETECTOR_PREFIX="${LOCAL_PREFIX}/detector"
-mkdir -p ${DETECTOR_PREFIX}
-
-## detector path: actual detector definition path
-export DETECTOR_PATH="${DETECTOR_PREFIX}/${JUGGLER_DETECTOR}"
-
-## build dir for ROOT to put its binaries etc.
-export ROOT_BUILD_DIR=$LOCAL_PREFIX/root_build
-
-echo "JUGGLER_DETECTOR:           ${JUGGLER_DETECTOR}"
-echo "JUGGLER_DETECTOR_VERSION:   ${JUGGLER_DETECTOR_VERSION}"
-echo "JUGGLER_N_EVENTS:           ${JUGGLER_N_EVENTS}"
-echo "JUGGLER_N_THREADS:          ${JUGGLER_N_THREADS}"
-echo "JUGGLER_RNG_SEED:           ${JUGGLER_RNG_SEED}"
-echo "JUGGLER_INSTALL_PREFIX:     ${JUGGLER_INSTALL_PREFIX}"
-echo "LOCAL_DATA_PATH:            ${LOCAL_DATA_PATH}"
-echo "LOCAL_PREFIX:               ${LOCAL_PREFIX}"
-echo "DETECTOR_PREFIX:            ${DETECTOR_PREFIX}"
-echo "DETECTOR_PATH:              ${DETECTOR_PATH}"
-echo "ROOT_BUILD_DIR:             ${ROOT_BUILD_DIR}"
-
-## =============================================================================
-## Setup PATH and LD_LIBRARY_PATH to include our prefixes
-echo "Adding JUGGLER_INSTALL_PREFIX and LOCAL_PREFIX to PATH and LD_LIBRARY_PATH"
-export PATH=${JUGGLER_INSTALL_PREFIX}/bin:${LOCAL_PREFIX}/bin:${PATH}
-export LD_LIBRARY_PATH=${JUGGLER_INSTALL_PREFIX}/lib:${LOCAL_PREFIX}/lib:${LD_LIBRARY_PATH}
-
-## =============================================================================
-## That's all!
-echo "Environment setup complete."
diff --git a/tools/dev-shell b/tools/dev-shell
deleted file mode 100755
index 0295f14bd010fb6539d0dc966c7a984bad1fc608..0000000000000000000000000000000000000000
--- a/tools/dev-shell
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/bin/bash
-
-## =============================================================================
-## Setup (if needed) and start a development shell environment on Linux or MacOS
-## =============================================================================
-
-## make sure we launch this script from the project root directory
-PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/..
-pushd ${PROJECT_ROOT}
-
-## We do not load the global development environment here, as this script is 
-## to be executed on a "naked" system outside of any container 
-
-## =============================================================================
-## Step 1: Parse command line options
-
-## do we want to force-update the container (only affects Linux)
-## default: we do not want to do this.
-FORCE_UPDATE=
-
-function print_the_help {
-  echo "USAGE:    ./util/start_dev_shell [-f]"
-  echo "OPTIONS:"
-  echo "          -f,--force    Force-update container (Only affects Linux)"
-  echo "          -h,--help     Print this message"
-  echo ""
-  echo "  This script will setup and launch a containerized development
-  environment"
-  exit
-}
-while [ $# -gt 0 ]
-do
-  key="$1"
-  case $key in
-    -f|--force)
-      FORCE_UPDATE="true"
-      shift # past value
-      ;;
-    -h|--help)
-      print_the_help
-      shift
-      ;;
-    *)    # unknown option
-      echo "unknown option $1"
-      exit 1
-      ;;
-  esac
-done
-
-## get OS type
-OS=`uname -s`
-
-## =============================================================================
-## Step 2: Update container and launch shell
-echo "Launching a containerized development shell"
-
-case ${OS} in
-  Linux)
-    echo "  - Detected OS: Linux"
-    ## Use the same prefix as we use for other local packages
-    export PREFIX=.local/lib
-    if [ ! -f $PREFIX/juggler_latest.sif ] || [ ! -z ${FORCE_UPDATE} ]; then
-      echo "  - Fetching singularity image"
-      mkdir -p $PREFIX
-      wget https://eicweb.phy.anl.gov/eic/juggler/-/jobs/artifacts/master/raw/build/juggler.sif?job=singularity:latest -O $PREFIX/juggler_latest.sif
-    fi
-    echo "  - Using singularity to launch shell..."
-    singularity exec $PREFIX/juggler_latest.sif eic-shell
-    ;;
-  Darwin)
-    echo "  - Detector OS: MacOS"
-    echo "  - Syncing docker container"
-    docker pull sly2j/juggler:latest
-    echo "  - Using docker to launch shell..."
-    docker run -v /Users:/Users -w=$PWD -i -t --rm sly2j/juggler:latest eic-shell
-    ;;
-  *)
-    echo "ERROR: dev shell not available for this OS (${OS})"
-    exit 1
-esac
-
-## =============================================================================
-## Step 3: All done
-echo "Exiting development environment..."
diff --git a/tools/download.sh b/tools/download.sh
deleted file mode 100755
index 47be238b461e3797bf124c46d21aaf33a348f8a5..0000000000000000000000000000000000000000
--- a/tools/download.sh
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/bash
-
-## =============================================================================
-## Download generator & reconstruction artifacts for one or more physics
-## processes.
-## =============================================================================
-
-## make sure we launch this script from the project root directory
-PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/..
-pushd ${PROJECT_ROOT}
-
-PROCS=()
-BRANCH="master"
-
-function print_the_help {
-  echo "USAGE:    -p process [-p process2] [-b git_branch]"
-  echo "OPTIONS:"
-  echo "          -p,--process  Physics process name (can be defined multiple
-  times)."
-  echo "          -b,--branch   Git branch to download artifacts from (D:
-  $BRANCH)"
-  echo "          -h,--help     Print this message"
-  echo ""
-  echo "  This script will download the relevant generator artifacts needed"
-  echo "  for local testing of the benchmarks."
-  exit
-}
-
-while [ $# -gt 0 ]
-do
-  key="$1"
-  case $key in
-    -p|--process)
-      PROCS+=("$2")
-      shift # past argument
-      shift # past value
-      ;;
-    -b|--branch)
-      BRANCH="$2"
-      shift # past argument
-      shift # past value
-      ;;
-    -h|--help)
-      print_the_help
-      shift
-      ;;
-    *)    # unknown option
-      echo "unknown option: $1"
-      exit 1
-      ;;
-  esac
-done
-
-echo "Downloading generator & reconstruction artifacts for one or more physics processes"
-
-if [ ${#PROCS[@]} -eq 0 ]; then
-  echo "ERROR: need one or more processes: -p <process name> "
-  exit 1
-fi
-
-for proc in ${PROCS[@]}; do
-  echo "Dowloading artifacts for $proc (branch: $BRANCH)"
-  wget https://eicweb.phy.anl.gov/EIC/benchmarks/physics_benchmarks/-/jobs/artifacts/$BRANCH/download?job=${proc}:generate -O results_gen.zip
-  ## FIXME this needs to be smarter, probably through more flags...
-  wget https://eicweb.phy.anl.gov/EIC/benchmarks/physics_benchmarks/-/jobs/artifacts/$BRANCH/download?job=${proc}:process -O results_rec.zip
-  echo "Unpacking artifacts..."
-  unzip -u -o results_gen.zip
-  unzip -u -o results_rec.zip
-  echo "Cleaning up..."
-  rm results_???.zip
-done
-popd
-echo "All done"
diff --git a/util/build_detector.sh b/util/build_detector.sh
deleted file mode 100755
index a12d3db9bb0be20ae678f6a4f44399ef80e5cd9e..0000000000000000000000000000000000000000
--- a/util/build_detector.sh
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/bin/bash
-
-## =============================================================================
-## Build and install the JUGGLER_DETECTOR detector package into our local prefix
-## =============================================================================
-
-## make sure we launch this script from the project root directory
-PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/..
-pushd ${PROJECT_ROOT}
-
-## =============================================================================
-## Load the environment variables. To build the detector we need the following
-## variables:
-##
-## - JUGGLER_DETECTOR: the detector package we want to use for this benchmark
-## - LOCAL_PREFIX:     location where local packages should be installed
-## - DETECTOR_PREFIX:  prefix for the detector definitions 
-## - DETECTOR_PATH:    full path for the detector definitions
-##                     this is the same as ${DETECTOR_PREFIX}/${JUGGLER_DETECTOR}
-##
-## You can read options/env.sh for more in-depth explanations of the variables
-## and how they can be controlled.
-source options/env.sh
-
-## =============================================================================
-## Step 1: download/update the detector definitions (if needed)
-pushd ${DETECTOR_PREFIX}
-
-## We need an up-to-date copy of the detector
-## start clean to avoid issues...
-if [ -d ${JUGGLER_DETECTOR} ]; then
-  echo "cleaning up ${JUGGLER_DETECTOR}" 
-  rm -rf ${JUGGLER_DETECTOR}
-fi
-echo "Fetching ${JUGGLER_DETECTOR}"
-git clone -b ${JUGGLER_DETECTOR_VERSION} https://eicweb.phy.anl.gov/EIC/detectors/${JUGGLER_DETECTOR}.git
-#else
-  #echo "Updating ${JUGGLER_DETECTOR}"
-  #pushd ${JUGGLER_DETECTOR}
-  #git pull --ff-only
-  #popd
-#fi
-## We also need an up-to-date copy of the accelerator. For now this is done
-## manually. Down the road we could maybe automize this with cmake
-if [ -d accelerator ]; then
-  echo "cleaning up accelerator"
-  rm -rf accelerator
-fi
-echo "Fetching accelerator"
-git clone https://eicweb.phy.anl.gov/EIC/detectors/accelerator.git
-#else
-#  echo "Updating accelerator"
-#  pushd accelerator
-#  git pull --ff-only
-#  popd
-#fi
-## Now symlink the accelerator definition into the detector definition
-echo "Linking accelerator definition into detector definition"
-ln -s -f ${DETECTOR_PREFIX}/accelerator/eic ${DETECTOR_PATH}/eic
-
-## =============================================================================
-## Step 2: Compile and install the detector definition
-echo "Building and installing the ${JUGGLER_DETECTOR} package"
-
-mkdir -p ${DETECTOR_PREFIX}/build
-pushd ${DETECTOR_PREFIX}/build
-cmake ${DETECTOR_PATH} -DCMAKE_INSTALL_PREFIX=${LOCAL_PREFIX} -DCMAKE_CXX_STANDARD=17 && make -j30 install || exit 1
-cmake ${DETECTOR_PATH} -DCMAKE_INSTALL_PREFIX=${LOCAL_PREFIX}  -DCMAKE_CXX_STANDARD=17  && make -j30 install
-
-## =============================================================================
-## Step 3: That's all!
-echo "Detector build/install complete!"
diff --git a/util/collect_benchmarks.py b/util/collect_benchmarks.py
deleted file mode 100755
index 0af7e9a12b37eb7616be3400d4046f0088bcf223..0000000000000000000000000000000000000000
--- a/util/collect_benchmarks.py
+++ /dev/null
@@ -1,181 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Combine the json files from the individual benchmark tests into
-a final master json file combining all benchmarks.
-
-Benchmark results are expected to be all json files in the results
-directory.
-"""
-
-## Our master definition file, the benchmark project directory
-MASTER_FILE=r'benchmarks/benchmarks.json'
-
-## Our results directory
-RESULTS_PATH=r'results'
-
-## Output json file with all benchmark results
-OUTPUT_FILE=r'results/summary.json'
-
-import argparse
-import json
-from pathlib import Path
-
-## Exceptions for this module
-class Error(Exception):
-    '''Base class for exceptions in this module.'''
-    pass
-class FileNotFoundError(Error):
-    '''File does not exist.
-
-    Attributes:
-        file: the file name
-        message: error message
-    '''
-    def __init__(self, file):
-        self.file = file
-        self.message = 'No such file or directory: {}'.format(file)
-
-class InvalidDefinitionError(Error):
-    '''Raised for missing keys in the definitions.
-
-    Attributes:
-        key: the missing key
-        file: the definition file
-        message: error message
-    '''
-    def __init__(self, key, file):
-        self.key = key
-        self.file = file
-        self.message = "key '{}' not found in '{}'".format(key, file)
-
-class InvalidResultError(Error):
-    '''Raised for invalid benchmark result value.
-
-    Attributes:
-        key: the missing key
-        value: the invalid value
-        file: the benchmark definition file
-        message: error message
-    '''
-    def __init__(self, key, value, file):
-        self.key = key
-        self.value = value
-        self.file = file
-        self.message = "value '{}' for key '{}' invalid in benchmark file '{}'".format(
-                value, key, file)
-    
-def collect_benchmarks():
-    '''Collect all benchmark results and write results to a single file.'''
-    print("Collecting all benchmark results")
-
-    ## load the test definition for this benchmark
-    results = _load_master()
-
-    ## collect the test results
-    results['benchmarks'] = _load_benchmarks()
-    
-    ## calculate aggregate test statistics
-    results = _aggregate_results(results)
-
-    ## save results to output file
-    _save(results)
-
-    ## Summarize results
-    for bm in results['benchmarks']:
-        _print_benchmark(bm)
-    _print_summary(results)
-
-def _load_master():
-    '''Load master definition.'''
-    master_file = Path(MASTER_FILE)
-    if not master_file.exists():
-        raise FileNotFoundError(master_file)
-    print('  --> Loading master definition from:', master_file)
-    results = None
-    with master_file.open() as f:
-        results = json.load(f)
-    ## ensure this is a valid benchmark file
-    for key in ('name', 'title', 'description'):
-        if not key in results:
-            raise InvalidDefinitionError('target', master_file)
-    return results
-
-def _load_benchmarks():
-    '''Load all benchmark results from the results folder.'''
-    print('  --> Collecting all benchmarks')
-    rootdir = Path(RESULTS_PATH)
-    results = []
-    for file in rootdir.glob('*.json'):
-        print('    --> Loading file:', file, '... ', end='')
-        with open(file) as f:
-            bm = json.load(f)
-            ## skip files that don't include test results
-            if not 'tests' in bm:
-                print('skipped (does not contain benchmark results).')
-                continue
-            ## check if these are valid benchmark results,
-            ## raise exception otherwise
-            for key in ('name', 'title', 'description', 'target', 'n_tests',
-                    'n_pass', 'n_fail', 'n_error', 'maximum', 'sum', 'value',
-                    'result'):
-                if not key in bm:
-                    raise InvalidDefinitionError(key, file)
-            if bm['result'] not in ('pass', 'fail', 'error'):
-                raise InvalidResultError('result', bm['result'], file)
-            ## Append to our test results
-            results.append(bm)
-            print('done')
-    return results
-
-def _aggregate_results(results):
-    '''Aggregate benchmark results.'''
-    print('  --> Aggregating benchmark statistics')
-    results['n_benchmarks'] = len(results['benchmarks'])
-    results['n_pass'] = len([1 for t in results['benchmarks'] if t['result'] == 'pass'])
-    results['n_fail'] = len([1 for t in results['benchmarks'] if t['result'] == 'fail'])
-    results['n_error'] = len([1 for t in results['benchmarks'] if t['result'] == 'error'])
-    if results['n_error'] > 0:
-        results['result'] = 'error'
-    elif results['n_fail'] == 0:
-        results['result'] = 'pass'
-    else:
-        results['result'] = 'fail'
-    return results
-
-def _save(results):
-    '''Save aggregated benchmark results'''
-    ofile = Path(OUTPUT_FILE)
-    print('  --> Saving results to:', ofile)
-    with ofile.open('w') as f:
-        json.dump(results, f, indent=4)
-
-def _print_benchmark(bm):
-    '''Print benchmark summary to the terminal.'''
-    print('====================================================================')
-    print('  Summary for:', bm['title'])
-    print('  Pass: {}, Fail: {}, Error: {} out of {} total tests'.format(
-        bm['n_pass'], bm['n_fail'], bm['n_error'],
-        bm['n_tests']))
-    print('  Weighted sum: {} / {}'.format(bm['sum'], bm['maximum']))
-    print('  kBenchmark value: {} (target: {})'.format(
-        bm['value'], bm['target']))
-    print('  ===> status:', bm['result'])
-
-def _print_summary(results):
-    '''Print master benchmark summary to the terminal.'''
-    print('====================================================================')
-    print('MASTER BENCHMARK SUMMARY FOR:', results['title'].upper())
-    print('Pass: {}, Fail: {}, Error: {} out of {} total benchmarks'.format(
-        results['n_pass'], results['n_fail'], results['n_error'],
-        results['n_benchmarks']))
-    print('===> status:', results['result'])
-    print('====================================================================')
-
-
-if __name__ == "__main__":
-    try:
-        collect_benchmarks()
-    except Error as e:
-        print()
-        print('ERROR', e.message)
diff --git a/util/collect_tests.py b/util/collect_tests.py
deleted file mode 100755
index 4d860ca79d9f996204a5ca9dc447fa10ed8ec4f4..0000000000000000000000000000000000000000
--- a/util/collect_tests.py
+++ /dev/null
@@ -1,204 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Collect the json files from individual benchmark tests into
-a larger json file that combines all benchmark information,
-and do additional accounting for the benchmark.
-
-Tests results are expected to have the following file name and directory
-structure:
-   results/<BENCHMARK_NAME>/**/<SOME_NAME>.json
-where ** implies we check recursively check all sub-directories of <BENCHMARK_NAME>
-
-Internally, we will look for the "tests" keyword in each of these
-files to identify them as benchmark components.
-"""
-
-## Our benchmark definition file, stored in the benchmark root directory
-BENCHMARK_FILE=r'benchmarks/{}/benchmark.json'
-
-## Our benchmark results directory
-RESULTS_PATH=r'results/{}'
-
-## Output json file with benchmark results
-OUTPUT_FILE=r'results/{}.json'
-
-import argparse
-import json
-from pathlib import Path
-
-## Exceptions for this module
-class Error(Exception):
-    '''Base class for exceptions in this module.'''
-    pass
-class FileNotFoundError(Exception):
-    '''File does not exist.
-
-    Attributes:
-        file: the file name
-        message: error message
-    '''
-    def __init__(self, file):
-        self.file = file
-        self.message = 'No such file or directory: {}'.format(file)
-
-class InvalidBenchmarkDefinitionError(Exception):
-    '''Raised for missing keys in the benchmark definition.
-
-    Attributes:
-        key: the missing key
-        file: the benchmark definition file
-        message: error message
-    '''
-    def __init__(self, key, file):
-        self.key = key
-        self.file = file
-        self.message = "key '{}' not found in benchmark file '{}'".format(key, file)
-
-class InvalidTestDefinitionError(Exception):
-    '''Raised for missing keys in the test result.
-
-    Attributes:
-        key: the missing key
-        file: the test result file
-        message: error message
-    '''
-    def __init__(self, key, file):
-        self.key = key
-        self.file = file
-        self.message = "key '{}' not found in test file '{}'".format(key, file)
-class InvalidTestResultError(Exception):
-    '''Raised for invalid test result value.
-
-    Attributes:
-        key: the missing key
-        value: the invalid value
-        file: the benchmark definition file
-        message: error message
-    '''
-    def __init__(self, key, value, file):
-        self.key = key
-        self.value = value
-        self.file = file
-        self.message = "value '{}' for key '{}' invalid in test file '{}'".format(
-                value, key, file)
-    
-    
-parser = argparse.ArgumentParser()
-parser.add_argument(
-        'benchmark',
-        action='append',
-        help='One or more benchmarks for which to collect test results.')
-
-def collect_results(benchmark):
-    '''Collect benchmark tests and write results to file.'''
-    print("Collecting results for benchmark '{}'".format(benchmark))
-
-    ## load the test definition for this benchmark
-    results = _load_benchmark(benchmark)
-
-    ## collect the test results
-    results['tests'] = _load_tests(benchmark)
-    
-    ## calculate aggregate test statistics
-    results = _aggregate_results(results)
-
-    ## save results to output file
-    _save(benchmark, results)
-
-    ## Summarize results
-    _print_summary(results)
-
-def _load_benchmark(benchmark):
-    '''Load benchmark definition.'''
-    benchfile = Path(BENCHMARK_FILE.format(benchmark))
-    if not benchfile.exists():
-        raise FileNotFoundError(benchfile)
-    print('  --> Loading benchmark definition from:', benchfile)
-    results = None
-    with benchfile.open() as f:
-        results = json.load(f)
-    ## ensure this is a valid benchmark file
-    for key in ('name', 'title', 'description', 'target'):
-        if not key in results:
-            raise InvalidBenchmarkDefinitionError('target', benchfile)
-    return results
-
-def _load_tests(benchmark):
-    '''Loop over all test results in benchmark folder and return results.'''
-    print('  --> Collecting all test results')
-    rootdir = Path(RESULTS_PATH.format(benchmark))
-    results = []
-    for file in rootdir.glob('**/*.json'):
-        print('    --> Loading file:', file, '... ', end='')
-        with open(file) as f:
-            new_results = json.load(f)
-            ## skip files that don't include test results
-            if not 'tests' in new_results:
-                print('not a test result')
-                continue
-            ## check if these are valid test results,
-            ## raise exception otherwise
-            for test in new_results['tests']:
-                for key in ('name', 'title', 'description', 'quantity', 'target',
-                        'value', 'result'):
-                    if not key in test:
-                        raise InvalidTestDefinitionError(key, file)
-                if test['result'] not in ('pass', 'fail', 'error'):
-                    raise InvalidTestResultError('result', test['result'], file)
-                ## ensure 'weight' key present, defaulting to 1 in needed
-                if not 'weight' in test:
-                    test['weight'] = 1.
-                ## Append to our test results
-                results.append(test)
-            print('done')
-    return results
-
-def _aggregate_results(results):
-    '''Aggregate test results for our benchmark.'''
-    print('  --> Aggregating benchmark statistics')
-    results['target'] = float(results['target'])
-    results['n_tests'] = len(results['tests'])
-    results['n_pass'] = len([1 for t in results['tests'] if t['result'] == 'pass'])
-    results['n_fail'] = len([1 for t in results['tests'] if t['result'] == 'fail'])
-    results['n_error'] = len([1 for t in results['tests'] if t['result'] == 'error'])
-    results['maximum'] = sum([t['weight'] for t in results['tests']])
-    results['sum'] = sum([t['weight'] for t in results['tests'] if t['result'] == 'pass'])
-    if (results['n_tests'] > 0):
-        results['value'] = results['sum'] / results['maximum']
-        if results['n_error'] > 0:
-            results['result'] = 'error'
-        elif results['value'] >= results['target']:
-            results['result'] = 'pass'
-        else:
-            results['result'] = 'fail'
-    else:
-        results['value'] = -1
-        results['result'] = 'error'
-    return results
-
-def _save(benchmark, results):
-    '''Save benchmark results'''
-    ofile = Path(OUTPUT_FILE.format(benchmark))
-    print('  --> Saving benchmark results to:', ofile)
-    with ofile.open('w') as f:
-        json.dump(results, f, indent=4)
-
-def _print_summary(results):
-    '''Print benchmark summary to the terminal.'''
-    print('====================================================================')
-    print('Summary for:', results['title'])
-    print('Pass: {}, Fail: {}, Error: {} out of {} total tests'.format(
-        results['n_pass'], results['n_fail'], results['n_error'],
-        results['n_tests']))
-    print('Weighted sum: {} / {}'.format(results['sum'], results['maximum']))
-    print('Benchmark value: {} (target: {})'.format(
-        results['value'], results['target']))
-    print('===> status:', results['result'])
-    print('====================================================================')
-
-
-if __name__ == "__main__":
-    args = parser.parse_args()
-    for benchmark in args.benchmark:
-        collect_results(benchmark)
diff --git a/util/compile_analyses.py b/util/compile_analyses.py
deleted file mode 100755
index 153f2ea2f61429b6864a21b3ad625e6b53373ba3..0000000000000000000000000000000000000000
--- a/util/compile_analyses.py
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Compile all root analysis scripts under
-benchmarks/<BENCHMARK>/analysis/*.cxx
-
-Doing this step here rather than during the main benchmark script has
-multiple advantages:
-    1. Get feedback on syntax errors early on, without wasting compute resources
-    2. Avoid race conditions for large benchmarks run in parallel
-    3. Make it easier to properly handle the root build directory, as
-       this has to exist prior to our attempt to compile, else all will
-       fail (this is probably an old bug in root...)
-
-Analysis scripts are expected to have extension 'cxx' and be located in the analysis
-subdirectory
-"""
-
-## Our analysis path and file extension for glob
-ANALYSIS_PATH=r'benchmarks/{}/analysis'
-ANALYSIS_EXT = r'cxx'
-
-import argparse
-import os
-from pathlib import Path
-
-## Exceptions for this module
-class Error(Exception):
-    '''Base class for exceptions in this module.'''
-    pass
-
-class PathNotFoundError(Exception):
-    '''Path does not exist.
-
-    Attributes:
-        path: the path name
-        message: error message
-    '''
-    def __init__(self, path):
-        self.file = file
-        self.message = 'No such directory: {}'.format(file)
-class NoAnalysesFoundError(Exception):
-    '''Did not find any analysis scripts to complile
-
-    Attributes:
-        path: the analysis path
-        message: error message
-    '''
-    def __init__(self, path):
-        self.file = file
-        self.message = 'No analysis found (extension \'{}\' in path: {}'.format(file,
-                ANALYSIS_EXT)
-
-class CompilationError(Exception):
-    '''Raised when we failed to compile an analysis script
-
-    Attributes:
-        file: analysis file name
-        path: analysis path
-        message: error message
-    '''
-    def __init__(self, file):
-        self.file = file
-        self.message = "Analysis '{}' failed to compile".format(file)
-
-parser = argparse.ArgumentParser()
-parser.add_argument(
-        'benchmark',
-        help='A benchmarks for which to compile the analysis scripts.')
-
-def compile_analyses(benchmark):
-    '''Compile all analysis scripts for a benchmark.'''
-    print("Compiling all analyis scripts for '{}'".format(benchmark))
-
-    ## Ensure our build directory exists
-    _init_build_dir(benchmark)
-
-    ## Get a list of all analysis scripts
-    _compile_all(benchmark)
-
-    ## All done!
-    print('All analyses for', benchmark, 'compiled successfully')
-
-def _init_build_dir(benchmark):
-    '''Initialize our ROOT build directory (if using one).'''
-    print(' --> Initializing ROOT build directory ...')
-    build_prefix = os.getenv('ROOT_BUILD_DIR')
-    if build_prefix is None:
-        print('    --> ROOT_BUILD_DIR not set, no action needed.')
-        return
-    ## deduce the root build directory
-    pwd = os.getenv('PWD')
-    build_dir = '{}/{}/{}'.format(build_prefix, pwd, ANALYSIS_PATH.format(benchmark))
-    print("    --> Ensuring directory '{}' exists".format(build_dir))
-    os.system('mkdir -p {}'.format(build_dir))
-
-def _compile_all(benchmark):
-    '''Compile all analysis for this benchmark.'''
-    print(' --> Compiling analysis scripts')
-    anadir = Path(ANALYSIS_PATH.format(benchmark))
-    if not anadir.exists():
-        raise PathNotFoundError(anadir)
-    ana_list = []
-    for file in anadir.glob('*.{}'.format(ANALYSIS_EXT)):
-        ana_list.append(file)
-        print('    --> Compiling:', file, flush=True)
-        err = os.system(_compile_cmd(file))
-        if err:
-            raise CompilationError(file)
-    if len(ana_list) == 0:
-        raise NoAnalysesFoundError(anadir)
-
-def _compile_cmd(file):
-    '''Return a one-line shell command to compile an analysis script.'''
-    return r'bash -c "root -q -b -e \".L {}+\""'.format(file)
-
-if __name__ == "__main__":
-    args = parser.parse_args()
-    compile_analyses(args.benchmark)
diff --git a/util/parse_cmd.sh b/util/parse_cmd.sh
deleted file mode 100755
index 04028d8958d03df22f7b2d964a75acf2d9b47317..0000000000000000000000000000000000000000
--- a/util/parse_cmd.sh
+++ /dev/null
@@ -1,127 +0,0 @@
-#!/bin/bash
-
-## =============================================================================
-## Generic utility script to parse command line arguments for the various
-## bash scripts that control the CI. This script should be source'd with
-## command line arguments from a bash-like (non-POSIX) shell such as
-## bash or zsh.
-##
-## To control some of the functionality of the script, you can set the following
-## environment variables prior to calling the script:
-##   - REQUIRE_DECAY:     require the --decay flag to be set
-## =============================================================================
-
-## Commented out because this should be taken care of by the 
-## calling script to not enforce a fixed directory structure.
-## make sure we launch this script from the project root directory
-#PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/..
-#pushd ${PROJECT_ROOT}
-
-## =============================================================================
-## Step 1: Process the command line arguments
-
-function print_the_help {
-  echo "USAGE:    --ebeam E --pbeam E --config C1 --decay D2"
-  echo "          [--config C2 --decay D2 --decay D3 ...]"
-  echo "REQUIRED ARGUMENTS:"
-  echo "          --ebeam       Electron beam energy"
-  echo "          --pbeam       Ion beam energy"
-  echo "          --config      Generator configuration identifiers (at least one)"
-  if [ ! -z ${REQUIRE_DECAY} ]; then
-    echo "        --decay       Specific decay particle (e.g. muon)."
-  fi
-  if [ ! -z ${REQUIRE_LEADING} ]; then
-    echo "        --leading     Leading particle of interest (e.g. jpsi)."
-  fi
-  echo "          -h,--help     Print this message"
-  echo ""
-  echo "  Generate multiple monte carlo samples for a desired process." 
-  exit
-}
-
-## Required variables
-EBEAM=
-PBEAM=
-DECAYS=
-CONFIG=
-
-while [ $# -gt 0 ]
-do
-  key="$1"
-  case $key in
-    --config)
-      CONFIG="$2"
-      shift # past argument
-      shift # past value
-      ;;
-    --ebeam)
-      EBEAM="$2"
-      shift # past argument
-      shift # past value
-      ;;
-    --pbeam)
-      PBEAM="$2"
-      shift # past argument
-      shift # past value
-      ;;
-    --leading)
-      LEADING="$2"
-      shift # past argument
-      shift # past value
-      ;;
-    --decay)
-      DECAY="$2"
-      shift # past argument
-      shift # past value
-      ;;
-    -h|--help)
-      print_the_help
-      exit 0
-      ;;
-    *)    # unknown option
-      echo "unknown option"
-      exit 1
-      ;;
-  esac
-done
-
-if [ -z $CONFIG ]; then
-  echo "ERROR: CONFIG not defined: --config <config>"
-  print_the_help
-  exit 1
-elif [ -z $EBEAM ]; then
-  echo "ERROR: EBEAM not defined: --ebeam <energy>"
-  print_the_help
-  exit 1
-elif [ -z $PBEAM ]; then
-  echo "ERROR: PBEAM not defined: --pbeam <energy>"
-  print_the_help
-  exit 1
-elif [ -z $LEADING ] && [ ! -z $REQUIRE_LEADING ]; then
-  echo "ERROR: LEADING not defined: --leading <channel>"
-  print_the_help
-  exit 1
-elif [ ! -z $LEADING ] && [ -z $REQUIRE_LEADING ]; then
-  echo "ERROR: LEADING flag specified but not required"
-  print_the_help
-  exit 1
-elif [ -z $DECAY ] && [ ! -z $REQUIRE_DECAY ]; then
-  echo "ERROR: DECAY not defined: --decay <channel>"
-  print_the_help
-  exit 1
-elif [ ! -z $DECAY ] && [ -z $REQUIRE_DECAY ]; then
-  echo "ERROR: DECAY flag specified but not required"
-  print_the_help
-  exit 1
-fi
-
-## Export the configured variables
-export CONFIG
-export EBEAM
-export PBEAM
-if [ ! -z $REQUIRE_LEADING ]; then
-  export LEADING
-fi
-if [ ! -z $REQUIRE_DECAY ]; then
-  export DECAY
-fi
diff --git a/util/print_env.sh b/util/print_env.sh
deleted file mode 100755
index 7a4a8935ca7a8ad4262830af5f3ecbd8c76a6117..0000000000000000000000000000000000000000
--- a/util/print_env.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-
-echo "JUGGLER_TAG:                ${JUGGLER_TAG}"
-echo "JUGGLER_DETECTOR:           ${JUGGLER_DETECTOR}"
-echo "JUGGLER_DETECTOR_VERSION:   ${JUGGLER_DETECTOR_VERSION}"
-echo "JUGGLER_N_EVENTS:           ${JUGGLER_N_EVENTS}"
-echo "JUGGLER_N_THREADS:          ${JUGGLER_N_THREADS}"
-echo "JUGGLER_RNG_SEED:           ${JUGGLER_RNG_SEED}"
-echo "JUGGLER_INSTALL_PREFIX:     ${JUGGLER_INSTALL_PREFIX}"
-echo "LOCAL_DATA_PATH:            ${LOCAL_DATA_PATH}"
-echo "LOCAL_PREFIX:               ${LOCAL_PREFIX}"
-echo "DETECTOR_PREFIX:            ${DETECTOR_PREFIX}"
-echo "DETECTOR_PATH:              ${DETECTOR_PATH}"
diff --git a/util/run_many.py b/util/run_many.py
deleted file mode 100755
index ccb7e83a7f81d1bc502fcddb32900e5a31eebdb1..0000000000000000000000000000000000000000
--- a/util/run_many.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env python3
-
-""" 
-This script will run a CI generator or processing script for multiple configurations.
-
-Author: Sylvester Joosten <sjoosten@anl.gov>
-"""
-
-import os
-import argparse
-from multiprocessing import Pool, get_context
-from tempfile import NamedTemporaryFile
-
-class InvalidArgumentError(Exception):
-    pass
-
-parser = argparse.ArgumentParser()
-parser.add_argument(
-        'command',
-        help="Script to be launched in parallel")
-parser.add_argument(
-        '--energy', '-e',
-        dest='energies',
-        action='append',
-        help='One or more beam energy pairs (e.g. 10x100)',
-        required=True)
-parser.add_argument(
-        '--config', '-c',
-        dest='configs',
-        action='append',
-        help='One or more configurations',
-        required=True)
-parser.add_argument(
-        '--leading',
-        dest='leads',
-        action='append',
-        help='One or more leading particles(opt.)',
-        required=False)
-parser.add_argument(
-        '--decay',
-        dest='decays',
-        action='append',
-        help='One or more decay channels (opt.)',
-        required=False)
-parser.add_argument(
-        '--nproc',
-        dest='nproc',
-        default=5,
-        type=int,
-        help='Number of processes to launch in parallel',
-        required=False)
-
-def worker(command):
-    '''Execute the command in a system call, with the supplied argument string.'''
-    ## use a temporary file to capture the terminal output, and then
-    ## print the terminal output once the command finishes
-    with NamedTemporaryFile() as f:
-        cmd = [command, ' 2>&1 >', f.name]
-        cmd = ' '.join(cmd)
-        print("Executing '{}'".format(cmd))
-        ret = os.system(cmd)
-        with open(f.name) as log:
-            print(log.read())
-        return ret
-
-if __name__ == '__main__':
-    args = parser.parse_args()
-    print('Launching CI script in parallel for multiple settings')
-    for e in args.energies:
-        beam_setting = e.split('x')
-        if not beam_setting[0].isnumeric() or not beam_setting[1].isnumeric():
-            print("Error: invalid beam energy setting:", e)
-            raise InvalidArgumentError
-
-    if not os.path.exists(args.command):
-        print("Error: Script not found:", args.command)
-        raise InvalidArgumentError
-
-    if args.nproc < 1 or args.nproc > 50:
-        print("Error: Invalid process limit (should be 1-50):", args.nproc)
-        raise InvalidArgumentError
-
-    print(' - command: {}'.format(args.command))
-    print(' - energies: {}'.format(args.energies))
-    print(' - config: {}'.format(args.configs))
-    print(' - nproc: {}'.format(args.nproc))
-    if (args.leads):
-        print(' - leading: {}'.format(args.leads))
-    if (args.decays):
-        print(' - decay: {}'.format(args.decays))
-
-    ## Expand our command and argument list for all combinatorics
-    cmds = []
-    decays = args.decays if args.decays else [None]
-    leads = args.leads if args.leads else [None]
-    for e in args.energies:
-        for c in args.configs:
-            for l in leads:
-                for d in decays:
-                    beam_setting = e.split('x')
-                    cmd = [args.command,
-                           '--ebeam', beam_setting[0],
-                           '--pbeam', beam_setting[1],
-                           '--config', c]
-                    if l is not None:
-                        cmd += ['--leading', l]
-                    if d is not None:
-                        cmd += ['--decay', d]
-                    cmds.append(' '.join(cmd))
-
-    ## create a process pool
-    ## note that I'm using themultiprocessing.get_context function to setup
-    ## a context where subprocesses are created using the new "spawn" process
-    ## which avoids deadlocks that sometimes happen in the default dispatch
-    with get_context('spawn').Pool(processes=args.nproc) as pool:
-        return_values = pool.map(worker, cmds)
-        ## check if we all exited nicely, else exit with status 1
-        if not all(ret == 0 for ret in return_values):
-            n_fail = sum([1 for ret in return_values if ret != 0])
-            print('ERROR, {} of {} jobs failed'.format(n_fail, len(cmds)))
-            print('Return values:', [ret for ret in return_values if ret != 0])
-            exit(1)
-
-    ## That's all!