diff --git a/src/external/cppitertools-1.0/.clang-format b/src/external/cppitertools-1.0/.clang-format
new file mode 100644
index 0000000000000000000000000000000000000000..b5f272f2124d7b35f2bccb505cc1786058cd90dd
--- /dev/null
+++ b/src/external/cppitertools-1.0/.clang-format
@@ -0,0 +1,11 @@
+---
+Language:        Cpp
+BasedOnStyle:  Google
+AlignAfterOpenBracket: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortLoopsOnASingleLine: false
+BreakBeforeBinaryOperators: NonAssignment
+DerivePointerAlignment: false
+NamespaceIndentation: All
+...
+
diff --git a/src/external/cppitertools-1.0/.gitignore b/src/external/cppitertools-1.0/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ac51a054d2da1e1215b8e55951ca7649a3dbe984
--- /dev/null
+++ b/src/external/cppitertools-1.0/.gitignore
@@ -0,0 +1 @@
+bazel-*
diff --git a/src/external/cppitertools-1.0/BUILD b/src/external/cppitertools-1.0/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..8b162b867c871271adbd78c93b1ac027dae8ae74
--- /dev/null
+++ b/src/external/cppitertools-1.0/BUILD
@@ -0,0 +1,42 @@
+cc_library(
+    name = "cppitertools",
+    hdrs = [
+        "accumulate.hpp",
+        "chain.hpp",
+        "chunked.hpp",
+        "combinations.hpp",
+        "combinations_with_replacement.hpp",
+        "compress.hpp",
+        "count.hpp",
+        "cycle.hpp",
+        "dropwhile.hpp",
+        "enumerate.hpp",
+        "filter.hpp",
+        "filterfalse.hpp",
+        "groupby.hpp",
+        "imap.hpp",
+        "itertools.hpp",
+        "permutations.hpp",
+        "powerset.hpp",
+        "product.hpp",
+        "range.hpp",
+        "repeat.hpp",
+        "reversed.hpp",
+        "slice.hpp",
+        "sliding_window.hpp",
+        "sorted.hpp",
+        "starmap.hpp",
+        "takewhile.hpp",
+        "unique_everseen.hpp",
+        "unique_justseen.hpp",
+        "zip.hpp",
+        "zip_longest.hpp",
+    ],
+    srcs = [
+        "internal/iter_tuples.hpp",
+        "internal/iterator_wrapper.hpp",
+        "internal/iteratoriterator.hpp",
+        "internal/iterbase.hpp",
+    ],
+    visibility = ["//visibility:public"],
+)
diff --git a/src/external/cppitertools-1.0/LICENSE.md b/src/external/cppitertools-1.0/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..3d3c6ef0ca323f9b4dfd14b890252e1185253688
--- /dev/null
+++ b/src/external/cppitertools-1.0/LICENSE.md
@@ -0,0 +1,23 @@
+Copyright (c) 2013, Ryan Haining, Aaron Josephs, Google
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+  Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+  Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimer in the documentation and/or
+  other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/external/cppitertools-1.0/README.md b/src/external/cppitertools-1.0/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..6bf1907cd0d84eda9dfebc4adeb96b464c0181f0
--- /dev/null
+++ b/src/external/cppitertools-1.0/README.md
@@ -0,0 +1,843 @@
+CPPItertools
+============
+Range-based for loop add-ons inspired by the Python builtins and itertools
+library.  Like itertools and the Python3 builtins, this library uses lazy
+evaluation wherever possible.
+
+*Note*: Everthing is inside the `iter` namespace.
+
+Follow [@cppitertools](https://twitter.com/cppitertools) for updates.
+
+#### Table of Contents
+[range](#range)<br />
+[enumerate](#enumerate)<br />
+[zip](#zip)<br />
+[zip\_longest](#zip)<br />
+[imap](#imap)<br />
+[filter](#filter)<br />
+[filterfalse](#filterfalse)<br />
+[unique\_everseen](#unique_everseen)<br />
+[unique\_justseen](#unique_justseen)<br />
+[takewhile](#takewhile)<br />
+[dropwhile](#dropwhile)<br />
+[cycle](#cycle)<br />
+[repeat](#repeat)<br />
+[count](#count)<br />
+[groupby](#groupby)<br />
+[starmap](#starmap)<br />
+[accumulate](#accumulate)<br />
+[compress](#compress)<br />
+[sorted](#sorted)<br />
+[chain](#chain)<br />
+[chain.from\_iterable](#chainfrom_iterable)<br />
+[reversed](#reversed)<br />
+[slice](#slice)<br />
+[sliding\_window](#sliding_window)<br />
+[chunked](#chunked)<br />
+
+##### Combinatoric fuctions
+[product](#product)<br />
+[combinations](#combinations)<br />
+[combinations\_with\_replacement](#combinations_with_replacement)<br />
+[permutations](#permutations)<br />
+[powerset](#powerset)<br />
+
+#### Requirements
+This library is **header-only** and relies only on the C++ standard
+library. The only exception is `zip_longest` which uses `boost::optional`.
+`#include <cppitertools/itertools.hpp>` will include all of the provided
+tools except for `zip_longest` which must be included separately.  You may
+also include individual pieces with the relevant header
+(`#include <cppitertools/enumerate.hpp>` for example).
+
+#### Requirements of passed objects
+Most itertools will work with iterables using InputIterators and not copy
+or move any underlying elements.  The itertools that need ForwardIterators or
+have additional requirements are noted in this document. However, the cases
+should be fairly obvious: any time an element needs to appear multiple times
+(as in `combinations` or `cycle`) or be looked at more than once (specifically,
+`sorted`).
+This library takes every effort to rely on as little as possible from the
+underlying iterables, but if anything noteworthy is needed it is described
+in this document.
+
+#### Guarantees of implementations
+By implementations I mean the objects returned by the API's functions. All of
+the implementation classes are move-constructible, not copy-constructible,
+not assignable. All iterators that work over another iterable are tagged
+as InputIterators and behave as such.
+
+#### Feedback
+If you find anything not working as you expect, not compiling when you believe
+it should, a divergence from the python itertools behavior, or any sort of
+error, please let me know. The preferable means would be to open an issue on
+github. If you want to talk about an issue that you don't feel would be
+appropriate as a github issue (or you just don't want to open one),
+You can email me directly with whatever code you have that describes the
+problem, I've been pretty responsive in the past. If I believe you are
+"misusing" the library I'll try to put the blame on myself for being unclear
+in this document and take the steps to clarify it.  So please, contact me with
+any concerns, I'm open to feedback.
+
+#### How (not) to use this library
+The library functions create and return objects that are properly templated on
+the iterable they are passed. These exact names of these types or
+precisely how they are templated is unspecified, you should rely on the
+functions described in this document.
+If you plan to use these functions in very simple, straight forward means as in
+the examples on this page, then you will be fine. If you feel like you need to
+open the header files, then I've probably under-described something, let me
+know.
+
+#### Handling of rvalues vs lvalues
+The rules are pretty simple, and the library can be largely used without
+knowledge of them.
+Let's take an example
+```c++
+std::vector<int> vec{2,4,6,8};
+for (auto&& p : enumerate(vec)) { /* ... */ }
+```
+In this case, `enumerate` will return an object that has bound a reference to
+`vec`. No copies are produced here, neither of `vec` nor of the elements it
+holds.
+
+If an rvalue was passed to enumerate, binding a reference would be unsafe.
+Consider:
+```c++
+for (auto&& p : enumerate(std::vector<int>{2,4,6,8})) { /* ... */ }
+```
+Instead, `enumerate` will return an object that has the temporary *moved* into
+it.  That is, the returned object will contain a `std::vector<int>` rather than
+just a reference to one. This may seem like a contrived example, but it matters
+when `enumerate` is passed the result of a function call like `enumerate(f())`,
+or, more obviously, something like `enumerate(zip(a, b))`.  The object returned
+from `zip` must be moved into the `enumerate` object. As a more specific
+result, itertools can be mixed and nested.
+
+
+
+#### Pipe syntax
+Wherever it makes sense I've implemented the "pipe" operator that has become
+common in similar libraries. When the syntax is available it is done by pulling
+out the iterable from the call and placing it before the tool. For example:
+
+```c++
+filter(pred, seq);  // regular call
+seq | filter(pred);  // pipe-style
+enumerate(seq);  // regular call
+seq | enumerate;  // pipe-style.
+```
+
+The following tools support pipe. The remaining I left out because although
+some of them have multiple reasonable versions, it wasn't obvious to me how I
+would expect them to behave:
+
+- accumulate
+- chain.from\_iterable
+- chunked
+- combinations
+- combinations\_with\_replacement
+- cycle
+- dropwhile
+- enumerate
+- filter
+- filterfalse
+- groupby
+- imap
+- permutations
+- powerset
+- reversed
+- slice
+- sliding\_window
+- sorted
+- starmap
+- takewhile
+- unique\_everseen
+- unique\_justseen
+
+I don't personally care for the piping style, but it seemed to be desired by
+the users.
+
+
+range
+-----
+
+Uses an underlying iterator to achieve the same effect of the python range
+function.  `range` can be used in three different ways:
+
+Only the stopping point is provided.  Prints `0 1 2 3 4 5 6 7 8 9`
+```c++
+for (auto i : range(10)) {
+    cout << i << '\n';
+}
+```
+
+The start and stop are both provided.  Prints `10 11 12 13 14`
+```c++
+for (auto i : range(10, 15)) {
+    cout << i << '\n';
+}
+```
+
+The start, stop, and step are all provided.  Prints `20 22 24 26 28`
+```c++
+for (auto i : range(20, 30, 2)) {
+    cout << i << '\n';
+}
+```
+
+Negative values are allowed as well.  Prints `2 1 0 -1 -2`
+```c++
+for (auto i : range(2, -3, -1)) {
+    cout << i << '\n';
+}
+```
+
+A step size of 0 results in an empty range (Python's raises an exception).
+The following prints nothing
+```c++
+for (auto i : range(0, 10, 0)) {
+    cout << i << '\n';
+}
+```
+
+In addition to normal integer range operations, doubles and
+other numeric types are supported through the template
+
+Prints: `5.0 5.5 6.0` ... `9.5`
+```c++
+for(auto i : range(5.0, 10.0, 0.5)) {
+    cout << i << '\n';
+}
+```
+
+*Implementation Note*: Typical ranges have their current value incremented by
+the step size repeatedly (`value += step`). Floating point range value are
+recomputed at each step to avoid accumulating floating point inaccuracies
+(`value = start + (step * steps_taken`). The result of the latter is a bit
+slower but more accurate.
+
+enumerate
+---------
+
+Continually "yields" containers similar to pairs.  They are basic structs with a
+.index and a .element.  Usage appears as:
+
+```c++
+vector<int> vec{2, 4, 6, 8};
+for (auto&& e : enumerate(vec)) {
+    cout << e.index
+         << ": "
+         << e.element
+         << '\n';
+}
+```
+
+filter
+------
+Called as `filter(predicate, iterable)`.  The predicate can be any callable.
+`filter` will only yield values that are true under the predicate.
+
+Prints values greater than 4:  `5 6 7 8`
+```c++
+vector<int> vec{1, 5, 4, 0, 6, 7, 3, 0, 2, 8, 3, 2, 1};
+for (auto&& i : filter([] (int i) { return i > 4; }, vec)) {
+    cout << i <<'\n';
+}
+
+```
+
+If no predicate is passed, the elements themselves are tested for truth
+
+Prints only non-zero values.
+```c++
+for(auto&& i : filter(vec)) {
+    cout << i << '\n';
+}
+```
+
+filterfalse
+-----------
+Similar to filter, but only prints values that are false under the predicate.
+
+Prints values not greater than 4: `1 4 3 2 3 2 1 `
+```c++
+vector<int> vec{1, 5, 4, 0, 6, 7, 3, 0, 2, 8, 3, 2, 1};
+for (auto&& i : filterfalse([] (int i) { return i > 4; }, vec)) {
+    cout << i <<'\n';
+}
+
+```
+
+If no predicate is passed, the elements themselves are tested for truth.
+
+Prints only zero values.
+```c++
+for(auto&& i : filterfalse(vec)) {
+    cout << i << '\n';
+}
+
+```
+unique\_everseen
+---------------
+*Additional Requirements*: Underlying values must be copy-constructible.
+
+This is a filter adaptor that only generates values that have never been seen
+before. For this to work your object must be specialized for `std::hash`.
+
+Prints `1 2 3 4 5 6 7 8 9`
+```c++
+vector<int> v {1,2,3,4,3,2,1,5,6,7,7,8,9,8,9,6};
+for (auto&& i : unique_everseen(v)) {
+    cout << i << ' ';
+}
+```
+
+unique\_justseen
+--------------
+Another filter adaptor that only omits consecutive duplicates.
+
+Prints `1 2 3 4 3 2 1`
+Example Usage:
+```c++
+vector<int> v {1,1,1,2,2,3,3,3,4,3,2,1,1,1};
+for (auto&& i : unique_justseen(v)) {
+    cout << i << ' ';
+}
+```
+
+takewhile
+---------
+Yields elements from an iterable until the first element that is false under
+the predicate is encountered.
+
+Prints `1 2 3 4`.  (5 is false under the predicate)
+```c++
+vector<int> ivec{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
+for (auto&& i : takewhile([] (int i) {return i < 5;}, ivec)) {
+    cout << i << '\n';
+}
+```
+
+dropwhile
+---------
+Yields all elements after and including the first element that is true under
+the predicate.
+
+Prints `5 6 7 1 2`
+```c++
+vector<int> ivec{1, 2, 3, 4, 5, 6, 7, 1, 2};
+for (auto&& i : dropwhile([] (int i) {return i < 5;}, ivec)) {
+    cout << i << '\n';
+}
+```
+
+cycle
+-----
+*Additional Requirements*: Input must have a ForwardIterator
+
+
+Repeatedly produces all values of an iterable.  The loop will be infinite, so a
+`break` or other control flow structure is necessary to exit.
+
+Prints `1 2 3` repeatedly until `some_condition` is true
+```c++
+vector<int> vec{1, 2, 3};
+for (auto&& i : cycle(vec)) {
+    cout << i << '\n';
+    if (some_condition) {
+        break;
+    }
+}
+```
+
+repeat
+------
+Repeatedly produces a single argument forever, or a given number of times.
+`repeat` will bind a reference when passed an lvalue and move when given
+an rvalue.  It will then yield a reference to the same item until completion.
+
+The below prints `1` five times.
+```c++
+for (auto&& e : repeat(1, 5)) {
+    cout << e << '\n';
+}
+```
+
+The below prints `2` forever
+```c++
+for (auto&& e : repeat(2)) {
+    cout << e << '\n';
+}
+```
+
+count
+-----
+Effectively a `range` without a stopping point.<br />
+`count()` with no arguments will start counting from 0 with a positive
+step of 1.<br />
+`count(i)` will start counting from `i` with a positive step of 1.<br />
+`count(i, st)` will start counting from `i` with a step of `st`.
+
+*Technical limitations*: Unlike Python which can use its long integer
+types when needed, count() would eventually exceed the
+maximum possible value for its type (or minimum with a negative step).
+`count` is actually implemented as a `range` with the stopping point
+being the `std::numeric_limits<T>::max()` for the integral type (`long`
+by default)
+
+The below will print `0 1 2` ... etc
+```c++
+for (auto&& i : count()) {
+    cout << i << '\n';
+}
+```
+
+groupby
+-------
+*Additional Requirements*: If the Input's iterator's `operator*()` returns
+a reference, the reference must remain valid after the iterator is incremented.
+Roughly equivalent to requiring the Input have a ForwardIterator.
+
+Separate an iterable into groups sharing a common key.  The following example
+creates a new group whenever a string of a different length is encountered.
+```c++
+vector<string> vec = {
+    "hi", "ab", "ho",
+    "abc", "def",
+    "abcde", "efghi"
+};
+
+for (auto&& gb : groupby(vec, [] (const string &s) {return s.length(); })) {
+    cout << "key: " << gb.first << '\n';
+    cout << "content: ";
+    for (auto&& s : gb.second) {
+        cout << s << "  ";
+    }
+    cout << '\n';
+}
+```
+*Note*: Just like Python's `itertools.groupby`, this doesn't do any sorting.
+It just iterates through, making a new group each time there is a key change.
+Thus, if the group is unsorted, the same key may appear multiple times.
+
+starmap
+-------
+
+Takes a sequence of tuple-like objects (anything that works with `std::get`)
+and unpacks each object into individual arguments for each function call.
+The below example takes a `vector` of `pairs` of ints, and passes them
+to a function expecting two ints, with the elements of the `pair` being
+the first and second arguments to the function.
+
+```c++
+vector<pair<int, int>> v = {{2, 3}, {5, 2}, {3, 4}}; // {base, exponent}
+for (auto&& i : starmap([](int b, int e){return pow(b, e);}, v)) {
+    // ...
+}
+```
+
+`starmap` can also work over a tuple-like object of tuple-like objects even
+when the contained objects are different as long as the functor works with
+multiple types of calls.  For example, a `Callable` struct with overloads
+for its `operator()` will work as long as all overloads have the same
+return type
+
+```c++
+struct Callable {
+    int operator()(int i) const;
+    int operator()(int i, char c) const;
+    int operator()(double d, int i, char c) const;
+};
+```
+
+This will work with a tuple of mixed types
+
+```c++
+auto t = make_tuple(
+        make_tuple(5), // first form
+        make_pair(3, 'c'), // second
+        make_tuple(1.0, 1, '1')); // third
+for (auto&& i : starmap(Callable{}, t)) {
+    // ...
+}
+```
+
+accumulate
+-------
+*Additional Requirements*: Type return from functor (with reference removed)
+must be assignable.
+
+Differs from `std::accumulate` (which in my humble opinion should be named
+`std::reduce` or `std::foldl`).  It is similar to a functional reduce where one
+can see all of the intermediate results.  By default, it keeps a running sum.
+Prints: `1 3 6 10 15`
+```c++
+for (auto&& i : accumulate(range(1, 6))) {
+    cout << i << '\n';
+}
+```
+A second, optional argument may provide an alternative binary function
+to compute results.  The following example multiplies the numbers, rather
+than adding them.
+Prints: `1 2 6 24 120`
+
+```c++
+for (auto&& i : accumulate(range(1, 6), std::multiplies<int>{})) {
+    cout << i << '\n';
+}
+```
+
+Note: The intermediate result type must support default construction
+and assignment.
+
+zip
+---
+Takes an arbitrary number of ranges of different types and efficiently iterates
+over them in parallel (so an iterator to each container is incremented
+simultaneously).  When you dereference an iterator to "zipped" range you get a
+tuple of the elements the iterators were holding.
+
+Example usage:
+```c++
+array<int,4> i{{1,2,3,4}};
+vector<float> f{1.2,1.4,12.3,4.5,9.9};
+vector<string> s{"i","like","apples","alot","dude"};
+array<double,5> d{{1.2,1.2,1.2,1.2,1.2}};
+
+for (auto&& e : zip(i,f,s,d)) {
+    cout << std::get<0>(e) << ' '
+         << std::get<1>(e) << ' '
+         << std::get<2>(e) << ' '
+         << std::get<3>(e) << '\n';
+    std::get<1>(e)=2.2f; // modifies the underlying 'f' array
+}
+```
+
+zip\_longest
+-----------
+Terminates on the longest sequence instead of the shortest.
+Repeatedly yields a tuple of `boost::optional<T>`s where `T` is the type
+yielded by the sequences' respective iterators.  Because of its boost
+dependency, `zip_longest` is not in `itertools.hpp` and must be included
+separately.
+The following loop prints either "Just <item>" or "Nothing" for each
+element in each tuple yielded.
+
+```c++
+vector<int> v1 = {0, 1, 2, 3};
+vector<int> v2 = {10, 11};
+for (auto&& t : zip_longest(v1, v2)) {
+    cout << '{';
+    if (std::get<0>(t)) {
+        cout << "Just " << *std::get<0>(t);
+    } else {
+        cout << "Nothing";
+    }
+    cout << ", ";
+    if (std::get<1>(t)) {
+        cout << "Just " << *std::get<1>(t);
+    } else {
+        cout << "Nothing";
+    }
+    cout << "}\n";
+}
+```
+
+The output is:
+```
+{Just 0, Just 10}
+{Just 1, Just 11}
+{Just 2, Nothing}
+{Just 3, Nothing}
+```
+
+imap
+----
+
+Takes a function and one or more iterables.  The number of iterables must
+match the number of arguments to the function.  Applies the function to
+each element (or elements) in the iterable(s).  Terminates on the shortest
+sequence.
+
+Prints the squares of the numbers in vec: `1 4 9 16 25`
+```c++
+vector<int> vec{1, 2, 3, 4, 5};
+for (auto&& i : imap([] (int x) {return x * x;}, vec)) {
+    cout << i << '\n';
+}
+```
+
+With more than one sequence, the below adds corresponding elements from
+each vector together, printing `11 23 35 47 59 71`
+```c++
+vector<int> vec1{1, 3, 5, 7, 9, 11};
+vector<int> vec2{10, 20, 30, 40, 50, 60};
+for (auto&& i : imap([] (int x, int y) { return x + y; }, vec1, vec2)) {
+    cout << i << '\n';
+}
+```
+
+*Note*: The name `imap` is chosen to prevent confusion/collision with
+`std::map`, and because it is more related to `itertools.imap` than
+the python builtin `map`.
+
+
+compress
+--------
+
+Yields only the values corresponding to true in the selectors iterable.
+Terminates on the shortest sequence.
+
+Prints `2 6`
+```c++
+vector<int> ivec{1, 2, 3, 4, 5, 6};
+vector<bool> bvec{false, true, false, false, false, true};
+for (auto&& i : compress(ivec, bvec) {
+    cout << i << '\n';
+}
+```
+
+sorted
+------
+*Additional Requirements*: Input must have a ForwardIterator
+
+Allows iteration over a sequence in sorted order. `sorted` does
+**not** produce a new sequence, copy elements, or modify the original
+sequence.  It only provides a way to iterate over existing elements.
+`sorted` also takes an optional second
+[comparator](http://en.cppreference.com/w/cpp/concept/Compare)
+argument.  If not provided, defaults to `std::less`. <br />
+Iterables passed to sorted are required to have an iterator with
+an `operator*() const` member.
+
+The below outputs `0 1 2 3 4`.
+
+```c++
+unordered_set<int> nums{4, 0, 2, 1, 3};
+for (auto&& i : sorted(nums)) {
+    cout << i << '\n';
+}
+```
+
+chain
+-----
+*Additional Requirements*: The underlying iterators of all containers'
+`operator*` must have the *exact* same type
+
+This can chain any set of ranges together as long as their iterators
+dereference to the same type.
+
+```c++
+vector<int> empty{};
+vector<int> vec1{1,2,3,4,5,6};
+array<int,4> arr1{{7,8,9,10}};
+
+for (auto&& i : chain(empty,vec1,arr1)) {
+    cout << i << '\n';
+}
+```
+
+chain.from\_iterable
+-------------------
+
+Similar to chain, but rather than taking a variadic number of iterables,
+it takes an iterable of iterables and chains the contained iterables together.
+A simple example is shown below using a vector of vectors to represent
+a 2d ragged array, and prints it in row-major order.
+```c++
+vector<vector<int>> matrix = {
+    {1, 2, 3},
+    {4, 5},
+    {6, 8, 9, 10, 11, 12}
+};
+
+for (auto&& i : chain.from_iterable(matrix)) {
+    cout << i << '\n';
+}
+```
+
+reversed
+-------
+*Additional Requirements*: Input must be compatible with `std::rbegin()` and
+`std::rend()`
+
+Iterates over elements of a sequence in reverse order.
+
+```c++
+for (auto&& i : reversed(a)) {
+    cout << i << '\n';
+}
+```
+
+slice
+-----
+
+Returns selected elements from a range, parameters are start, stop and step.
+the range returned is [start,stop) where you only take every step element
+
+This outputs `0 3 6 9 12`
+```c++
+vector<int> a{0,1,2,3,4,5,6,7,8,9,10,11,12,13};
+for (auto&& i : slice(a,0,15,3)) {
+    cout << i << '\n';
+}
+```
+
+sliding\_window
+-------------
+*Additional Requirements*: Input must have a ForwardIterator
+
+Takes a section from a range and increments the whole section.  If the
+window size is larger than the length of the input, the `sliding_window` will
+yield nothing (begin == end).
+
+Example:
+`[1, 2, 3, 4, 5, 6, 7, 8, 9]`
+
+take a section of size 4, output is:
+```
+1 2 3 4
+2 3 4 5
+3 4 5 6
+4 5 6 7
+5 6 7 8
+6 7 8 9
+```
+
+Example Usage:
+```c++
+vector<int> v = {1,2,3,4,5,6,7,8,9};
+for (auto&& sec : sliding_window(v,4)) {
+    for (auto&& i : sec) {
+        cout << i << ' ';
+        i.get() = 90;
+    }
+    cout << '\n';
+}
+```
+chunked
+------
+
+chunked will yield subsequent chunkes of an iterable in blocks of a specified
+size. The final chunk may be shorter than the rest if the chunk size given
+does not evenly divide the length of the iterable
+
+Example usage:
+```c++
+vector<int> v {1,2,3,4,5,6,7,8,9};
+for (auto&& sec : chunked(v,4)) {
+    for (auto&& i : sec) {
+        cout << i << ' ';
+    }
+    cout << '\n';
+}
+```
+
+The above prints:
+```
+1 2 3 4
+5 6 7 8
+9
+```
+
+product
+------
+*Additional Requirements*: Input must have a ForwardIterator
+
+Generates the cartesian product of the given ranges put together.
+
+Example usage:
+```c++
+vector<int> v1{1,2,3};
+vector<int> v2{7,8};
+vector<string> v3{"the","cat"};
+vector<string> v4{"hi","what","up","dude"};
+for (auto&& t : product(v1,v2,v3,v4)) {
+    cout << std::get<0>(t) << ", "
+        << std::get<1>(t) << ", "
+        << std::get<2>(t) << ", "
+        << std::get<3>(t) << '\n';
+}
+```
+
+Product also accepts a "repeat" as a template argument. Currently this is the only way to do repeats. **If you are reading this and need `product(seq, 3)` instead of `product<3>(seq)` please open an issue**.
+
+Example usage:
+```c++
+std::string s = "abc";
+// equivalent of product(s, s, s);
+for (auto&& t : product<3>(s)) {
+   // ...
+}
+```
+
+combinations
+-----------
+*Additional Requirements*: Input must have a ForwardIterator
+
+Generates n length unique sequences of the input range.
+
+Example usage:
+```c++
+vector<int> v = {1,2,3,4,5};
+for (auto&& i : combinations(v,3)) {
+    for (auto&& j : i ) cout << j << " ";
+    cout << '\n';
+}
+```
+
+combinations\_with\_replacement
+-----------------------------
+*Additional Requirements*: Input must have a ForwardIterator
+
+Like combinations, but with replacement of each element.  The
+below is printed by the loop that follows:
+```
+{A, A}
+{A, B}
+{A, C}
+{B, B}
+{B, C}
+{C, C}
+```
+```c++
+for (auto&& v : combinations_with_replacement(s, 2)) {
+    cout << '{' << v[0] << ", " << v[1] << "}\n";
+}
+```
+
+permutations
+-----------
+*Additional Requirements*: Input must have a ForwardIterator.  Iterator must
+have an `operator*() const`.
+
+Generates all the permutations of a range using `std::next_permutation`.
+
+Example usage:
+```c++
+vector<int> v = {1,2,3,4,5};
+for (auto&& vec : permutations(v)) {
+    for (auto&& i : vec) {
+        cout << i << ' ';
+    }
+    cout << '\n';
+}
+```
+
+powerset
+-------
+*Additional Requirements*: Input must have a ForwardIterator
+
+Generates every possible subset of a set, runs in O(2^n).
+
+Example usage:
+```c++
+vector<int> vec {1,2,3,4,5,6,7,8,9};
+for (auto&& v : powerset(vec)) {
+    for (auto&& i : v) {
+        cout << i << " ";
+    }
+    cout << '\n';
+}
+```
diff --git a/src/external/cppitertools-1.0/WORKSPACE b/src/external/cppitertools-1.0/WORKSPACE
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/external/cppitertools-1.0/accumulate.hpp b/src/external/cppitertools-1.0/accumulate.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3e419d8b62054774cd07728a511bb11bc56aba81
--- /dev/null
+++ b/src/external/cppitertools-1.0/accumulate.hpp
@@ -0,0 +1,138 @@
+#ifndef ITER_ACCUMULATE_H_
+#define ITER_ACCUMULATE_H_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename Container, typename AccumulateFunc>
+    class Accumulator;
+
+    using AccumulateFn = IterToolFnOptionalBindSecond<Accumulator, std::plus<>>;
+  }
+  constexpr impl::AccumulateFn accumulate{};
+}
+
+template <typename Container, typename AccumulateFunc>
+class iter::impl::Accumulator {
+ private:
+  Container container_;
+  mutable AccumulateFunc accumulate_func_;
+
+  friend AccumulateFn;
+
+  using AccumVal = std::remove_reference_t<std::result_of_t<AccumulateFunc(
+      iterator_deref<Container>, iterator_deref<Container>)>>;
+
+  Accumulator(Container&& container, AccumulateFunc accumulate_func)
+      : container_(std::forward<Container>(container)),
+        accumulate_func_(accumulate_func) {}
+
+ public:
+  Accumulator(Accumulator&&) = default;
+
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    IteratorWrapper<ContainerT> sub_iter_;
+    IteratorWrapper<ContainerT> sub_end_;
+    AccumulateFunc* accumulate_func_;
+    std::unique_ptr<AccumVal> acc_val_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = AccumVal;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter,
+        IteratorWrapper<ContainerT>&& sub_end, AccumulateFunc& accumulate_fun)
+        : sub_iter_{std::move(sub_iter)},
+          sub_end_{std::move(sub_end)},
+          accumulate_func_(&accumulate_fun),
+          // only get first value if not an end iterator
+          acc_val_{
+              !(sub_iter_ != sub_end_) ? nullptr : new AccumVal(*sub_iter_)} {}
+
+    Iterator(const Iterator& other)
+        : sub_iter_{other.sub_iter_},
+          sub_end_{other.sub_end_},
+          accumulate_func_{other.accumulate_func_},
+          acc_val_{other.acc_val_ ? new AccumVal(*other.acc_val_) : nullptr} {}
+
+    Iterator& operator=(const Iterator& other) {
+      if (this == &other) {
+        return *this;
+      }
+      sub_iter_ = other.sub_iter_;
+      sub_end_ = other.sub_end_;
+      accumulate_func_ = other.accumulate_func_;
+      acc_val_.reset(other.acc_val_ ? new AccumVal(*other.acc_val_) : nullptr);
+      return *this;
+    }
+
+    Iterator(Iterator&&) = default;
+    Iterator& operator=(Iterator&&) = default;
+
+    const AccumVal& operator*() const {
+      return *acc_val_;
+    }
+
+    const AccumVal* operator->() const {
+      return acc_val_.get();
+    }
+
+    Iterator& operator++() {
+      ++sub_iter_;
+      if (sub_iter_ != sub_end_) {
+        *acc_val_ = (*accumulate_func_)(*acc_val_, *sub_iter_);
+      }
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {get_begin(container_), get_end(container_), accumulate_func_};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_), accumulate_func_};
+  }
+  Iterator<AsConst<Container>> begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), accumulate_func_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), accumulate_func_};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/chain.hpp b/src/external/cppitertools-1.0/chain.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..64aeb19d6a7057b4d1d51cb2d9351a0fd10278c3
--- /dev/null
+++ b/src/external/cppitertools-1.0/chain.hpp
@@ -0,0 +1,392 @@
+#ifndef ITER_CHAIN_HPP_
+#define ITER_CHAIN_HPP_
+
+#include "internal/iter_tuples.hpp"
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <array>
+#include <iterator>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename TupType, std::size_t... Is>
+    class Chained;
+
+    template <typename Container>
+    class ChainedFromIterable;
+
+    using ChainFromIterableFn = IterToolFn<ChainedFromIterable>;
+
+    // rather than a chain function, use a callable object to support
+    // from_iterable
+    class ChainMaker;
+  }
+}
+
+template <typename TupType, std::size_t... Is>
+class iter::impl::Chained {
+ private:
+  friend ChainMaker;
+
+  template <typename TupTypeT>
+  class IteratorData {
+    IteratorData() = delete;
+    static_assert(
+        std::tuple_size<std::decay_t<TupTypeT>>::value == sizeof...(Is),
+        "tuple size != sizeof Is");
+
+    static_assert(
+        are_same<iterator_deref<std::tuple_element_t<Is, TupTypeT>>...>::value,
+        "All chained iterables must have iterators that "
+        "dereference to the same type, including cv-qualifiers "
+        "and references.");
+
+   public:
+    using IterTupType = iterator_tuple_type<TupTypeT>;
+    using DerefType = iterator_deref<std::tuple_element_t<0, TupTypeT>>;
+    using ArrowType = iterator_arrow<std::tuple_element_t<0, TupTypeT>>;
+
+    template <std::size_t Idx>
+    static DerefType get_and_deref(IterTupType& iters) {
+      return *std::get<Idx>(iters);
+    }
+
+    template <std::size_t Idx>
+    static ArrowType get_and_arrow(IterTupType& iters) {
+      return apply_arrow(std::get<Idx>(iters));
+    }
+
+    template <std::size_t Idx>
+    static void get_and_increment(IterTupType& iters) {
+      ++std::get<Idx>(iters);
+    }
+
+    template <std::size_t Idx>
+    static bool get_and_check_not_equal(
+        const IterTupType& lhs, const IterTupType& rhs) {
+      return std::get<Idx>(lhs) != std::get<Idx>(rhs);
+    }
+
+    using DerefFunc = DerefType (*)(IterTupType&);
+    using ArrowFunc = ArrowType (*)(IterTupType&);
+    using IncFunc = void (*)(IterTupType&);
+    using NeqFunc = bool (*)(const IterTupType&, const IterTupType&);
+
+    constexpr static std::array<DerefFunc, sizeof...(Is)> derefers{
+        {get_and_deref<Is>...}};
+
+    constexpr static std::array<ArrowFunc, sizeof...(Is)> arrowers{
+        {get_and_arrow<Is>...}};
+
+    constexpr static std::array<IncFunc, sizeof...(Is)> incrementers{
+        {get_and_increment<Is>...}};
+
+    constexpr static std::array<NeqFunc, sizeof...(Is)> neq_comparers{
+        {get_and_check_not_equal<Is>...}};
+
+    using TraitsValue =
+        iterator_traits_deref<std::tuple_element_t<0, TupTypeT>>;
+  };
+
+  Chained(TupType&& t) : tup_(std::move(t)) {}
+  TupType tup_;
+
+ public:
+  Chained(Chained&&) = default;
+
+  template <typename TupTypeT>
+  class Iterator {
+   private:
+    using IterData = IteratorData<TupTypeT>;
+    std::size_t index_;
+    typename IterData::IterTupType iters_;
+    typename IterData::IterTupType ends_;
+
+    void check_for_end_and_adjust() {
+      while (index_ < sizeof...(Is)
+             && !(IterData::neq_comparers[index_](iters_, ends_))) {
+        ++index_;
+      }
+    }
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = typename IteratorData<TupTypeT>::TraitsValue;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(std::size_t i, typename IterData::IterTupType&& iters,
+        typename IterData::IterTupType&& ends)
+        : index_{i}, iters_(std::move(iters)), ends_(std::move(ends)) {
+      check_for_end_and_adjust();
+    }
+
+    decltype(auto) operator*() {
+      return IterData::derefers[index_](iters_);
+    }
+
+    decltype(auto) operator-> () {
+      return IterData::arrowers[index_](iters_);
+    }
+
+    Iterator& operator++() {
+      IterData::incrementers[index_](iters_);
+      check_for_end_and_adjust();
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    // TODO make const and non-const iterators comparable
+    bool operator!=(const Iterator& other) const {
+      return index_ != other.index_
+             || (index_ != sizeof...(Is)
+                    && IterData::neq_comparers[index_](iters_, other.iters_));
+    }
+
+    bool operator==(const Iterator& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<TupType> begin() {
+    return {0, typename IteratorData<TupType>::IterTupType{get_begin(
+                   std::get<Is>(tup_))...},
+        typename IteratorData<TupType>::IterTupType{
+            get_end(std::get<Is>(tup_))...}};
+  }
+
+  Iterator<TupType> end() {
+    return {sizeof...(Is), typename IteratorData<TupType>::IterTupType{get_end(
+                               std::get<Is>(tup_))...},
+        typename IteratorData<TupType>::IterTupType{
+            get_end(std::get<Is>(tup_))...}};
+  }
+
+  Iterator<AsConst<TupType>> begin() const {
+    return {0, typename IteratorData<AsConst<TupType>>::IterTupType{get_begin(
+                   impl::as_const(std::get<Is>(tup_)))...},
+        typename IteratorData<AsConst<TupType>>::IterTupType{
+            get_end(impl::as_const(std::get<Is>(tup_)))...}};
+  }
+
+  Iterator<AsConst<TupType>> end() const {
+    return {sizeof...(Is),
+        typename IteratorData<AsConst<TupType>>::IterTupType{
+            get_end(impl::as_const(std::get<Is>(tup_)))...},
+        typename IteratorData<AsConst<TupType>>::IterTupType{
+            get_end(impl::as_const(std::get<Is>(tup_)))...}};
+  }
+};
+
+// jesus christ. what have I done.
+template <typename TupType, std::size_t... Is>
+template <typename TupTypeT>
+constexpr std::array<typename iter::impl::Chained<TupType,
+                         Is...>::template IteratorData<TupTypeT>::DerefFunc,
+    sizeof...(Is)>
+    iter::impl::Chained<TupType, Is...>::IteratorData<TupTypeT>::derefers;
+
+template <typename TupType, std::size_t... Is>
+template <typename TupTypeT>
+constexpr std::array<typename iter::impl::Chained<TupType,
+                         Is...>::template IteratorData<TupTypeT>::ArrowFunc,
+    sizeof...(Is)>
+    iter::impl::Chained<TupType, Is...>::IteratorData<TupTypeT>::arrowers;
+
+template <typename TupType, std::size_t... Is>
+template <typename TupTypeT>
+constexpr std::array<typename iter::impl::Chained<TupType,
+                         Is...>::template IteratorData<TupTypeT>::IncFunc,
+    sizeof...(Is)>
+    iter::impl::Chained<TupType, Is...>::IteratorData<TupTypeT>::incrementers;
+
+template <typename TupType, std::size_t... Is>
+template <typename TupTypeT>
+constexpr std::array<typename iter::impl::Chained<TupType,
+                         Is...>::template IteratorData<TupTypeT>::NeqFunc,
+    sizeof...(Is)>
+    iter::impl::Chained<TupType, Is...>::IteratorData<TupTypeT>::neq_comparers;
+
+template <typename Container>
+class iter::impl::ChainedFromIterable {
+ private:
+  friend ChainFromIterableFn;
+  Container container_;
+  ChainedFromIterable(Container&& container)
+      : container_(std::forward<Container>(container)) {}
+
+ public:
+  ChainedFromIterable(ChainedFromIterable&&) = default;
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    using SubContainer = iterator_deref<ContainerT>;
+    using SubIter = IteratorWrapper<SubContainer>;
+
+    IteratorWrapper<ContainerT> top_level_iter_;
+    IteratorWrapper<ContainerT> top_level_end_;
+    std::unique_ptr<SubIter> sub_iter_p_;
+    std::unique_ptr<SubIter> sub_end_p_;
+
+    static std::unique_ptr<SubIter> clone_sub_pointer(const SubIter* sub_iter) {
+      return sub_iter ? std::make_unique<SubIter>(*sub_iter) : nullptr;
+    }
+
+    template <typename T>
+    bool sub_iters_differ(const Iterator<T>& other) const {
+      // checking if they're the same also handles them both being nullptr
+      if (static_cast<const void*>(sub_iter_p_.get())
+          == static_cast<const void*>(other.sub_iter_p_.get())) {
+        return false;
+      }
+      if (sub_iter_p_ == nullptr || other.sub_iter_p_ == nullptr) {
+        // since the first check tests if they're the same,
+        // this will return if only one is nullptr
+        return true;
+      }
+      return *sub_iter_p_ != *other.sub_iter_p_;
+    }
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = iterator_traits_deref<iterator_deref<ContainerT>>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& top_iter,
+        IteratorWrapper<ContainerT>&& top_end)
+        : top_level_iter_{std::move(top_iter)},
+          top_level_end_{std::move(top_end)},
+          sub_iter_p_{!(top_iter != top_end)
+                          ?  // iter == end ?
+                          nullptr
+                          : std::make_unique<SubIter>(get_begin(*top_iter))},
+          sub_end_p_{!(top_iter != top_end)
+                         ?  // iter == end ?
+                         nullptr
+                         : std::make_unique<SubIter>(get_end(*top_iter))} {}
+
+    Iterator(const Iterator& other)
+        : top_level_iter_{other.top_level_iter_},
+          top_level_end_{other.top_level_end_},
+          sub_iter_p_{clone_sub_pointer(other.sub_iter_p_.get())},
+          sub_end_p_{clone_sub_pointer(other.sub_end_p_.get())} {}
+
+    Iterator& operator=(const Iterator& other) {
+      if (this == &other) {
+        return *this;
+      }
+
+      top_level_iter_ = other.top_level_iter_;
+      top_level_end_ = other.top_level_end_;
+      sub_iter_p_ = clone_sub_pointer(other.sub_iter_p_.get());
+      sub_end_p_ = clone_sub_pointer(other.sub_end_p_.get());
+
+      return *this;
+    }
+
+    Iterator(Iterator&&) = default;
+    Iterator& operator=(Iterator&&) = default;
+    ~Iterator() = default;
+
+    Iterator& operator++() {
+      ++*sub_iter_p_;
+      if (!(*sub_iter_p_ != *sub_end_p_)) {
+        ++top_level_iter_;
+        if (top_level_iter_ != top_level_end_) {
+          sub_iter_p_ = std::make_unique<SubIter>(get_begin(*top_level_iter_));
+          sub_end_p_ = std::make_unique<SubIter>(get_end(*top_level_iter_));
+        } else {
+          sub_iter_p_.reset();
+          sub_end_p_.reset();
+        }
+      }
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return top_level_iter_ != other.top_level_iter_
+             || sub_iters_differ(other);
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+
+    iterator_deref<iterator_deref<ContainerT>> operator*() {
+      return **sub_iter_p_;
+    }
+
+    iterator_arrow<iterator_deref<ContainerT>> operator->() {
+      return apply_arrow(*sub_iter_p_);
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {get_begin(container_), get_end(container_)};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_)};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_end(impl::as_const(container_))};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_))};
+  }
+};
+
+class iter::impl::ChainMaker {
+ private:
+  template <typename TupleType, std::size_t... Is>
+  Chained<TupleType, Is...> chain_impl(
+      TupleType&& containers, std::index_sequence<Is...>) const {
+    return {std::move(containers)};
+  }
+
+ public:
+  // expose regular call operator to provide usual chain()
+  template <typename... Containers>
+  auto operator()(Containers&&... cs) const {
+    return chain_impl(
+        std::tuple<Containers...>{std::forward<Containers>(cs)...},
+        std::index_sequence_for<Containers...>{});
+  }
+
+  ChainFromIterableFn from_iterable;
+};
+
+namespace iter {
+  namespace {
+    constexpr auto chain = iter::impl::ChainMaker{};
+  }
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/chunked.hpp b/src/external/cppitertools-1.0/chunked.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..33d194d1fd1e06f0e04ec0a0ae53d59cc40d704f
--- /dev/null
+++ b/src/external/cppitertools-1.0/chunked.hpp
@@ -0,0 +1,133 @@
+#ifndef ITER_CHUNKED_HPP_
+#define ITER_CHUNKED_HPP_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iteratoriterator.hpp"
+#include "internal/iterbase.hpp"
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace iter {
+  namespace impl {
+    template <typename Container>
+    class Chunker;
+
+    using ChunkedFn = IterToolFnBindSizeTSecond<Chunker>;
+  }
+  constexpr impl::ChunkedFn chunked{};
+}
+
+template <typename Container>
+class iter::impl::Chunker {
+ private:
+  Container container_;
+  std::size_t chunk_size_;
+
+  Chunker(Container&& container, std::size_t sz)
+      : container_(std::forward<Container>(container)), chunk_size_{sz} {}
+
+  friend ChunkedFn;
+
+  template <typename T>
+  using IndexVector = std::vector<IteratorWrapper<T>>;
+  template <typename T>
+  using DerefVec = IterIterWrapper<IndexVector<T>>;
+
+ public:
+  Chunker(Chunker&&) = default;
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    IteratorWrapper<ContainerT> sub_iter_;
+    IteratorWrapper<ContainerT> sub_end_;
+    DerefVec<ContainerT> chunk_;
+    std::size_t chunk_size_ = 0;
+
+    bool done() const {
+      return chunk_.empty();
+    }
+
+    void refill_chunk() {
+      chunk_.get().clear();
+      std::size_t i{0};
+      while (i < chunk_size_ && sub_iter_ != sub_end_) {
+        chunk_.get().push_back(sub_iter_);
+        ++sub_iter_;
+        ++i;
+      }
+    }
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = DerefVec<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter,
+        IteratorWrapper<ContainerT>&& sub_end, std::size_t s)
+        : sub_iter_{std::move(sub_iter)},
+          sub_end_{std::move(sub_end)},
+          chunk_size_{s} {
+      chunk_.get().reserve(chunk_size_);
+      refill_chunk();
+    }
+
+    Iterator& operator++() {
+      refill_chunk();
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return !(*this == other);
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return done() == other.done()
+             && (done() || !(sub_iter_ != other.sub_iter_));
+    }
+
+    DerefVec<ContainerT>& operator*() {
+      return chunk_;
+    }
+
+    DerefVec<ContainerT>* operator->() {
+      return &chunk_;
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {get_begin(container_), get_end(container_), chunk_size_};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_), chunk_size_};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), chunk_size_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), chunk_size_};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/combinations.hpp b/src/external/cppitertools-1.0/combinations.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..afdefe4a149a5d5a4c757e1b4a27c8e7ae81c54f
--- /dev/null
+++ b/src/external/cppitertools-1.0/combinations.hpp
@@ -0,0 +1,152 @@
+#ifndef ITER_COMBINATIONS_HPP_
+#define ITER_COMBINATIONS_HPP_
+
+#include "internal/iteratoriterator.hpp"
+#include "internal/iterbase.hpp"
+
+#include <iterator>
+#include <type_traits>
+#include <vector>
+
+namespace iter {
+  namespace impl {
+    template <typename Container>
+    class Combinator;
+
+    using CombinationsFn = IterToolFnBindSizeTSecond<Combinator>;
+  }
+  constexpr impl::CombinationsFn combinations{};
+}
+
+template <typename Container>
+class iter::impl::Combinator {
+ private:
+  Container container_;
+  std::size_t length_;
+
+  friend CombinationsFn;
+
+  Combinator(Container&& container, std::size_t length)
+      : container_(std::forward<Container>(container)), length_{length} {}
+
+  template <typename T>
+  using IndexVector = std::vector<iterator_type<T>>;
+  template <typename T>
+  using CombIteratorDeref = IterIterWrapper<IndexVector<T>>;
+
+ public:
+  Combinator(Combinator&&) = default;
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    constexpr static const int COMPLETE = -1;
+    std::remove_reference_t<ContainerT>* container_p_;
+    CombIteratorDeref<ContainerT> indices_;
+    int steps_{};
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = CombIteratorDeref<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(ContainerT& container, std::size_t n)
+        : container_p_{&container}, indices_{n} {
+      if (n == 0) {
+        steps_ = COMPLETE;
+        return;
+      }
+      size_t inc = 0;
+      for (auto& iter : indices_.get()) {
+        auto it = get_begin(*container_p_);
+        dumb_advance(it, get_end(*container_p_), inc);
+        if (it != get_end(*container_p_)) {
+          iter = it;
+          ++inc;
+        } else {
+          steps_ = COMPLETE;
+          break;
+        }
+      }
+    }
+
+    CombIteratorDeref<ContainerT>& operator*() {
+      return indices_;
+    }
+
+    CombIteratorDeref<ContainerT>* operator->() {
+      return &indices_;
+    }
+
+    Iterator& operator++() {
+      for (auto iter = indices_.get().rbegin(); iter != indices_.get().rend();
+           ++iter) {
+        ++(*iter);
+
+        // what we have to check here is if the distance between
+        // the index and the end of indices_ is >= the distance
+        // between the item and end of item
+        auto dist = std::distance(indices_.get().rbegin(), iter);
+
+        if (!(dumb_next(*iter, dist) != get_end(*container_p_))) {
+          if ((iter + 1) != indices_.get().rend()) {
+            size_t inc = 1;
+            for (auto down = iter; down != indices_.get().rbegin() - 1;
+                 --down) {
+              (*down) = dumb_next(*(iter + 1), 1 + inc);
+              ++inc;
+            }
+          } else {
+            steps_ = COMPLETE;
+            break;
+          }
+        } else {
+          break;
+        }
+        // we break because none of the rest of the items need
+        // to be incremented
+      }
+      if (steps_ != COMPLETE) {
+        ++steps_;
+      }
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return !(*this == other);
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return steps_ == other.steps_;
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {container_, length_};
+  }
+
+  Iterator<Container> end() {
+    return {container_, 0};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {impl::as_const(container_), length_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {impl::as_const(container_), 0};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/combinations_with_replacement.hpp b/src/external/cppitertools-1.0/combinations_with_replacement.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5b26f4ed6d52b6a9a2746755e0658af27cae0996
--- /dev/null
+++ b/src/external/cppitertools-1.0/combinations_with_replacement.hpp
@@ -0,0 +1,131 @@
+#ifndef ITER_COMBINATIONS_WITH_REPLACEMENT_HPP_
+#define ITER_COMBINATIONS_WITH_REPLACEMENT_HPP_
+
+#include "internal/iteratoriterator.hpp"
+#include "internal/iterbase.hpp"
+
+#include <iterator>
+#include <type_traits>
+#include <vector>
+
+namespace iter {
+  namespace impl {
+    template <typename Container>
+    class CombinatorWithReplacement;
+    using CombinationsWithReplacementFn =
+        IterToolFnBindSizeTSecond<CombinatorWithReplacement>;
+  }
+  constexpr impl::CombinationsWithReplacementFn combinations_with_replacement{};
+}
+
+template <typename Container>
+class iter::impl::CombinatorWithReplacement {
+ private:
+  Container container_;
+  std::size_t length_;
+
+  friend CombinationsWithReplacementFn;
+
+  CombinatorWithReplacement(Container&& container, std::size_t n)
+      : container_(std::forward<Container>(container)), length_{n} {}
+
+  template <typename T>
+  using IndexVector = std::vector<iterator_type<T>>;
+  template <typename T>
+  using CombIteratorDeref = IterIterWrapper<IndexVector<T>>;
+
+ public:
+  CombinatorWithReplacement(CombinatorWithReplacement&&) = default;
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    constexpr static const int COMPLETE = -1;
+    std::remove_reference_t<ContainerT>* container_p_;
+    CombIteratorDeref<ContainerT> indices_;
+    int steps_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = CombIteratorDeref<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(ContainerT& in_container, std::size_t n)
+        : container_p_{&in_container},
+          indices_(n, get_begin(in_container)),
+          steps_{(get_begin(in_container) != get_end(in_container) && n)
+                     ? 0
+                     : COMPLETE} {}
+
+    CombIteratorDeref<ContainerT>& operator*() {
+      return indices_;
+    }
+
+    CombIteratorDeref<ContainerT>* operator->() {
+      return &indices_;
+    }
+
+    Iterator& operator++() {
+      for (auto iter = indices_.get().rbegin(); iter != indices_.get().rend();
+           ++iter) {
+        ++(*iter);
+        if (!(*iter != get_end(*container_p_))) {
+          if ((iter + 1) != indices_.get().rend()) {
+            for (auto down = iter; down != indices_.get().rbegin() - 1;
+                 --down) {
+              (*down) = dumb_next(*(iter + 1));
+            }
+          } else {
+            steps_ = COMPLETE;
+            break;
+          }
+        } else {
+          // we break because none of the rest of the items
+          // need to be incremented
+          break;
+        }
+      }
+      if (steps_ != COMPLETE) {
+        ++steps_;
+      }
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return !(*this == other);
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return steps_ == other.steps_;
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {container_, length_};
+  }
+
+  Iterator<Container> end() {
+    return {container_, 0};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {impl::as_const(container_), length_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {impl::as_const(container_), 0};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/compress.hpp b/src/external/cppitertools-1.0/compress.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..60a48244e1d74dd10d0302a107866414c70f11bd
--- /dev/null
+++ b/src/external/cppitertools-1.0/compress.hpp
@@ -0,0 +1,140 @@
+#ifndef ITER_COMPRESS_H_
+#define ITER_COMPRESS_H_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <iterator>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename Container, typename Selector>
+    class Compressed;
+  }
+
+  template <typename Container, typename Selector>
+  impl::Compressed<Container, Selector> compress(Container&&, Selector&&);
+}
+
+template <typename Container, typename Selector>
+class iter::impl::Compressed {
+ private:
+  Container container_;
+  Selector selectors_;
+
+  friend Compressed iter::compress<Container, Selector>(
+      Container&&, Selector&&);
+
+  Compressed(Container&& in_container, Selector&& in_selectors)
+      : container_(std::forward<Container>(in_container)),
+        selectors_(std::forward<Selector>(in_selectors)) {}
+
+ public:
+  Compressed(Compressed&&) = default;
+  template <typename ContainerT, typename SelectorT>
+  class Iterator {
+   private:
+    template <typename, typename>
+    friend class Iterator;
+    IteratorWrapper<ContainerT> sub_iter_;
+    IteratorWrapper<ContainerT> sub_end_;
+
+    IteratorWrapper<SelectorT> selector_iter_;
+    IteratorWrapper<SelectorT> selector_end_;
+
+    void increment_iterators() {
+      ++sub_iter_;
+      ++selector_iter_;
+    }
+
+    void skip_failures() {
+      while (sub_iter_ != sub_end_ && selector_iter_ != selector_end_
+             && !*selector_iter_) {
+        increment_iterators();
+      }
+    }
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = iterator_traits_deref<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& cont_iter,
+        IteratorWrapper<ContainerT>&& cont_end,
+        IteratorWrapper<SelectorT>&& sel_iter,
+        IteratorWrapper<SelectorT>&& sel_end)
+        : sub_iter_{std::move(cont_iter)},
+          sub_end_{std::move(cont_end)},
+          selector_iter_{std::move(sel_iter)},
+          selector_end_{std::move(sel_end)} {
+      skip_failures();
+    }
+
+    iterator_deref<ContainerT> operator*() {
+      return *sub_iter_;
+    }
+
+    iterator_arrow<ContainerT> operator->() {
+      return apply_arrow(sub_iter_);
+    }
+
+    Iterator& operator++() {
+      increment_iterators();
+      skip_failures();
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T, typename U>
+    bool operator!=(const Iterator<T, U>& other) const {
+      return sub_iter_ != other.sub_iter_
+             && selector_iter_ != other.selector_iter_;
+    }
+
+    template <typename T, typename U>
+    bool operator==(const Iterator<T, U>& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<Container, Selector> begin() {
+    return {get_begin(container_), get_end(container_), get_begin(selectors_),
+        get_end(selectors_)};
+  }
+
+  Iterator<Container, Selector> end() {
+    return {get_end(container_), get_end(container_), get_end(selectors_),
+        get_end(selectors_)};
+  }
+
+  Iterator<AsConst<Container>, AsConst<Selector>> begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_end(impl::as_const(container_)),
+        get_begin(impl::as_const(selectors_)),
+        get_end(impl::as_const(selectors_))};
+  }
+
+  Iterator<AsConst<Container>, AsConst<Selector>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_)),
+        get_end(impl::as_const(selectors_)),
+        get_end(impl::as_const(selectors_))};
+  }
+};
+
+template <typename Container, typename Selector>
+iter::impl::Compressed<Container, Selector> iter::compress(
+    Container&& container_, Selector&& selectors_) {
+  return {
+      std::forward<Container>(container_), std::forward<Selector>(selectors_)};
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/count.hpp b/src/external/cppitertools-1.0/count.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1694589c180ca0d3c7704a46a552e52e74bb5e3a
--- /dev/null
+++ b/src/external/cppitertools-1.0/count.hpp
@@ -0,0 +1,23 @@
+#ifndef ITER_COUNT_H_
+#define ITER_COUNT_H_
+
+#include "range.hpp"
+
+#include <limits>
+
+namespace iter {
+  template <typename T>
+  constexpr auto count(T start, T step) noexcept {
+    // if step is < 0, set the stop to numeric min, otherwise numeric max
+    T stop = step < T(0) ? std::numeric_limits<T>::min()
+                         : std::numeric_limits<T>::max();
+    return range(start, stop, step);
+  }
+
+  template <typename T = long>
+  constexpr auto count(T start = T(0)) noexcept {
+    return count(start, T(1));
+  }
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/cycle.hpp b/src/external/cppitertools-1.0/cycle.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..30cbefb066f8b965c932c69ba1ec6ccdc6349709
--- /dev/null
+++ b/src/external/cppitertools-1.0/cycle.hpp
@@ -0,0 +1,108 @@
+#ifndef ITER_CYCLE_H_
+#define ITER_CYCLE_H_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <initializer_list>
+#include <iterator>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename Container>
+    class Cycler;
+
+    using CycleFn = IterToolFn<Cycler>;
+  }
+  constexpr impl::CycleFn cycle{};
+}
+
+template <typename Container>
+class iter::impl::Cycler {
+ private:
+  friend CycleFn;
+
+  Container container_;
+
+  Cycler(Container&& container)
+      : container_(std::forward<Container>(container)) {}
+
+ public:
+  Cycler(Cycler&&) = default;
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    IteratorWrapper<ContainerT> sub_iter_;
+    IteratorWrapper<ContainerT> sub_begin_;
+    IteratorWrapper<ContainerT> sub_end_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = iterator_traits_deref<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter,
+        IteratorWrapper<ContainerT>&& sub_end)
+        : sub_iter_{sub_iter},
+          sub_begin_{sub_iter},
+          sub_end_{std::move(sub_end)} {}
+
+    iterator_deref<ContainerT> operator*() {
+      return *sub_iter_;
+    }
+
+    iterator_arrow<ContainerT> operator->() {
+      return apply_arrow(sub_iter_);
+    }
+
+    Iterator& operator++() {
+      ++sub_iter_;
+      // reset to beginning upon reaching the sub_end_
+      if (!(sub_iter_ != sub_end_)) {
+        sub_iter_ = sub_begin_;
+      }
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {get_begin(container_), get_end(container_)};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_)};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_end(impl::as_const(container_))};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_))};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/dropwhile.hpp b/src/external/cppitertools-1.0/dropwhile.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..939f696e15575b66ed0285ccda30c6f5d4c10b65
--- /dev/null
+++ b/src/external/cppitertools-1.0/dropwhile.hpp
@@ -0,0 +1,127 @@
+#ifndef ITER_DROPWHILE_H_
+#define ITER_DROPWHILE_H_
+
+#include "filter.hpp"
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <iterator>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename FilterFunc, typename Container>
+    class Dropper;
+
+    using DropWhileFn = IterToolFnOptionalBindFirst<Dropper, BoolTester>;
+  }
+  constexpr impl::DropWhileFn dropwhile{};
+}
+
+template <typename FilterFunc, typename Container>
+class iter::impl::Dropper {
+ private:
+  Container container_;
+  mutable FilterFunc filter_func_;
+
+  friend DropWhileFn;
+
+  Dropper(FilterFunc filter_func, Container&& container)
+      : container_(std::forward<Container>(container)),
+        filter_func_(filter_func) {}
+
+ public:
+  Dropper(Dropper&&) = default;
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    using Holder = DerefHolder<iterator_deref<ContainerT>>;
+    IteratorWrapper<ContainerT> sub_iter_;
+    IteratorWrapper<ContainerT> sub_end_;
+    Holder item_;
+    FilterFunc* filter_func_;
+
+    void inc_sub_iter() {
+      ++sub_iter_;
+      if (sub_iter_ != sub_end_) {
+        item_.reset(*sub_iter_);
+      }
+    }
+
+    // skip all values for which the predicate is true
+    void skip_passes() {
+      while (sub_iter_ != sub_end_ && (*filter_func_)(item_.get())) {
+        inc_sub_iter();
+      }
+    }
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = iterator_traits_deref<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter,
+        IteratorWrapper<ContainerT>&& sub_end, FilterFunc& filter_func)
+        : sub_iter_{std::move(sub_iter)},
+          sub_end_{std::move(sub_end)},
+          filter_func_(&filter_func) {
+      if (sub_iter_ != sub_end_) {
+        item_.reset(*sub_iter_);
+      }
+      skip_passes();
+    }
+
+    typename Holder::reference operator*() {
+      return item_.get();
+    }
+
+    typename Holder::pointer operator->() {
+      return item_.get_ptr();
+    }
+
+    Iterator& operator++() {
+      inc_sub_iter();
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {get_begin(container_), get_end(container_), filter_func_};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_), filter_func_};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), filter_func_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), filter_func_};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/enumerate.hpp b/src/external/cppitertools-1.0/enumerate.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a7bc97e3b45dd3eff3fd2f1242d3b512fca7238
--- /dev/null
+++ b/src/external/cppitertools-1.0/enumerate.hpp
@@ -0,0 +1,135 @@
+#ifndef ITER_ENUMERATE_H_
+#define ITER_ENUMERATE_H_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <functional>
+#include <initializer_list>
+#include <iterator>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename Index, typename Elem>
+    using EnumBasePair = std::pair<Index, Elem>;
+
+    // "yielded" by the Enumerable::Iterator.  Has a .index, and a
+    // .element referencing the value yielded by the subiterator
+    template <typename Index, typename Elem>
+    class EnumIterYield : public EnumBasePair<Index, Elem> {
+      using BasePair = EnumBasePair<Index, Elem>;
+      using BasePair::BasePair;
+
+     public:
+      typename BasePair::first_type index = BasePair::first;
+      typename BasePair::second_type& element = BasePair::second;
+    };
+
+    template <typename Container, typename Index>
+    class Enumerable;
+
+    using EnumerateFn = IterToolFnOptionalBindSecond<Enumerable, std::size_t>;
+  }
+  constexpr impl::EnumerateFn enumerate{};
+}
+
+namespace std {
+  template <typename Index, typename Elem>
+  class tuple_size<iter::impl::EnumIterYield<Index, Elem>>
+      : public tuple_size<iter::impl::EnumBasePair<Index, Elem>> {};
+
+  template <std::size_t N, typename Index, typename Elem>
+  class tuple_element<N, iter::impl::EnumIterYield<Index, Elem>>
+      : public tuple_element<N, iter::impl::EnumBasePair<Index, Elem>> {};
+}
+
+template <typename Container, typename Index>
+class iter::impl::Enumerable {
+ private:
+  Container container_;
+  const Index start_;
+
+  friend EnumerateFn;
+
+  // Value constructor for use only in the enumerate function
+  Enumerable(Container&& container, Index start)
+      : container_(std::forward<Container>(container)), start_{start} {}
+
+ public:
+  Enumerable(Enumerable&&) = default;
+
+  template <typename T>
+  using IterYield = EnumIterYield<Index, iterator_deref<T>>;
+
+  //  Holds an iterator of the contained type and an Index for the
+  //  index_.  Each call to ++ increments both of these data members.
+  //  Each dereference returns an IterYield.
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    IteratorWrapper<ContainerT> sub_iter_;
+    Index index_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = IterYield<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter, Index start)
+        : sub_iter_{std::move(sub_iter)}, index_{start} {}
+
+    IterYield<ContainerT> operator*() {
+      return {index_, *sub_iter_};
+    }
+
+    ArrowProxy<IterYield<ContainerT>> operator->() {
+      return {**this};
+    }
+
+    Iterator& operator++() {
+      ++sub_iter_;
+      ++index_;
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {get_begin(container_), start_};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), start_};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {get_begin(impl::as_const(container_)), start_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)), start_};
+  }
+};
+#endif
diff --git a/src/external/cppitertools-1.0/examples/.gitignore b/src/external/cppitertools-1.0/examples/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..319fe583864f7071d76dc527d3f279e0db70d502
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/.gitignore
@@ -0,0 +1,6 @@
+*.o
+*.swp
+*_examples
+.sconsign.dblite
+config.log
+.sconf_temp/
diff --git a/src/external/cppitertools-1.0/examples/SConstruct b/src/external/cppitertools-1.0/examples/SConstruct
new file mode 100644
index 0000000000000000000000000000000000000000..119b2026a91f43413e812fbfb756329e39a5b06c
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/SConstruct
@@ -0,0 +1,48 @@
+import os
+
+env = Environment(
+    ENV=os.environ,
+    CXXFLAGS= ['-g', '-Wall', '-Wextra',
+               '-pedantic', '-std=c++14',
+               '-I/usr/local/include'
+    ],
+    CPPPATH='..',
+    LINKFLAGS=['-L/usr/local/lib'])
+
+# allows highighting to print to terminal from compiler output
+env['ENV']['TERM'] = os.environ['TERM']
+
+progs = Split(
+    '''
+    accumulate
+    chain
+    chunked
+    combinatoric
+    compress
+    count
+    cycle
+    dropwhile
+    enumerate
+    filter
+    filterfalse
+    groupby
+    imap
+    range
+    repeat
+    reversed
+    slice
+    sliding_window
+    sorted
+    starmap
+    takewhile
+    unique_justseen
+    unique_everseen
+    zip
+    ''')
+
+if Configure(env).CheckCXXHeader('boost/optional.hpp'):
+    progs.append('zip_longest')
+    progs.append('mixed')
+
+for p in progs:
+    env.Program('{0}_examples.cpp'.format(p))
diff --git a/src/external/cppitertools-1.0/examples/accumulate_examples.cpp b/src/external/cppitertools-1.0/examples/accumulate_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9d9a2f677723a334986837a32d308b62356e18f9
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/accumulate_examples.cpp
@@ -0,0 +1,22 @@
+#include <accumulate.hpp>
+#include <range.hpp>
+
+#include <iostream>
+#include <vector>
+
+int main() {
+    std::vector<int> vec = {0, 1, 2, 3, 4, 5};
+    // accumulate adds elements by default
+    std::cout << "accumulate({1, 2, 3, 4, 5}): { ";
+    for (auto i : iter::accumulate(vec)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    // accumulate with a lambda for subtraction
+    std::cout << "accumulate({1, 2, 3, 4, 5}, subtract): { ";
+    for (auto i : iter::accumulate(vec, [](int a, int b){return a - b;})) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+}
diff --git a/src/external/cppitertools-1.0/examples/chain_examples.cpp b/src/external/cppitertools-1.0/examples/chain_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..607fa040af87fb79aea12879305ffe6f46e959cf
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/chain_examples.cpp
@@ -0,0 +1,45 @@
+#include <chain.hpp>
+
+#include <vector>
+#include <string>
+#include <list>
+#include <iostream>
+#include <array>
+
+int main() {
+    // chaining combines two sequences for iteration
+    std::vector<int> ivec = {1, 4, 7, 9};
+    std::vector<int> lvec = {100, 200, 300, 400, 500, 600};
+    std::cout << "chaining two int vectors together:\n";
+    for (auto&& e : iter::chain(ivec, lvec)) {
+        std::cout << e << ' ';
+    }
+    std::cout << '\n';
+
+    std::vector<char> vec1 = {'c', 'h'};
+    std::array<char,4> arr1{{'a', 'i', 'n', 'i'}};
+    std::string s{"ng different "};
+    std::list<char> lst = {'t', 'y', 'p', 'e', 's'};
+
+    // chain can mix different sequence types as long as the type yielded
+    // by their underlying iterators is *exactly* the same
+    std::cout << "mixing: ";
+    for (auto&& c : iter::chain(vec1, arr1, s, lst)) {
+        std::cout << c;
+    }
+    std::cout << '\n';
+
+    std::vector<std::vector<int>> matrix = {
+        {2, 4, 6},
+        {8, 10, 12},
+        {14, 16, 18}
+    };
+
+    // chain.from_iterable effectively flattens a sequence by one level
+    std::cout << "chain.from_iterable to flatten matrix:\n";
+    for (auto&& i : iter::chain.from_iterable(matrix)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+
+}
diff --git a/src/external/cppitertools-1.0/examples/chunked_examples.cpp b/src/external/cppitertools-1.0/examples/chunked_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9c3046f512bb6c34ef7054672ef42e7f2d5c1649
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/chunked_examples.cpp
@@ -0,0 +1,23 @@
+#include <chunked.hpp>
+
+#include <iostream>
+#include <vector>
+
+int main() {
+    std::cout << "chunk size: 4\n";
+    std::vector<int> v {1,2,3,4,5,6,7,8,9}; 
+    for (auto&& sec : iter::chunked(v, 4)) {
+        for (auto&& i : sec) {
+            std::cout << i << " ";
+        }
+        std::cout << '\n';
+    }
+
+    std::cout << "chunk size: 3\n";
+    for (auto&& sec : iter::chunked(v,3)) {
+        for (auto&& i : sec) {
+            std::cout << i << " ";
+        }
+        std::cout << '\n';
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/combinatoric_examples.cpp b/src/external/cppitertools-1.0/examples/combinatoric_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d7111a5660dd798d120b23a1902a7fb0f4126f0e
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/combinatoric_examples.cpp
@@ -0,0 +1,63 @@
+#include <combinations.hpp>
+#include <combinations_with_replacement.hpp>
+#include <permutations.hpp>
+#include <product.hpp>
+#include <powerset.hpp>
+
+#include <vector>
+#include <string>
+#include <iostream>
+
+int main() {
+    const std::vector<int> v1 = {1,2,3,4,5};
+
+    std::cout << "combinations({1,2,3,4,5}, 3}:\n";
+    for (auto&& ns : iter::combinations(v1,3)) {
+        std::cout << "{ ";
+        for (auto&& j : ns ) {
+            std::cout << j << ' ';
+        }
+        std::cout << "}\n";
+    }
+
+
+
+    // allows elements to be used repeatedly
+    std::cout << "combinations_with_replacement({1, 2}, 4):\n";
+    std::vector<int> v2 = {1,2};
+    for (auto&& ns : iter::combinations_with_replacement(v2, 4)) {
+        std::cout << "{ ";
+        for (auto&& j : ns ) {
+            std::cout << j << ' ';
+        }
+        std::cout << "}\n";
+    }
+
+    std::cout << "permutations(\"abc\"):\n";
+    std::string s{"abc"};
+    for (auto&& cs : iter::permutations(s)) {
+        std::cout << "{ ";
+        for (auto&& c : cs ) {
+            std::cout << c << ' ';
+        }
+        std::cout << "}\n";
+    }
+
+    std::vector<std::string> v3 = {"abc", "def"};
+    std::cout << "product of three vectors (int, int, string):\n";
+    for (auto&& t : iter::product(v1,v2,v3)) {
+        std::cout << "{ "
+            << std::get<0>(t) << ' '
+            << std::get<1>(t) << ' '
+            << std::get<2>(t) << " }\n";
+    }
+
+    std::cout << "powerset({1,2,3,4,5}):\n";
+    for (auto&& ns : iter::powerset(v1)) {
+        std::cout << "{ ";
+        for (auto&& i : ns ) {
+            std::cout << i << ' ';
+        }
+        std::cout << "}\n";
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/compress_examples.cpp b/src/external/cppitertools-1.0/examples/compress_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b60197a98d7b1bb62a3201c4c2b046bdcc15e1f0
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/compress_examples.cpp
@@ -0,0 +1,43 @@
+#include <compress.hpp>
+
+#include <vector>
+#include <iostream>
+
+template <typename DataType, typename SelectorType>
+void testcase(std::vector<DataType> data_vec,
+        std::vector<SelectorType> sel_vec)
+{
+
+    for (auto e : compress(data_vec, sel_vec)) {
+        std::cout << e << '\n';
+    }
+}
+
+int main(void) {
+    using BVec = const std::vector<bool>;
+
+    std::vector<int> ns{0, 1, 2, 3, 4, 5};
+    std::cout << "ns = { ";
+    for (auto&& i : ns) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    BVec b1{true, false, true, false, true, false};
+
+    std::cout << "compress(ns, {true, false, true, false, true, false}): { ";
+    for (auto&& i : iter::compress(ns, b1)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+    
+
+    BVec b2 {true}; 
+    // compress terminates on the shortest sequence (either one)
+    std::cout << "compress(ns, {true}): { ";
+    for (auto&& i : iter::compress(ns, b2)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+    
+}
diff --git a/src/external/cppitertools-1.0/examples/count_examples.cpp b/src/external/cppitertools-1.0/examples/count_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..10ac8a722478da936dc35f33a8f1842dcc82dc6e
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/count_examples.cpp
@@ -0,0 +1,37 @@
+#include <count.hpp>
+
+#include <iostream>
+
+int main() {
+    std::cout << "count() counts towards infinity, breaks at 10\n";
+    for (auto i : iter::count()) {
+        std::cout << i << '\n';
+        if (i == 10) {
+            break;
+        }
+    }
+
+    std::cout << "count(20) starts counting from 20, breaks at 30\n";
+    for (auto i : iter::count(20)) {
+        std::cout << i << '\n';
+        if (i == 30) {
+            break;
+        }
+    }
+
+    std::cout << "count(50, 2) counts by 2 starting at 50, breaks at 70\n";
+    for (auto i : iter::count(50, 2)) {
+        std::cout << i << '\n';
+        if (i >= 70) {
+            break;
+        }
+    }
+
+    std::cout << "count(0, -1) counts down towards -infinity, breaks at -10\n";
+    for (auto i : iter::count(0, -1)) {
+        std::cout << i << '\n';
+        if (i == -10) {
+            break;
+        }
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/cycle_examples.cpp b/src/external/cppitertools-1.0/examples/cycle_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7f973d665045ddf388159c72720126cf2e20cb67
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/cycle_examples.cpp
@@ -0,0 +1,30 @@
+#include <cycle.hpp>
+
+#include <vector>
+#include <string>
+#include <iostream>
+
+int main() {
+    size_t count = 0;
+
+    std::cout << "cycle({2, 4, 6}) run 20 times:\n";
+    std::vector<int> vec = {2, 4, 6};
+    for (auto&& i : iter::cycle(vec)) {
+        std::cout << i << '\n';
+        if (count == 20) {
+            break;
+        }
+        ++count;
+    }
+
+    std::cout << "cycle(\"hello\") run 20 times:\n";
+    count = 0;
+    const std::string s("hello");
+    for (auto&& c : iter::cycle(s)) {
+        std::cout << c << '\n';
+        if (count == 20) {
+            break;
+        }
+        ++count;
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/dropwhile_examples.cpp b/src/external/cppitertools-1.0/examples/dropwhile_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8c628f1afa88c92c17c7730cf435d6faa2caf25a
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/dropwhile_examples.cpp
@@ -0,0 +1,19 @@
+#include <dropwhile.hpp>
+
+#include <vector>
+#include <iostream>
+
+
+int main() {
+    std::vector<int> ns = {0, 1, 2, 3, 4, 5};
+    std::cout << "ns = { ";
+    for (auto&& i : ns) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    std::cout << "dropwhile elements are less than 5\n";
+    for (auto&& i : iter::dropwhile([] (int i) {return i < 5;}, ns)) {
+        std::cout << i << '\n';
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/enumerate_examples.cpp b/src/external/cppitertools-1.0/examples/enumerate_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3978669651733ab3ddc10529fd91514b11651895
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/enumerate_examples.cpp
@@ -0,0 +1,42 @@
+#include <enumerate.hpp>
+
+#include <string>
+#include <iostream>
+#include <vector>
+#include <cassert>
+
+
+int main() {
+    std::cout << "enumerating the characters of a string \"hello\":\n";
+    const std::string const_string("hello");
+    for (auto&& e : iter::enumerate(const_string)) {
+        std::cout << '(' << e.index << ", " << e.element << ") ";
+    }
+    std::cout << '\n';
+
+    std::vector<int> vec = {20, 30, 50};
+    std::cout << "enumerating a vector of {20, 30, 50}:\n";
+    for (auto&& e : iter::enumerate(vec)) { 
+        std::cout << '(' << e.index << ", " << e.element << ") ";
+        e.element = 0;
+    }
+    std::cout << '\n';
+    assert(vec[0] == 0);
+    assert(vec[1] == 0);
+    assert(vec[2] == 0);
+
+    // itertools supports raw arrays
+    std::cout << "statically sized arrays can be enumerated\n";
+    int array[] = {1, 9, 8, 11};
+    for (auto&& e : iter::enumerate(array)) {
+        std::cout << '(' << e.index << ", " << e.element << ") ";
+    }
+    std::cout << '\n';
+
+    // itertools supports temporaries
+    std::cout << "vector temporary of {5, 2}\n";
+    for (auto&& e : iter::enumerate(std::vector<int>(5,2))) {
+        std::cout << '(' << e.index << ", " << e.element << ") ";
+    }
+    std::cout << '\n';
+}
diff --git a/src/external/cppitertools-1.0/examples/filter_examples.cpp b/src/external/cppitertools-1.0/examples/filter_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d28c443a7c0e6d3f417140cd269a6d53d24f037a
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/filter_examples.cpp
@@ -0,0 +1,58 @@
+#include <filter.hpp>
+
+#include <vector>
+#include <iostream>
+
+bool greater_than_four(int i) {
+    return i > 4;
+}
+
+class LessThanValue {
+    private:
+        int compare_val;
+
+    public:
+        LessThanValue() = delete;
+        LessThanValue(int v) : compare_val(v) { }
+
+        bool operator() (int i) {
+            return i < this->compare_val;
+        }
+};
+
+
+int main() {
+    std::vector<int> ns{1, 5, 6, 0, 7, 2, 3, 8, 3, 0, 2, 1};
+    std::cout << "ns = { ";
+    for (auto&& i : ns) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    std::cout << "Greater than 4 (function pointer)\n";
+    for (auto&& i : iter::filter(greater_than_four, ns)) {
+        std::cout << i << '\n';
+    }
+
+    std::cout << "Less than 4 (lambda)\n";
+    for (auto&& i : iter::filter([] (const int i) { return i < 4; }, ns)) {
+        std::cout << i << '\n';
+    }
+
+    LessThanValue lv(4);
+    std::cout << "Less than 4 (callable object)\n";
+    for (auto&& i : iter::filter(lv, ns)) {
+        std::cout << i << '\n';
+    }
+
+    // filter(seq) with no predicate uses the truthiness of the values
+    std::cout << "Nonzero ints filter(ns)\n";
+    for (auto&& i : iter::filter(ns)) {
+        std::cout << i << '\n';
+    }
+
+    std::cout << "odd numbers\n";
+    for (auto&& i : iter::filter([] (const int i) {return i % 2;}, ns)) {
+        std::cout << i << '\n';
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/filterfalse_examples.cpp b/src/external/cppitertools-1.0/examples/filterfalse_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..772005a87e1833e78d9ec1537483f15f286a531c
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/filterfalse_examples.cpp
@@ -0,0 +1,29 @@
+#include <filterfalse.hpp>
+
+#include <vector>
+#include <iostream>
+
+bool greater_than_four(int i) {
+    return i > 4;
+}
+
+int main() {
+    std::vector<int> ns{1, 5, 6, 0, 7, 2, 3, 8, 3, 0, 2, 1};
+    std::cout << "ns = { ";
+    for (auto&& i : ns) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    // like filter() but only shows elements that are false under the predicate
+    std::cout << "Greater than 4\n";
+    for (auto&& i : iter::filterfalse(greater_than_four, ns)) {
+        std::cout << i << '\n';
+    }
+
+    // single argument version only shows falsey values
+    std::cout << "filterfalse(ns):\n";
+    for (auto&& i : iter::filterfalse(ns)) {
+        std::cout << i << '\n';
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/groupby_examples.cpp b/src/external/cppitertools-1.0/examples/groupby_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d4a99187f8ae002b8ab2700b71c5500c06ae7ed
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/groupby_examples.cpp
@@ -0,0 +1,48 @@
+#include <groupby.hpp>
+
+#include <vector>
+#include <iostream>
+#include <string>
+#include <functional>
+
+int main() {
+    auto len = [](const std::string& str){ return str.size(); };
+    std::vector<std::string> vec = {
+        "hi", "ab", "ho",
+        "abc", "def",
+        "abcde", "efghi"
+    };
+
+    std::cout << "strings grouped by their length\n";
+    for (auto&& gb : iter::groupby(vec, len)) {
+        std::cout << "key(" << gb.first << "): ";
+        for (auto&& s : gb.second) {
+            std::cout << s << "  ";
+        }
+        std::cout << '\n';
+    }
+
+    // groups may be skipped entirely or partially consumed
+    std::cout << "skipping length of 3\n";
+    for (auto&& gb : iter::groupby(vec, len)) {
+        if (gb.first == 3) {
+            continue;
+        }
+        std::cout << "key(" << gb.first << "): ";
+        for (auto&& s : gb.second) {
+            std::cout << s << "  ";
+        }
+        std::cout << '\n';
+    }
+
+
+    std::cout << "ints grouped by their value:\n";
+    std::vector<int> ivec = {5, 5, 6, 6, 19, 19, 19, 19, 69, 0, 10, 10};
+    for (auto&& gb : iter::groupby(ivec)) {
+        std::cout << "key(" << gb.first << "): ";
+        for (auto&& s : gb.second) {
+            std::cout << s << "  ";
+        }
+        std::cout << '\n';
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/imap_examples.cpp b/src/external/cppitertools-1.0/examples/imap_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..812281b6f572e9e336ed5c807a15f3e97cec38b4
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/imap_examples.cpp
@@ -0,0 +1,33 @@
+#include <imap.hpp>
+
+#include <vector>
+#include <iostream>
+
+int main() {
+    // applies the function on its way through the sequence
+    std::cout << "mapping i*i over [1, 7): ";
+    std::vector<int> vec1 = {1, 2, 3, 4, 5, 6};
+    for (auto i : iter::imap([] (int i) {return i * i; }, vec1)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+
+    // multiple sequences can be used, each iterator will be advanced
+    // f(a[0], b[0]) then f(a[1], b[1]) etc.
+    std::cout << "vector sum of <1, 2, ..., 6> and <10, 20, ..., 60>: ";
+    std::vector<int> vec2 = {10, 20, 30, 40, 50, 60};
+    for (auto i : iter::imap([] (int x, int y) { return x + y; }, vec1, vec2)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+
+    // it will terminate on the shortest sequence
+    std::cout << "vector sum of <1, 2, ..., 6>, <10, 20, ..., 60>, and "
+        "<100, 200, 300>: ";
+    std::vector<int> vec3 = {100, 200, 300};
+    for (auto i : iter::imap([] (int a, int b, int c) { return a + b + c; }, 
+                vec1, vec2, vec3)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+}
diff --git a/src/external/cppitertools-1.0/examples/mixed_examples.cpp b/src/external/cppitertools-1.0/examples/mixed_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..08ff774bc9c74231b6fb1e37d31c7d7e4acf5ba4
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/mixed_examples.cpp
@@ -0,0 +1,69 @@
+#include <itertools.hpp>
+#include <zip_longest.hpp>
+
+#include <vector>
+#include <tuple>
+#include <array>
+#include <iostream>
+#include <string>
+
+template <typename T>
+std::ostream & operator<<(std::ostream & out, const boost::optional<T>& opt) {
+    if (opt) {
+        out << "Just " << *opt;
+    } else {
+        out << "Nothing";
+    }
+    return out;
+}
+int main() {
+    {
+        std::vector<int> vec1{1,2,3,4,5,6};
+        std::vector<int> vec2{1,2,3,4,5};
+        std::vector<std::string> strvec = 
+            {"his","name","was","robert","paulson","his",
+                "name","was","robert","paulson"};
+        for (auto&& t : iter::zip_longest(iter::chain(vec1,vec2),strvec)) {
+            std::cout << std::get<0>(t) << " "
+                << std::get<1>(t) << std::endl;
+        }
+    }
+
+    std::string str = "hello world";
+    std::vector<int> vec = {6, 9, 6, 9};
+    for (auto&& p : iter::enumerate(iter::enumerate(str))) { (void)p; }
+    for (auto&& p : iter::enumerate(iter::zip(str, vec))) { (void)p; }
+
+    std::cout << std::endl;
+    {
+        std::vector<int> vec1{1,2,3,4,5,6};
+        std::vector<int> vec2{7,8,9,10};
+        std::vector<std::string> strvec
+        {"We're","done","when","I","say","we're","done"};
+        for (auto&& t : iter::zip(strvec,
+                    iter::chain(iter::slice(vec1,2,6),
+                        iter::slice(vec2,1,4)))) {
+            std::cout << std::get<0>(t) << " "
+                << std::get<1>(t) << std::endl;
+        }
+    }
+    std::cout << std::endl;
+    {
+        std::vector<int> vec1{1,2,3,4,5,6};
+        std::vector<int> vec2{7,8,9,10};
+        for (auto&& s : iter::sliding_window(iter::chain(vec1,vec2),4)) {
+            for (auto&& i : s) std::cout << i << " ";
+            std::cout<<std::endl;
+        }
+    }
+    std::cout << std::endl;
+
+    auto prod_range = iter::product(iter::range(10), iter::range(5));
+
+    for (auto&& ij: iter::filter(
+                [](std::tuple<unsigned, unsigned> const& c)
+                {return std::get<0>(c) >= std::get<1>(c);},
+                prod_range)) {
+                    std::cout << std::get<0>(ij) << "," << std::get<1>(ij) << std::endl;
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/range_examples.cpp b/src/external/cppitertools-1.0/examples/range_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1dd0319935ad9cc93ce17220e5dbb124b53a9659
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/range_examples.cpp
@@ -0,0 +1,62 @@
+#include <range.hpp>
+
+#include <iostream>
+
+int main() {
+    // print [0, 10)
+    std::cout << "range(10): { ";
+    for (auto i : iter::range(10)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    // print [20, 30)
+    std::cout << "range(20, 30): { ";
+    for (auto i : iter::range(20, 30)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    // prints every second number in the range [50, 60)
+    std::cout << "range(50, 60, 2): { ";
+    for (auto i : iter::range(50, 60, 2)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    // prints every second number in the range [-10, 10)
+    std::cout << "range(-10, 10, 2): { ";
+    for (auto i : iter::range(-10, 10, 2)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    // the step doesn't need to cause i to equal stop eventually
+    std::cout << "range(0, 5, 4): { ";
+    for (auto i : iter::range(0, 5, 4)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    // ranges can count down as well as up
+    std::cout << "range(-1, -10, -2): { ";
+    for (auto i : iter::range(-1, -10, -2)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    // range works with floats and other types as well
+    // the usual concerns with float comparison come into play here
+    std::cout << "range(5.0, 9.9, 0.5): { ";
+    for(auto i : iter::range(5.0, 9.9, 0.5)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    // range also works with unsigned values
+    std::cout << "range(10u): { ";
+    for(auto i : iter::range(10u)){
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+}
diff --git a/src/external/cppitertools-1.0/examples/repeat_examples.cpp b/src/external/cppitertools-1.0/examples/repeat_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..24d37a8dc35968e3625f6a8b9516858fb11da4a2
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/repeat_examples.cpp
@@ -0,0 +1,22 @@
+#include <repeat.hpp>
+
+#include <iostream>
+
+int main () {
+    int a = 10;
+    int i = 0;
+
+    std::cout << "repeat(10) breaks after 20: ";
+    for (auto&& num : iter::repeat(a)) { 
+        std::cout << num << ' ';
+        ++i;
+        if (i >= 20) break;
+    }
+    std::cout << '\n';
+
+    std::cout << "repeat(" << a << ", 15): repeats 15 times: ";
+    for (auto&& num : iter::repeat(a,15)) {
+        std::cout << num << ' ';
+    }
+    std::cout << '\n';
+}
diff --git a/src/external/cppitertools-1.0/examples/reversed_examples.cpp b/src/external/cppitertools-1.0/examples/reversed_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d4d804d91921348df7dc7dd5ef5b52dccfc9dfa2
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/reversed_examples.cpp
@@ -0,0 +1,29 @@
+#include <reversed.hpp>
+
+#include <iostream>
+#include <vector>
+#include <string>
+
+int main () {
+    std::vector<int> nums{1,2,3,4,5,6,7};
+    std::vector<std::string> words{"hey","how","are","you","doing"};
+
+    std::cout << "numbers reversed: ";
+    for (auto&& i : iter::reversed(nums)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+
+    std::cout << "greeting reversed: ";
+    for (auto&& s : iter::reversed(words)) {
+        std::cout << s << ' ';
+    }
+    std::cout << '\n';
+
+    std::cout << "statically sized array: ";
+    int arr[] = {1, 2, 3, 4, 5, 6, 7};
+    for (auto&& i : iter::reversed(arr)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+}
diff --git a/src/external/cppitertools-1.0/examples/samples.hpp b/src/external/cppitertools-1.0/examples/samples.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..690fb71f563d54b065d478c854933a5511e1465c
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/samples.hpp
@@ -0,0 +1,98 @@
+#ifndef ITERTOOLS_SAMPLE_CLASSES_HPP
+#define ITERTOOLS_SAMPLE_CLASSES_HPP
+
+#include <iostream>
+#include <utility>
+#include <cstddef>
+
+namespace itertest {
+    class MoveOnly {
+        private:
+            int i; // not an aggregate
+        public:
+            MoveOnly(int v)
+                : i{v}
+            { }
+
+            MoveOnly(const MoveOnly&) = delete;
+            MoveOnly& operator=(const MoveOnly&) = delete;
+
+            MoveOnly(MoveOnly&& other) noexcept
+                : i{other.i}
+            { }
+
+            MoveOnly& operator=(MoveOnly&& other) noexcept {
+                this->i = other.i;
+                return *this;
+            }
+
+            // for std::next_permutation compatibility
+            friend bool operator<(const MoveOnly& lhs, const MoveOnly& rhs) {
+                return lhs.i < rhs.i;
+            }
+
+            friend std::ostream& operator<<(
+                    std::ostream& out, const MoveOnly& self) {
+                return out << self.i;
+            }
+
+    };
+
+    class DerefByValue {
+        private:
+            static constexpr std::size_t N = 3;
+            int array[N] = {0, 1, 2};
+        public:
+            DerefByValue() = default;
+
+            class Iterator {
+                private:
+                    int *current;
+                public:
+                    Iterator() = default;
+                    Iterator(int *p)
+                        : current{p}
+                    { }
+
+                    bool operator!=(const Iterator& other) const {
+                        return this->current != other.current;
+                    }
+
+                    // for testing, iterator derefences to an int instead of
+                    // an int&
+                    int operator*() const {
+                        return *this->current;
+                    }
+
+                    Iterator& operator++() {
+                        ++this->current;
+                        return *this;
+                    }
+            };
+
+            Iterator begin() {
+                return {this->array};
+            }
+
+            Iterator end() {
+                return {this->array + N};
+            }
+    };
+
+    class DerefByValueFancy {
+        private:
+            static constexpr std::size_t N = 3;
+            int array[N] = {0, 1, 2};
+        public:
+            DerefByValueFancy() = default;
+
+            int *begin() {
+                return this->array;
+            }
+
+            int *end() {
+                return this->array + N;
+            }
+    };
+}
+#endif // #ifndef ITERTOOLS_SAMPLE_CLASSES_HPP
diff --git a/src/external/cppitertools-1.0/examples/slice_examples.cpp b/src/external/cppitertools-1.0/examples/slice_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..42a16e33d5d1e38651d9e979051fe2f0cad5880e
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/slice_examples.cpp
@@ -0,0 +1,58 @@
+#include <iostream>
+
+#include <slice.hpp>
+#include <range.hpp>
+
+#include <vector>
+#include <array>
+
+int main() {
+    std::string a = "hello world";
+
+    std::cout << "a = { ";
+    for (auto&& i : a) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+
+    // just like range(), the step doesn't have to make it end anywhere
+    // specific, below, the step is too great to cover more than two elements
+    std::cout << "slice(a, 1, 4, 7): { ";
+    for (auto&& i : iter::slice(a, 1, 4, 5)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "\n";
+
+    std::cout << "slice(a, 2): { ";
+    for (auto&& i : iter::slice(a, 2)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+    
+    std::cout << "slice(a, 1, 5): { ";
+    for (auto&& i : iter::slice(a, 1, 5)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    std::cout << "slice(a, 2, 8, 2): { ";
+    for (auto&& i : iter::slice(a, 2, 8, 2)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+    
+    // the stop can be beyond the end
+    std::cout << "slice(a, 100): { ";
+    for (auto&& i : iter::slice(a, 100)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+
+    // the start can too
+    std::cout << "slice(a, 100, 200): { ";
+    for (auto&& i : iter::slice(a, 100, 200)) {
+        std::cout << i << ' ';
+    }
+    std::cout << "}\n";
+}
diff --git a/src/external/cppitertools-1.0/examples/sliding_window_examples.cpp b/src/external/cppitertools-1.0/examples/sliding_window_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ff28620253ab1342c995b6d683eaf176d9814161
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/sliding_window_examples.cpp
@@ -0,0 +1,22 @@
+#include "sliding_window.hpp"
+
+#include <iostream>
+#include <vector>
+
+int main() {
+    std::vector<int> v = {1,2,3,4,5,6,7,8,9}; 
+    for (auto&& sec : iter::sliding_window(v,4)) {
+        for (auto&& i : sec) {
+            std::cout << i << ' ';
+        }
+        std::cout << '\n';
+    }
+
+    std::cout << "Empty when window size is > length\n";
+    for (auto&& sec : iter::sliding_window(std::vector<int>{1,2,3}, 10)) {
+        for (auto&& i : sec) {
+            std::cout << i << ' ';
+        }
+        std::cout << '\n';
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/sorted_examples.cpp b/src/external/cppitertools-1.0/examples/sorted_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..109d137d08f1119b8ad831a98b132c263d864ab7
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/sorted_examples.cpp
@@ -0,0 +1,33 @@
+#include <sorted.hpp>
+
+#include <iostream>
+#include <vector>
+#include <unordered_set>
+#include <string>
+
+int main() {
+    std::vector<int> vec = {19, 45, 32, 10, 0, 90, 15, 1, 7, 5, 6, 69};
+    std::cout << "sorted(vector): ";
+    for (auto&& i : iter::sorted(vec)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+
+    std::cout << "sorted by last character: ";
+    std::vector<std::string> svec = {"hello", "everyone", "thanks", "for",
+                                     "having", "me", "here", "today"};
+    for (auto&& s : iter::sorted(svec,
+                [] (const std::string& s1, const std::string& s2) {
+                    return *s1.rbegin() < *s2.rbegin();})) {
+        std::cout << s << ' ';
+    }
+    std::cout << '\n';
+
+    // works even if the container isn't sortable
+    std::cout << "unordered_set<int> sorted: ";
+    std::unordered_set<int> uset = {10, 1, 20, 4, 50, 3};
+    for (auto&& i : iter::sorted(uset)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+}
diff --git a/src/external/cppitertools-1.0/examples/starmap_examples.cpp b/src/external/cppitertools-1.0/examples/starmap_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9797756c508ffec685e5384480f867379a7bcf80
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/starmap_examples.cpp
@@ -0,0 +1,40 @@
+#include <starmap.hpp>
+
+#include <array>
+#include <tuple>
+#include <vector>
+#include <iostream>
+
+// Since this is an example file I'm dumping a bunch of using declarations
+// here, in real code I use using declarations very sparingly
+using std::pair;
+using std::tuple;
+using std::vector;
+using std::make_pair;
+using std::make_tuple;
+
+struct Callable {
+    int operator()(int i) const { return i; }
+    int operator()(int i, char c) const { return i + c; }
+    int operator()(unsigned int u, int i, char c) const { return i + c + u; }
+};
+
+int main() {
+    // the function will be called with the tuple-like object unpacked
+    // using std::get.  This means an iterable of tuples, arrays, pairs, or
+    // whatever works with std::get
+    vector<pair<int, int>> v = {{2, 3}, {5, 2}, {3, 4}}; // {base, exponent}
+    for (auto&& i : iter::starmap([](int b, int e){ return b * e; }, v)) {
+        std::cout << i << '\n';
+    }
+
+    // Alternatively if an object has multiple call operators, a tuple-like
+    // object of tuple-like objects 
+    auto t = make_tuple(
+            make_tuple(5), // first form
+            make_pair(3, 'c'), // second form
+            make_tuple(1u, 1, '1')); // third form
+    for (auto&& i : iter::starmap(Callable{}, t)) {
+        std::cout << i << '\n';
+    }
+}
diff --git a/src/external/cppitertools-1.0/examples/takewhile_examples.cpp b/src/external/cppitertools-1.0/examples/takewhile_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3062a1413c90209859ec909e87714fb2c90fb793
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/takewhile_examples.cpp
@@ -0,0 +1,13 @@
+#include <takewhile.hpp>
+
+#include <vector>
+#include <iostream>
+
+int main() {
+    std::vector<int> ivec{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
+    std::cout << "take ints as long as they are less than 5: ";
+    for (auto&& i : iter::takewhile([] (int i) {return i < 5;}, ivec)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+}
diff --git a/src/external/cppitertools-1.0/examples/unique_everseen_examples.cpp b/src/external/cppitertools-1.0/examples/unique_everseen_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a670f0b1c08f90df21b73c6f3036a16a0487ee80
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/unique_everseen_examples.cpp
@@ -0,0 +1,13 @@
+#include <unique_everseen.hpp>
+
+#include <vector>
+#include <iostream>
+
+int main() {
+    std::vector<int> v {1,2,3,4,3,2,1,5,6,7,7,8,9,8,9,6};
+    std::cout << "omits all duplicates: ";
+    for (auto&& i : iter::unique_everseen(v)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+}
diff --git a/src/external/cppitertools-1.0/examples/unique_justseen_examples.cpp b/src/external/cppitertools-1.0/examples/unique_justseen_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..878cd4f8d3acb907fa5930fb3600c89cd2a184f5
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/unique_justseen_examples.cpp
@@ -0,0 +1,20 @@
+#include <unique_justseen.hpp>
+
+#include <vector>
+#include <iostream>
+
+int main() {
+    std::cout << "omits consecutive duplicates: ";
+    std::vector<int> v = {1,1,1,2,2,4,4,5,6,7,8,8,8,8,9,9};
+    for (auto&& i : iter::unique_justseen(v)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+
+    std::cout << "doesn't omit non-consecutive duplicates: ";
+    std::vector<int> v2 = {1,2,3,2,1,2,3};
+    for (auto&& i : iter::unique_justseen(v2)) {
+        std::cout << i << ' ';
+    }
+    std::cout << '\n';
+}
diff --git a/src/external/cppitertools-1.0/examples/zip_examples.cpp b/src/external/cppitertools-1.0/examples/zip_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b7f347adfd2757fe397df9cc624a63807d9a68d
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/zip_examples.cpp
@@ -0,0 +1,17 @@
+#include <zip.hpp>
+
+#include <vector>
+#include <string>
+#include <iostream>
+
+int main() {
+    std::vector<int> ivec{1, 4, 9, 16, 25, 36};
+    std::vector<std::string> svec{"hello", "good day", "goodbye"};
+
+    // zip terminates on the shortest sequence, and is variadic
+    std::cout << "zipping a vector of ints and a vector of strings\n";
+    for (auto&& e : iter::zip(ivec, svec)) {
+        std::cout << '(' << std::get<0>(e) << ", " << std::get<1>(e) << ")\n";
+    }
+}
+
diff --git a/src/external/cppitertools-1.0/examples/zip_longest_examples.cpp b/src/external/cppitertools-1.0/examples/zip_longest_examples.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1bbffbf8af8771fb2c22b470618b90cc37ddcbbb
--- /dev/null
+++ b/src/external/cppitertools-1.0/examples/zip_longest_examples.cpp
@@ -0,0 +1,29 @@
+#include <zip_longest.hpp>
+
+#include <vector>
+#include <string>
+#include <iostream>
+#include <array>
+#include <boost/optional.hpp>
+
+// prints Just VALUE or Nothing
+template <typename T>
+std::ostream & operator<<(std::ostream & out, const boost::optional<T>& opt) {
+    if (opt) {
+        out << "Just " << *opt;
+    } else {
+        out << "Nothing";
+    }
+    return out;
+}
+
+int main() {
+    std::vector<int> ivec = {1, 4, 9, 16, 25, 36};
+    std::vector<std::string> svec = {"hello", "good day", "goodbye"};
+
+    std::cout << "zipping a vector of strings with a vector of ints:\n";
+    for (auto&& e : iter::zip_longest(ivec, svec)) {
+        std::cout << '(' << std::get<0>(e) << ", "
+            << std::get<1>(e)  << ")\n";
+    }
+}
diff --git a/src/external/cppitertools-1.0/filter.hpp b/src/external/cppitertools-1.0/filter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a8c04004004efe611e4441dcb25fecc10aa4dff1
--- /dev/null
+++ b/src/external/cppitertools-1.0/filter.hpp
@@ -0,0 +1,140 @@
+#ifndef ITER_FILTER_H_
+#define ITER_FILTER_H_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <initializer_list>
+#include <iterator>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename FilterFunc, typename Container>
+    class Filtered;
+
+    struct BoolTester {
+      template <typename T>
+      constexpr bool operator()(const T& item_) const {
+        return bool(item_);
+      }
+    };
+
+    using FilterFn = IterToolFnOptionalBindFirst<Filtered, BoolTester>;
+  }
+
+  constexpr impl::FilterFn filter{};
+}
+
+template <typename FilterFunc, typename Container>
+class iter::impl::Filtered {
+ private:
+  Container container_;
+  mutable FilterFunc filter_func_;
+
+  friend FilterFn;
+
+ protected:
+  // Value constructor for use only in the filter function
+  Filtered(FilterFunc filter_func, Container&& container)
+      : container_(std::forward<Container>(container)),
+        filter_func_(filter_func) {}
+
+ public:
+  Filtered(Filtered&&) = default;
+
+  template <typename ContainerT>
+  class Iterator {
+   protected:
+    template <typename>
+    friend class Iterator;
+    using Holder = DerefHolder<iterator_deref<ContainerT>>;
+    IteratorWrapper<ContainerT> sub_iter_;
+    IteratorWrapper<ContainerT> sub_end_;
+    Holder item_;
+    FilterFunc* filter_func_;
+
+    void inc_sub_iter() {
+      ++sub_iter_;
+      if (sub_iter_ != sub_end_) {
+        item_.reset(*sub_iter_);
+      }
+    }
+
+    // increment until the iterator points to is true on the
+    // predicate.  Called by constructor and operator++
+    void skip_failures() {
+      while (sub_iter_ != sub_end_ && !(*filter_func_)(item_.get())) {
+        inc_sub_iter();
+      }
+    }
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = iterator_traits_deref<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter,
+        IteratorWrapper<ContainerT>&& sub_end, FilterFunc& filter_func)
+        : sub_iter_{std::move(sub_iter)},
+          sub_end_{std::move(sub_end)},
+          filter_func_(&filter_func) {
+      if (sub_iter_ != sub_end_) {
+        item_.reset(*sub_iter_);
+      }
+      skip_failures();
+    }
+
+    typename Holder::reference operator*() {
+      return item_.get();
+    }
+
+    typename Holder::pointer operator->() {
+      return item_.get_ptr();
+    }
+
+    Iterator& operator++() {
+      inc_sub_iter();
+      skip_failures();
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {get_begin(container_), get_end(container_), filter_func_};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_), filter_func_};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), filter_func_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), filter_func_};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/filterfalse.hpp b/src/external/cppitertools-1.0/filterfalse.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9de7a3dfdd84e38203758bd32fc53c9270c5ca92
--- /dev/null
+++ b/src/external/cppitertools-1.0/filterfalse.hpp
@@ -0,0 +1,53 @@
+#ifndef ITER_FILTER_FALSE_HPP_
+#define ITER_FILTER_FALSE_HPP_
+
+#include "filter.hpp"
+#include "internal/iterbase.hpp"
+
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    // Callable object that reverses the boolean result of another
+    // callable, taking the object in a Container's iterator
+    template <typename FilterFunc>
+    class PredicateFlipper {
+     private:
+      FilterFunc filter_func_;
+
+     public:
+      PredicateFlipper(FilterFunc filter_func)
+          : filter_func_(std::move(filter_func)) {}
+
+      // Calls the filter_func_
+      template <typename T>
+      bool operator()(const T& item) const {
+        return !bool(filter_func_(item));
+      }
+
+      // with non-const incase FilterFunc::operator() is non-const
+      template <typename T>
+      bool operator()(const T& item) {
+        return !bool(filter_func_(item));
+      }
+    };
+
+    template <typename FilterFunc, typename Container>
+    class FilterFalsed;
+
+    using FilterFalseFn = IterToolFnOptionalBindFirst<FilterFalsed, BoolTester>;
+  }
+  constexpr impl::FilterFalseFn filterfalse{};
+}
+
+// Delegates to Filtered with PredicateFlipper<FilterFunc>
+template <typename FilterFunc, typename Container>
+class iter::impl::FilterFalsed
+    : public Filtered<PredicateFlipper<FilterFunc>, Container> {
+  friend FilterFalseFn;
+  FilterFalsed(FilterFunc in_filter_func, Container&& in_container)
+      : Filtered<PredicateFlipper<FilterFunc>, Container>(
+            {in_filter_func}, std::forward<Container>(in_container)) {}
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/groupby.hpp b/src/external/cppitertools-1.0/groupby.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d0d9276b4f5ba8185f292f73fc332f40decc2618
--- /dev/null
+++ b/src/external/cppitertools-1.0/groupby.hpp
@@ -0,0 +1,297 @@
+#ifndef ITER_GROUP_BY_HPP_
+#define ITER_GROUP_BY_HPP_
+
+// this is easily the most functionally complex itertool
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename Container, typename KeyFunc>
+    class GroupProducer;
+
+    struct Identity {
+      template <typename T>
+      const T& operator()(const T& t) const {
+        return t;
+      }
+    };
+
+    using GroupByFn = IterToolFnOptionalBindSecond<GroupProducer, Identity>;
+  }
+  constexpr impl::GroupByFn groupby{};
+}
+
+template <typename Container, typename KeyFunc>
+class iter::impl::GroupProducer {
+ private:
+  Container container_;
+  mutable KeyFunc key_func_;
+
+  friend GroupByFn;
+
+  template <typename T>
+  using key_func_ret = std::result_of_t<KeyFunc(iterator_deref<T>)>;
+
+  GroupProducer(Container&& container, KeyFunc key_func)
+      : container_(std::forward<Container>(container)), key_func_(key_func) {}
+
+ public:
+  GroupProducer(GroupProducer&&) = default;
+
+  template <typename T>
+  class Iterator;
+  template <typename T>
+  class Group;
+
+ private:
+  template <typename T>
+  using KeyGroupPair = std::pair<key_func_ret<T>, Group<T>>;
+  template <typename T>
+  using Holder = DerefHolder<iterator_deref<T>>;
+
+ public:
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    IteratorWrapper<ContainerT> sub_iter_;
+    IteratorWrapper<ContainerT> sub_end_;
+    Holder<ContainerT> item_;
+    KeyFunc* key_func_;
+    std::unique_ptr<KeyGroupPair<ContainerT>> current_key_group_pair_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = KeyGroupPair<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter,
+        IteratorWrapper<ContainerT>&& sub_end, KeyFunc& key_func)
+        : sub_iter_{std::move(sub_iter)},
+          sub_end_{std::move(sub_end)},
+          key_func_(&key_func) {
+      if (sub_iter_ != sub_end_) {
+        item_.reset(*sub_iter_);
+      }
+    }
+
+    Iterator(const Iterator& other)
+        : sub_iter_{other.sub_iter_},
+          sub_end_{other.sub_end_},
+          item_{other.item_},
+          key_func_{other.key_func_} {}
+
+    Iterator& operator=(const Iterator& other) {
+      if (this == &other) {
+        return *this;
+      }
+      sub_iter_ = other.sub_iter_;
+      sub_end_ = other.sub_end_;
+      item_ = other.item_;
+      key_func_ = other.key_func_;
+      current_key_group_pair_.reset();
+      return *this;
+    }
+
+    ~Iterator() = default;
+
+    // NOTE the implicitly generated move constructor would
+    // be wrong
+
+    KeyGroupPair<ContainerT>& operator*() {
+      set_key_group_pair();
+      return *current_key_group_pair_;
+    }
+
+    KeyGroupPair<ContainerT>* operator->() {
+      set_key_group_pair();
+      return current_key_group_pair_.get();
+    }
+
+    Iterator& operator++() {
+      if (!current_key_group_pair_) {
+        set_key_group_pair();
+      }
+      current_key_group_pair_.reset();
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+
+    void increment_iterator() {
+      if (sub_iter_ != sub_end_) {
+        ++sub_iter_;
+        if (sub_iter_ != sub_end_) {
+          item_.reset(*sub_iter_);
+        }
+      }
+    }
+
+    bool exhausted() const {
+      return !(sub_iter_ != sub_end_);
+    }
+
+    typename Holder<ContainerT>::reference get() {
+      return item_.get();
+    }
+
+    typename Holder<ContainerT>::pointer get_ptr() {
+      return item_.get_ptr();
+    }
+
+    key_func_ret<ContainerT> next_key() {
+      return (*key_func_)(item_.get());
+    }
+
+    void set_key_group_pair() {
+      if (!current_key_group_pair_) {
+        current_key_group_pair_ = std::make_unique<KeyGroupPair<ContainerT>>(
+            (*key_func_)(item_.get()), Group<ContainerT>{*this, next_key()});
+      }
+    }
+  };
+
+  template <typename ContainerT>
+  class Group {
+   private:
+    template <typename>
+    friend class Iterator;
+    friend class GroupIterator;
+    Iterator<ContainerT>& owner_;
+    key_func_ret<ContainerT> key_;
+
+    // completed is set if a Group is iterated through
+    // completely.  It is checked in the destructor, and
+    // if the Group has not been completed, the destructor
+    // exhausts it.  This ensures that the next Group starts
+    // at the correct position when the user short-circuits
+    // iteration over a Group.
+    // The move constructor sets the rvalue's completed
+    // attribute to true, so its destructor doesn't do anything
+    // when called.
+    bool completed = false;
+
+    Group(Iterator<ContainerT>& owner, key_func_ret<ContainerT> key)
+        : owner_(owner), key_(key) {}
+
+   public:
+    ~Group() {
+      if (!completed) {
+        for (auto iter = begin(), end_it = end(); iter != end_it; ++iter) {
+        }
+      }
+    }
+
+    // move-constructible, non-copy-constructible, non-assignable
+    Group(Group&& other) noexcept : owner_(other.owner_),
+                                    key_{other.key_},
+                                    completed{other.completed} {
+      other.completed = true;
+    }
+
+    class GroupIterator {
+     private:
+      std::remove_reference_t<key_func_ret<ContainerT>>* key_;
+      Group* group_p_;
+
+      bool not_at_end() {
+        return !group_p_->owner_.exhausted()
+               && group_p_->owner_.next_key() == *key_;
+      }
+
+     public:
+      using iterator_category = std::input_iterator_tag;
+      using value_type = iterator_traits_deref<ContainerT>;
+      using difference_type = std::ptrdiff_t;
+      using pointer = value_type*;
+      using reference = value_type&;
+
+      // TODO template this? idk if it's relevant here
+      GroupIterator(Group* group_p, key_func_ret<ContainerT>& key)
+          : key_{&key}, group_p_{group_p} {}
+
+      bool operator!=(const GroupIterator& other) const {
+        return !(*this == other);
+      }
+
+      bool operator==(const GroupIterator& other) const {
+        return group_p_ == other.group_p_;
+      }
+
+      GroupIterator& operator++() {
+        group_p_->owner_.increment_iterator();
+        if (!not_at_end()) {
+          group_p_->completed = true;
+          group_p_ = nullptr;
+        }
+        return *this;
+      }
+
+      GroupIterator operator++(int) {
+        auto ret = *this;
+        ++*this;
+        return ret;
+      }
+
+      iterator_deref<ContainerT> operator*() {
+        return group_p_->owner_.get();
+      }
+
+      typename Holder<ContainerT>::pointer operator->() {
+        return group_p_->owner_.get_ptr();
+      }
+    };
+
+    GroupIterator begin() {
+      return {this, key_};
+    }
+
+    GroupIterator end() {
+      return {nullptr, key_};
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {get_begin(container_), get_end(container_), key_func_};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_), key_func_};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), key_func_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), key_func_};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/imap.hpp b/src/external/cppitertools-1.0/imap.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f022e6d01358218dc9f59cee048e862d81995c17
--- /dev/null
+++ b/src/external/cppitertools-1.0/imap.hpp
@@ -0,0 +1,23 @@
+#ifndef ITER_IMAP_H_
+#define ITER_IMAP_H_
+
+#include "starmap.hpp"
+#include "zip.hpp"
+
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    struct IMapFn : PipeableAndBindFirst<IMapFn> {
+      template <typename MapFunc, typename... Containers>
+      decltype(auto) operator()(
+          MapFunc map_func, Containers&&... containers) const {
+        return starmap(map_func, zip(std::forward<Containers>(containers)...));
+      }
+      using PipeableAndBindFirst<IMapFn>::operator();
+    };
+  }
+  constexpr impl::IMapFn imap{};
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/internal/iter_tuples.hpp b/src/external/cppitertools-1.0/internal/iter_tuples.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..99fdfffe2552509bb8818e8c4a8fa8056193e71e
--- /dev/null
+++ b/src/external/cppitertools-1.0/internal/iter_tuples.hpp
@@ -0,0 +1,72 @@
+#ifndef ITERTOOLS_ITER_TUPLES_HPP_
+#define ITERTOOLS_ITER_TUPLES_HPP_
+
+#include "iterator_wrapper.hpp"
+#include "iterbase.hpp"
+
+namespace iter {
+  namespace impl {
+    namespace detail {
+      template <typename... Ts>
+      std::tuple<iterator_deref<Ts>...> iterator_tuple_deref_helper(
+          const std::tuple<Ts...>&);
+
+      template <typename... Ts>
+      std::tuple<IteratorWrapper<Ts>...> iterator_tuple_type_helper(
+          const std::tuple<Ts...>&);
+
+      template <typename... Ts>
+      std::tuple<iterator_deref<const std::remove_reference_t<Ts>>...>
+      const_iterator_tuple_deref_helper(const std::tuple<Ts...>&);
+
+      template <typename... Ts>
+      std::tuple<IteratorWrapper<const std::remove_reference_t<Ts>>...>
+      const_iterator_tuple_type_helper(const std::tuple<Ts...>&);
+    }
+    // Given a tuple template argument, evaluates to a tuple of iterators
+    // for the template argument's contained types.
+    template <typename TupleType>
+    using iterator_tuple_type =
+        decltype(detail::iterator_tuple_type_helper(std::declval<TupleType>()));
+
+    template <typename TupleType>
+    using const_iterator_tuple_type = decltype(
+        detail::const_iterator_tuple_type_helper(std::declval<TupleType>()));
+
+    // Given a tuple template argument, evaluates to a tuple of
+    // what the iterators for the template argument's contained types
+    // dereference to
+    template <typename TupleType>
+    using iterator_deref_tuple = decltype(
+        detail::iterator_tuple_deref_helper(std::declval<TupleType>()));
+
+    template <typename TupleType>
+    using const_iterator_deref_tuple = decltype(
+        detail::const_iterator_tuple_deref_helper(std::declval<TupleType>()));
+
+    // function absorbing all arguments passed to it. used when
+    // applying a function to a parameter pack but not passing the evaluated
+    // results anywhere
+    template <typename... Ts>
+    void absorb(Ts&&...) {}
+
+    namespace detail {
+      template <typename Func, typename TupleType, std::size_t... Is>
+      decltype(auto) call_with_tuple_impl(
+          Func&& mf, TupleType&& tup, std::index_sequence<Is...>) {
+        return mf(std::forward<std::tuple_element_t<Is,
+                std::remove_reference_t<TupleType>>>(std::get<Is>(tup))...);
+      }
+    }
+
+    // expand a TupleType into individual arguments when calling a Func
+    template <typename Func, typename TupleType>
+    decltype(auto) call_with_tuple(Func&& mf, TupleType&& tup) {
+      constexpr auto TUP_SIZE = std::tuple_size<std::decay_t<TupleType>>::value;
+      return detail::call_with_tuple_impl(std::forward<Func>(mf),
+          std::forward<TupleType>(tup), std::make_index_sequence<TUP_SIZE>{});
+    }
+  }
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/internal/iterator_wrapper.hpp b/src/external/cppitertools-1.0/internal/iterator_wrapper.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..272266cff315c9000ce378b47c4be66ddf9e46e3
--- /dev/null
+++ b/src/external/cppitertools-1.0/internal/iterator_wrapper.hpp
@@ -0,0 +1,170 @@
+#ifndef ITERTOOLS_ITERATOR_WRAPPER_HPP_
+#define ITERTOOLS_ITERATOR_WRAPPER_HPP_
+
+#include <cassert>
+#include "iterbase.hpp"
+
+namespace iter {
+  namespace impl {
+    // iterator_end_type<C> is the type of C's end iterator
+    template <typename Container>
+    using iterator_end_type = decltype(get_end(std::declval<Container&>()));
+
+    template <typename SubIter, typename SubEnd>
+    class IteratorWrapperImpl;
+
+    // If begin and end return the same type, type will be
+    // iterator_type<Container>
+    // If begin and end return different types, type will be IteratorWrapperImpl
+    template <typename Container, bool same_types>
+    struct IteratorWrapperImplType;
+
+    template <typename Container>
+    struct IteratorWrapperImplType<Container, true>
+        : type_is<iterator_type<Container>> {};
+
+    template <typename Container>
+    struct IteratorWrapperImplType<Container, false>
+        : type_is<IteratorWrapperImpl<iterator_type<Container>,
+              iterator_end_type<Container>>> {};
+
+    template <typename Container>
+    using IteratorWrapper = typename IteratorWrapperImplType<Container,
+        std::is_same<impl::iterator_type<Container>,
+            impl::iterator_end_type<Container>>{}>::type;
+  }
+}
+
+template <typename SubIter, typename SubEnd>
+class iter::impl::IteratorWrapperImpl {
+ private:
+  static_assert(!std::is_same<SubIter, SubEnd>{}, "");
+  enum class IterState { Normal, End, Uninitialized };
+
+  void destroy_sub() {
+    if (state_ == IterState::Normal) {
+      sub_iter_.~SubIter();
+    } else if (state_ == IterState::End) {
+      sub_end_.~SubEnd();
+    }
+    state_ = IterState::Uninitialized;
+  }
+
+  template <typename T>
+  void copy_or_move_sub_from(T&& other) {
+    if (this == &other) {
+      return;
+    }
+    if (state_ == other.state_) {
+      if (state_ == IterState::Normal) {
+        sub_iter_ = std::forward<T>(other).sub_iter_;
+      } else if (state_ == IterState::End) {
+        sub_end_ = std::forward<T>(other).sub_end_;
+      }
+    } else {
+      // state_s are different, must destroy and reconstruct
+      destroy_sub();
+      if (other.state_ == IterState::Normal) {
+        new (&sub_iter_) SubIter(std::forward<T>(other).sub_iter_);
+      } else if (other.state_ == IterState::End) {
+        new (&sub_end_) SubEnd(std::forward<T>(other).sub_end_);
+      }
+      state_ = other.state_;
+    }
+  }
+
+  void copy_sub_from(const IteratorWrapperImpl& other) {
+    copy_or_move_sub_from(other);
+  }
+
+  void move_sub_from(IteratorWrapperImpl&& other) {
+    copy_or_move_sub_from(std::move(other));
+  }
+
+  // TODO replace with std::variant when C++17 is going strong
+  union {
+    SubIter sub_iter_;
+    SubEnd sub_end_;
+  };
+  IterState state_{IterState::Uninitialized};
+
+ public:
+  IteratorWrapperImpl() : IteratorWrapperImpl(SubIter{}) {}
+
+  IteratorWrapperImpl(const IteratorWrapperImpl& other) {
+    copy_sub_from(other);
+  }
+
+  IteratorWrapperImpl& operator=(const IteratorWrapperImpl& other) {
+    copy_sub_from(other);
+    return *this;
+  }
+
+  IteratorWrapperImpl(IteratorWrapperImpl&& other) {
+    move_sub_from(std::move(other));
+  }
+
+  IteratorWrapperImpl& operator=(IteratorWrapperImpl&& other) {
+    move_sub_from(std::move(other));
+    return *this;
+  }
+
+  IteratorWrapperImpl(SubIter&& it)
+      : sub_iter_{std::move(it)}, state_{IterState::Normal} {}
+
+  IteratorWrapperImpl(SubEnd&& it)
+      : sub_end_(std::move(it)), state_{IterState::End} {}
+
+  IteratorWrapperImpl& operator++() {
+    assert(state_ == IterState::Normal);  // because ++ing the end is UB
+    ++sub_iter_;
+    return *this;
+  }
+
+  decltype(auto) operator*() {
+    assert(state_ == IterState::Normal);  // because *ing the end is UB
+    return *sub_iter_;
+  }
+
+  decltype(auto) operator*() const {
+    assert(state_ == IterState::Normal);  // because *ing the end is UB
+    return *sub_iter_;
+  }
+
+  decltype(auto) operator-> () {
+    assert(state_ == IterState::Normal);
+    return apply_arrow(sub_iter_);
+  }
+
+  decltype(auto) operator-> () const {
+    assert(state_ == IterState::Normal);
+    return apply_arrow(sub_iter_);
+  }
+
+  bool operator!=(const IteratorWrapperImpl& other) const {
+    assert(state_ != IterState::Uninitialized
+           && other.state_ != IterState::Uninitialized);
+    if (state_ == other.state_) {
+      if (state_ == IterState::End) {
+        // NOTE this used to be return sub_end_ != other.sub_end_;
+        // but rangev3 sentinels aren't comparable
+        // https://github.com/ericniebler/range-v3/issues/564
+        return false;
+      } else {
+        return sub_iter_ != other.sub_iter_;
+      }
+    } else {
+      if (state_ == IterState::Normal) {  // other is End
+        return sub_iter_ != other.sub_end_;
+      } else {  // other is Normal
+        return sub_end_ != other.sub_iter_;
+      }
+    }
+  }
+
+  ~IteratorWrapperImpl() {
+    this->destroy_sub();
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/internal/iteratoriterator.hpp b/src/external/cppitertools-1.0/internal/iteratoriterator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..af7ceefc0a5dcb6bd174b987cc508783bea2536e
--- /dev/null
+++ b/src/external/cppitertools-1.0/internal/iteratoriterator.hpp
@@ -0,0 +1,279 @@
+#ifndef ITERATOR_ITERATOR_HPP_
+#define ITERATOR_ITERATOR_HPP_
+
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include "iterbase.hpp"
+
+// IterIterWrapper and IteratorIterator provide a means to have a container
+// of iterators act like a container of the pointed to objects. This is useful
+// for combinatorics and similar itertools which need to keep track of
+// more than one element at a time.
+// an IterIterWrapper<some_collection_type<collection<T>::iterator>>
+// behave like some_collection<T> when iterated over or indexed
+
+namespace iter {
+  namespace impl {
+    template <typename T, typename = void>
+    struct HasConstDeref : std::false_type {};
+
+    template <typename T>
+    struct HasConstDeref<T, void_t<decltype(*std::declval<const T&>())>>
+        : std::true_type {};
+
+    template <typename TopIter>
+    class IteratorIterator {
+      template <typename> friend class IteratorIterator;
+      using Diff = std::ptrdiff_t;
+      static_assert(
+          std::is_same<
+              typename std::iterator_traits<TopIter>::iterator_category,
+              std::random_access_iterator_tag>::value,
+          "IteratorIterator only works with random access iterators");
+
+     private:
+      TopIter sub_iter;
+
+     public:
+      using iterator_category = std::random_access_iterator_tag;
+      using value_type = std::remove_reference_t<decltype(**std::declval<TopIter>())>;
+      using difference_type = std::ptrdiff_t;
+      using pointer = value_type*;
+      using reference = value_type&;
+      IteratorIterator() = default;
+      IteratorIterator(const TopIter& it) : sub_iter{it} {}
+
+      const TopIter& get() const {
+        return sub_iter;
+      }
+
+      template <typename T>
+      bool operator==(const IteratorIterator<T>& other) const {
+        return !(*this != other);
+      }
+
+      template <typename T>
+      bool operator!=(const IteratorIterator<T>& other) const {
+        return this->sub_iter != other.sub_iter;
+      }
+
+      IteratorIterator& operator++() {
+        ++this->sub_iter;
+        return *this;
+      }
+
+      IteratorIterator operator++(int) {
+        auto ret = *this;
+        ++*this;
+        return ret;
+      }
+
+      IteratorIterator& operator--() {
+        --this->sub_iter;
+        return *this;
+      }
+
+      IteratorIterator operator--(int) {
+        auto ret = *this;
+        --*this;
+        return ret;
+      }
+
+      auto operator*() -> decltype(**sub_iter) {
+        return **this->sub_iter;
+      }
+
+      auto operator-> () -> decltype(*sub_iter) {
+        return *this->sub_iter;
+      }
+
+      IteratorIterator& operator+=(Diff n) {
+        this->sub_iter += n;
+        return *this;
+      }
+
+      IteratorIterator operator+(Diff n) const {
+        auto it = *this;
+        it += n;
+        return it;
+      }
+
+      friend IteratorIterator operator+(Diff n, IteratorIterator it) {
+        it += n;
+        return it;
+      }
+
+      IteratorIterator& operator-=(Diff n) {
+        this->sub_iter -= n;
+        return *this;
+      }
+
+      IteratorIterator operator-(Diff n) const {
+        auto it = *this;
+        it -= n;
+        return it;
+      }
+
+      friend IteratorIterator operator-(Diff n, IteratorIterator it) {
+        it -= n;
+        return it;
+      }
+
+      Diff operator-(const IteratorIterator& rhs) const {
+        return this->sub_iter - rhs.sub_iter;
+      }
+
+      auto operator[](Diff idx) -> decltype(*sub_iter[idx]) {
+        return *sub_iter[idx];
+      }
+
+      bool operator<(const IteratorIterator& rhs) const {
+        return this->sub_iter < rhs.sub_iter;
+      }
+
+      bool operator>(const IteratorIterator& rhs) const {
+        return this->sub_iter > rhs.sub_iter;
+      }
+
+      bool operator<=(const IteratorIterator& rhs) const {
+        return this->sub_iter <= rhs.sub_iter;
+      }
+
+      bool operator>=(const IteratorIterator& rhs) const {
+        return this->sub_iter >= rhs.sub_iter;
+      }
+    };
+
+    template <typename Container>
+    class IterIterWrapper {
+     private:
+      Container container;
+
+      using contained_iter = typename Container::value_type;
+      using size_type = typename Container::size_type;
+      using iterator = IteratorIterator<typename Container::iterator>;
+      using const_iterator =
+          IteratorIterator<typename Container::const_iterator>;
+      using reverse_iterator =
+          IteratorIterator<typename Container::reverse_iterator>;
+      using const_reverse_iterator =
+          IteratorIterator<typename Container::const_reverse_iterator>;
+
+      template <typename U = Container, typename = void>
+      struct ConstAtTypeOrVoid : type_is<void> {};
+
+      template <typename U>
+      struct ConstAtTypeOrVoid<U,
+          void_t<decltype(*std::declval<const U&>().at(0))>>
+          : type_is<decltype(*std::declval<const U&>().at(0))> {};
+
+      using const_at_type_or_void_t = typename ConstAtTypeOrVoid<>::type;
+
+      template <typename U = Container, typename = void>
+      struct ConstIndexTypeOrVoid : type_is<void> {};
+
+      template <typename U>
+      struct ConstIndexTypeOrVoid<U,
+          void_t<decltype(*std::declval<const U&>()[0])>>
+          : type_is<decltype(*std::declval<const U&>()[0])> {};
+
+      using const_index_type_or_void_t = typename ConstIndexTypeOrVoid<>::type;
+
+     public:
+      IterIterWrapper() = default;
+
+      explicit IterIterWrapper(size_type sz) : container(sz) {}
+
+      IterIterWrapper(size_type sz, const contained_iter& val)
+          : container(sz, val) {}
+
+      auto at(size_type pos) -> decltype(*container.at(pos)) {
+        return *container.at(pos);
+      }
+
+      auto at(size_type pos) const -> const_at_type_or_void_t {
+        return *container.at(pos);
+      }
+
+      auto operator[](size_type pos) noexcept(noexcept(*container[pos]))
+          -> decltype(*container[pos]) {
+        return *container[pos];
+      }
+
+      auto operator[](size_type pos) const noexcept(noexcept(*container[pos]))
+          -> const_index_type_or_void_t {
+        return *container[pos];
+      }
+
+      bool empty() const noexcept {
+        return container.empty();
+      }
+
+      size_type size() const noexcept {
+        return container.size();
+      }
+
+      iterator begin() noexcept {
+        return {container.begin()};
+      }
+
+      iterator end() noexcept {
+        return {container.end()};
+      }
+
+      const_iterator begin() const noexcept {
+        return {container.begin()};
+      }
+
+      const_iterator end() const noexcept {
+        return {container.end()};
+      }
+
+      const_iterator cbegin() const noexcept {
+        return {container.cbegin()};
+      }
+
+      const_iterator cend() const noexcept {
+        return {container.cend()};
+      }
+
+      reverse_iterator rbegin() noexcept {
+        return {container.rbegin()};
+      }
+
+      reverse_iterator rend() noexcept {
+        return {container.rend()};
+      }
+
+      const_reverse_iterator rbegin() const noexcept {
+        return {container.rbegin()};
+      }
+
+      const_reverse_iterator rend() const noexcept {
+        return {container.rend()};
+      }
+
+      const_reverse_iterator crbegin() const noexcept {
+        return {container.rbegin()};
+      }
+
+      const_reverse_iterator crend() const noexcept {
+        return {container.rend()};
+      }
+
+      // get() exposes the underlying container.  this allows the
+      // itertools to manipulate the iterators in the container
+      // and should not be depended on anywhere else.
+      Container& get() noexcept {
+        return container;
+      }
+
+      const Container& get() const noexcept {
+        return container;
+      }
+    };
+  }
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/internal/iterbase.hpp b/src/external/cppitertools-1.0/internal/iterbase.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..88d487bab2f5c71babf8b4a418e8fcf4f852394c
--- /dev/null
+++ b/src/external/cppitertools-1.0/internal/iterbase.hpp
@@ -0,0 +1,473 @@
+#ifndef ITERBASE_HPP_
+#define ITERBASE_HPP_
+
+// This file consists of utilities used for the generic nature of the
+// iterable wrapper classes.  As such, the contents of this file should be
+// considered UNDOCUMENTED and is subject to change without warning.  This
+// also applies to the name of the file.  No user code should include
+// this file directly.
+
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    namespace get_iters {
+      // This has to be set up in a really weird way.
+      // This looks at first as if it could be
+      // decltype(auto) get_begin(T& t) {
+      //     using std::begin;
+      //     return begin(t);
+      // }
+      // However, without return types in the declaration, SFINAE gets
+      // messed up everywhere.
+      using std::begin;
+      // TODO add constexpr for c++17
+      template <typename T>
+      auto get_begin(T& t) -> decltype(begin(t)) {
+        return begin(t);
+      }
+      using std::end;
+      // TODO add constexpr for c++17
+      template <typename T>
+      auto get_end(T& t) -> decltype(end(t)) {
+        return end(t);
+      }
+    }
+    using get_iters::get_begin;
+    using get_iters::get_end;
+
+    template <typename T>
+    struct type_is {
+      using type = T;
+    };
+
+    // TODO use std::as_const for c++17
+    template <typename T>
+    const T& as_const(T& t) {
+      return t;
+    }
+    template <typename T>
+    const T& as_const(const T& t) {
+      return t;
+    }
+    template <typename T>
+    void as_const(T&&) = delete;
+
+    template <typename T>
+    using AsConst = decltype(impl::as_const(std::declval<T&>()));
+
+    // gcc CWG 1558
+    template <typename...>
+    struct void_t_help {
+      using type = void;
+    };
+    template <typename... Ts>
+    using void_t = typename void_t_help<Ts...>::type;
+
+    // iterator_type<C> is the type of C's iterator
+    template <typename Container>
+    using iterator_type = decltype(get_begin(std::declval<Container&>()));
+
+    // iterator_type<C> is the type of C's iterator
+    template <typename Container>
+    using const_iterator_type = decltype(
+        get_begin(std::declval<const std::remove_reference_t<Container>&>()));
+
+    // iterator_deref<C> is the type obtained by dereferencing an iterator
+    // to an object of type C
+    template <typename Container>
+    using iterator_deref = decltype(*std::declval<iterator_type<Container>&>());
+
+    // const_iteator_deref is the type obtained through dereferencing
+    // a const iterator& (note: not a const_iterator).  ie: the result
+    // of Container::iterator::operator*() const
+    template <typename Container>
+    using const_iterator_deref =
+        decltype(*std::declval<const iterator_type<Container>&>());
+
+    // the type of dereferencing a const_iterator
+    template <typename Container>
+    using const_iterator_type_deref =
+        decltype(*std::declval<const_iterator_type<Container>&>());
+
+    template <typename Container>
+    using iterator_traits_deref =
+        std::remove_reference_t<iterator_deref<Container>>;
+
+    template <typename T, typename = void>
+    struct IsIterable : std::false_type {};
+
+    // Assuming that if a type works with begin, it is an iterable.
+    template <typename T>
+    struct IsIterable<T, void_t<iterator_type<T>>> : std::true_type {};
+
+    template <typename T>
+    constexpr bool is_iterable = IsIterable<T>::value;
+
+    namespace detail {
+      template <typename T, typename = void>
+      struct ArrowHelper {
+        using type = void;
+        void operator()(T&) const noexcept {}
+      };
+
+      template <typename T>
+      struct ArrowHelper<T*, void> {
+        using type = T*;
+        constexpr type operator()(T* t) const noexcept {
+          return t;
+        }
+      };
+
+      template <typename T>
+      struct ArrowHelper<T, void_t<decltype(std::declval<T&>().operator->())>> {
+        using type = decltype(std::declval<T&>().operator->());
+        type operator()(T& t) const {
+          return t.operator->();
+        }
+      };
+
+      template <typename T>
+      using arrow = typename detail::ArrowHelper<T>::type;
+    }
+
+    // type of C::iterator::operator->, also works with pointers
+    // void if the iterator has no operator->
+    template <typename C>
+    using iterator_arrow = detail::arrow<iterator_type<C>>;
+
+    // applys the -> operator to an object, if the object is a pointer,
+    // it returns the pointer
+    template <typename T>
+    detail::arrow<T> apply_arrow(T& t) {
+      return detail::ArrowHelper<T>{}(t);
+    }
+
+    // For iterators that have an operator* which returns a value
+    // they can return this type from their operator-> instead, which will
+    // wrap an object and allow it to be used with arrow
+    template <typename T>
+    class ArrowProxy {
+     private:
+      using TPlain = typename std::remove_reference<T>::type;
+      T obj;
+
+     public:
+      constexpr ArrowProxy(T&& in_obj) : obj(std::forward<T>(in_obj)) {}
+
+      TPlain* operator->() {
+        return &obj;
+      }
+    };
+
+    template <typename, typename = void>
+    struct is_random_access_iter : std::false_type {};
+
+    template <typename T>
+    struct is_random_access_iter<T,
+        std::enable_if_t<std::is_same<
+            typename std::iterator_traits<T>::iterator_category,
+            std::random_access_iterator_tag>::value>> : std::true_type {};
+
+    template <typename T>
+    using has_random_access_iter = is_random_access_iter<iterator_type<T>>;
+    // because std::advance assumes a lot and is actually smart, I need a dumb
+
+    // version that will work with most things
+    template <typename InputIt, typename Distance = std::size_t>
+    void dumb_advance_unsafe(InputIt& iter, Distance distance) {
+      for (Distance i(0); i < distance; ++i) {
+        ++iter;
+      }
+    }
+
+    template <typename Iter, typename EndIter, typename Distance>
+    void dumb_advance_impl(
+        Iter& iter, const EndIter& end, Distance distance, std::false_type) {
+      for (Distance i(0); i < distance && iter != end; ++i) {
+        ++iter;
+      }
+    }
+
+    template <typename Iter, typename EndIter, typename Distance>
+    void dumb_advance_impl(
+        Iter& iter, const EndIter& end, Distance distance, std::true_type) {
+      if (static_cast<Distance>(end - iter) < distance) {
+        iter = end;
+      } else {
+        iter += distance;
+      }
+    }
+
+    // iter will not be incremented past end
+    template <typename Iter, typename EndIter, typename Distance = std::size_t>
+    void dumb_advance(Iter& iter, const EndIter& end, Distance distance) {
+      dumb_advance_impl(iter, end, distance, is_random_access_iter<Iter>{});
+    }
+
+    template <typename ForwardIt, typename Distance = std::size_t>
+    ForwardIt dumb_next(ForwardIt it, Distance distance = 1) {
+      dumb_advance_unsafe(it, distance);
+      return it;
+    }
+
+    template <typename ForwardIt, typename Distance = std::size_t>
+    ForwardIt dumb_next(
+        ForwardIt it, const ForwardIt& end, Distance distance = 1) {
+      dumb_advance(it, end, distance);
+      return it;
+    }
+
+    template <typename Container, typename Distance = std::size_t>
+    Distance dumb_size(Container&& container) {
+      Distance d{0};
+      auto end_it = get_end(container);
+      for (auto it = get_begin(container); it != end_it; ++it) {
+        ++d;
+      }
+      return d;
+    }
+
+    template <typename... Ts>
+    struct are_same : std::true_type {};
+
+    template <typename T, typename U, typename... Ts>
+    struct are_same<T, U, Ts...>
+        : std::integral_constant<bool,
+              std::is_same<T, U>::value && are_same<T, Ts...>::value> {};
+
+    // DerefHolder holds the value gotten from an iterator dereference
+    // if the iterate dereferences to an lvalue references, a pointer to the
+    //     element is stored
+    // if it does not, a value is stored instead
+    // get() returns a reference to the held item
+    // get_ptr() returns a pointer to the held item
+    // reset() replaces the currently held item
+    template <typename T>
+    class DerefHolder {
+     private:
+      static_assert(!std::is_lvalue_reference<T>::value,
+          "Non-lvalue-ref specialization used for lvalue ref type");
+      // it could still be an rvalue reference
+      using TPlain = std::remove_reference_t<T>;
+
+      std::unique_ptr<TPlain> item_p;
+
+     public:
+      using reference = TPlain&;
+      using pointer = TPlain*;
+
+      DerefHolder() = default;
+
+      DerefHolder(const DerefHolder& other)
+          : item_p{other.item_p ? std::make_unique<TPlain>(*other.item_p)
+                                : nullptr} {}
+
+      DerefHolder& operator=(const DerefHolder& other) {
+        this->item_p =
+            other.item_p ? std::make_unique<TPlain>(*other.item_p) : nullptr;
+        return *this;
+      }
+
+      DerefHolder(DerefHolder&&) = default;
+      DerefHolder& operator=(DerefHolder&&) = default;
+      ~DerefHolder() = default;
+
+      reference get() {
+        return *this->item_p;
+      }
+
+      pointer get_ptr() {
+        return this->item_p.get();
+      }
+
+      void reset(T&& item) {
+        item_p = std::make_unique<TPlain>(std::move(item));
+      }
+
+      explicit operator bool() const {
+        return static_cast<bool>(this->item_p);
+      }
+    };
+
+    // Specialization for when T is an lvalue ref
+    template <typename T>
+    class DerefHolder<T&> {
+     public:
+      using reference = T&;
+      using pointer = T*;
+
+     private:
+      pointer item_p{};
+
+     public:
+      DerefHolder() = default;
+
+      reference get() {
+        return *this->item_p;
+      }
+
+      pointer get_ptr() {
+        return this->item_p;
+      }
+
+      void reset(reference item) {
+        this->item_p = &item;
+      }
+
+      explicit operator bool() const {
+        return this->item_p != nullptr;
+      }
+    };
+
+    // allows f(x) to be 'called' as x | f
+    // let the record show I dislike adding yet another syntactical mess to
+    // this clown car of a language.
+    template <typename ItTool>
+    struct Pipeable {
+      template <typename T>
+      friend decltype(auto) operator|(T&& x, const Pipeable& p) {
+        return static_cast<const ItTool&>(p)(std::forward<T>(x));
+      }
+    };
+
+    // Pipeable Callable generator, where ItImpl is templated on the first
+    // argument to the call.
+    template <template <typename> class ItImpl>
+    struct IterToolFn : Pipeable<IterToolFn<ItImpl>> {
+      template <typename T, typename... Ts>
+      ItImpl<T> operator()(T&& t, Ts... ts) const {
+        return {std::forward<T>(t), std::move(ts)...};
+      }
+    };
+
+    // Pipeable callable which allows binding of the first argument
+    // f(a, b) is the same as b | f(a)
+    template <typename F>
+    struct PipeableAndBindFirst : Pipeable<F> {
+     protected:
+      template <typename T>
+      struct FnPartial : Pipeable<FnPartial<T>> {
+        mutable T stored_arg;
+        constexpr FnPartial(T in_t) : stored_arg(in_t) {}
+
+        template <typename Container>
+        auto operator()(Container&& container) const {
+          return F{}(stored_arg, std::forward<Container>(container));
+        }
+      };
+
+     public:
+      template <typename T, typename = std::enable_if_t<!is_iterable<T>>>
+      FnPartial<std::decay_t<T>> operator()(T&& t) const {
+        return {std::forward<T>(t)};
+      }
+    };
+
+    // This is a complicated class to generate a callable that can work:
+    //  (1) with just a single (iterable) passed, and DefaultT substituted
+    //  (2) with an iterable and a callable
+    //  (3) with just a callable, to have the iterable passed later via pipe
+    template <template <typename, typename> class ItImpl, typename DefaultT>
+    struct IterToolFnOptionalBindFirst
+        : PipeableAndBindFirst<IterToolFnOptionalBindFirst<ItImpl, DefaultT>> {
+     private:
+      using Base =
+          PipeableAndBindFirst<IterToolFnOptionalBindFirst<ItImpl, DefaultT>>;
+
+     protected:
+      template <typename Container>
+      auto operator()(Container&& container, std::false_type) const {
+        return static_cast<const Base&>(*this)(
+            std::forward<Container>(container));
+      }
+
+      template <typename Container>
+      auto operator()(Container&& container, std::true_type) const {
+        return (*this)(DefaultT{}, std::forward<Container>(container));
+      }
+
+     public:
+      template <typename T>
+      auto operator()(T&& t) const {
+        return (*this)(std::forward<T>(t), IsIterable<T>{});
+      }
+
+      template <typename T, typename Container,
+          typename = std::enable_if_t<is_iterable<Container>>>
+      ItImpl<T, Container> operator()(T func, Container&& container) const {
+        return {std::move(func), std::forward<Container>(container)};
+      }
+    };
+
+    template <template <typename, typename> class ItImpl, typename DefaultT>
+    struct IterToolFnOptionalBindSecond
+        : Pipeable<IterToolFnOptionalBindSecond<ItImpl, DefaultT>> {
+     private:
+      // T is whatever is being held for later use
+      template <typename T>
+      struct FnPartial : Pipeable<FnPartial<T>> {
+        mutable T stored_arg;
+        constexpr FnPartial(T in_t) : stored_arg(in_t) {}
+
+        template <typename Container>
+        auto operator()(Container&& container) const {
+          return IterToolFnOptionalBindSecond{}(
+              std::forward<Container>(container), stored_arg);
+        }
+      };
+
+     public:
+      template <typename Container, typename T>
+      ItImpl<Container, T> operator()(Container&& container, T func) const {
+        return {std::forward<Container>(container), std::move(func)};
+      }
+
+      template <typename T, typename = std::enable_if_t<!is_iterable<T>>>
+      FnPartial<std::decay_t<T>> operator()(T&& func) const {
+        return {std::forward<T>(func)};
+      }
+
+      template <typename Container,
+          typename = std::enable_if_t<is_iterable<Container>>>
+      auto operator()(Container&& container) const {
+        return (*this)(std::forward<Container>(container), DefaultT{});
+      }
+    };
+
+    template <template <typename> class ItImpl>
+    struct IterToolFnBindSizeTSecond {  // NOTE not pipeable
+     private:
+      using Size = std::size_t;
+      struct FnPartial : Pipeable<FnPartial> {
+        Size sz{};
+        constexpr FnPartial(Size in_sz) : sz{in_sz} {}
+
+        template <typename Container>
+        auto operator()(Container&& container) const {
+          return IterToolFnBindSizeTSecond{}(
+              std::forward<Container>(container), sz);
+        }
+      };
+
+     public:
+      FnPartial operator()(Size sz) const {
+        return {sz};
+      }
+
+      template <typename Container,
+          typename = std::enable_if_t<is_iterable<Container>>>
+      ItImpl<Container> operator()(Container&& container, Size sz) const {
+        return {std::forward<Container>(container), sz};
+      }
+    };
+  }
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/itertools.hpp b/src/external/cppitertools-1.0/itertools.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7dd9b9a8b19641d48fc8fa352e1c96155b6fdd1c
--- /dev/null
+++ b/src/external/cppitertools-1.0/itertools.hpp
@@ -0,0 +1,36 @@
+#ifndef ITERTOOLS_ALL_HPP_
+#define ITERTOOLS_ALL_HPP_
+
+#include "accumulate.hpp"
+#include "chain.hpp"
+#include "chunked.hpp"
+#include "combinations.hpp"
+#include "combinations_with_replacement.hpp"
+#include "compress.hpp"
+#include "count.hpp"
+#include "cycle.hpp"
+#include "dropwhile.hpp"
+#include "enumerate.hpp"
+#include "filter.hpp"
+#include "filterfalse.hpp"
+#include "groupby.hpp"
+#include "imap.hpp"
+#include "permutations.hpp"
+#include "powerset.hpp"
+#include "product.hpp"
+#include "range.hpp"
+#include "repeat.hpp"
+#include "reversed.hpp"
+#include "slice.hpp"
+#include "sliding_window.hpp"
+#include "sorted.hpp"
+#include "starmap.hpp"
+#include "takewhile.hpp"
+#include "unique_everseen.hpp"
+#include "unique_justseen.hpp"
+#include "zip.hpp"
+
+// zip_longest is the only itertool with a boost depedency, so it must be
+// included explicitly
+
+#endif
diff --git a/src/external/cppitertools-1.0/permutations.hpp b/src/external/cppitertools-1.0/permutations.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..40945040e5ef4434bc4743676c6ef7207eaf69f0
--- /dev/null
+++ b/src/external/cppitertools-1.0/permutations.hpp
@@ -0,0 +1,128 @@
+#ifndef ITER_PERMUTATIONS_HPP_
+#define ITER_PERMUTATIONS_HPP_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iteratoriterator.hpp"
+#include "internal/iterbase.hpp"
+
+#include <algorithm>
+#include <initializer_list>
+#include <iterator>
+#include <utility>
+#include <vector>
+
+namespace iter {
+  namespace impl {
+    template <typename Container>
+    class Permuter;
+    using PermutationsFn = IterToolFn<Permuter>;
+  }
+  constexpr impl::PermutationsFn permutations{};
+}
+
+template <typename Container>
+class iter::impl::Permuter {
+ private:
+  friend PermutationsFn;
+  Container container_;
+
+  template <typename T>
+  using IndexVector = std::vector<IteratorWrapper<T>>;
+  template <typename T>
+  using Permutable = IterIterWrapper<IndexVector<T>>;
+
+  Permuter(Container&& container)
+      : container_(std::forward<Container>(container)) {}
+
+ public:
+  Permuter(Permuter&&) = default;
+
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    static constexpr const int COMPLETE = -1;
+    static bool cmp_iters(IteratorWrapper<ContainerT> lhs,
+        IteratorWrapper<ContainerT> rhs) noexcept {
+      return *lhs < *rhs;
+    }
+
+    Permutable<ContainerT> working_set_;
+    int steps_{};
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = Permutable<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter,
+        IteratorWrapper<ContainerT>&& sub_end)
+        : steps_{sub_iter != sub_end ? 0 : COMPLETE} {
+      // done like this instead of using vector ctor with
+      // two iterators because that causes a substitution
+      // failure when the iterator is minimal
+      while (sub_iter != sub_end) {
+        working_set_.get().push_back(sub_iter);
+        ++sub_iter;
+      }
+      std::sort(get_begin(working_set_.get()), get_end(working_set_.get()),
+          cmp_iters);
+    }
+
+    Permutable<ContainerT>& operator*() {
+      return working_set_;
+    }
+
+    Permutable<ContainerT>* operator->() {
+      return &working_set_;
+    }
+
+    Iterator& operator++() {
+      ++steps_;
+      if (!std::next_permutation(get_begin(working_set_.get()),
+              get_end(working_set_.get()), cmp_iters)) {
+        steps_ = COMPLETE;
+      }
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return !(*this == other);
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return steps_ == other.steps_;
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {get_begin(container_), get_end(container_)};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_)};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_end(impl::as_const(container_))};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_))};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/powerset.hpp b/src/external/cppitertools-1.0/powerset.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..af2706346211a4c21dc3bcbf4349ea068f8e1f39
--- /dev/null
+++ b/src/external/cppitertools-1.0/powerset.hpp
@@ -0,0 +1,131 @@
+#ifndef ITER_POWERSET_HPP_
+#define ITER_POWERSET_HPP_
+
+#include "combinations.hpp"
+#include "internal/iterbase.hpp"
+
+#include <cassert>
+#include <initializer_list>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename Container>
+    class Powersetter;
+
+    using PowersetFn = IterToolFn<Powersetter>;
+  }
+  constexpr impl::PowersetFn powerset{};
+}
+
+template <typename Container>
+class iter::impl::Powersetter {
+ private:
+  Container container_;
+  template <typename T>
+  using CombinatorType = decltype(combinations(std::declval<T&>(), 0));
+
+  friend PowersetFn;
+
+  Powersetter(Container&& container)
+      : container_(std::forward<Container>(container)) {}
+
+ public:
+  Powersetter(Powersetter&&) = default;
+
+  template <typename ContainerT>
+  class Iterator {
+   private:
+#if 0
+    template <typename> friend class Iterator;
+#endif
+    std::remove_reference_t<ContainerT>* container_p_;
+    std::size_t set_size_{};
+    std::shared_ptr<CombinatorType<ContainerT>> comb_;
+    iterator_type<CombinatorType<ContainerT>> comb_iter_;
+    iterator_type<CombinatorType<ContainerT>> comb_end_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = CombinatorType<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(ContainerT& container, std::size_t sz)
+        : container_p_{&container},
+          set_size_{sz},
+          comb_{std::make_shared<CombinatorType<ContainerT>>(
+              combinations(container, sz))},
+          comb_iter_{get_begin(*comb_)},
+          comb_end_{get_end(*comb_)} {}
+
+    Iterator& operator++() {
+      ++comb_iter_;
+      if (comb_iter_ == comb_end_) {
+        ++set_size_;
+        comb_ = std::make_shared<CombinatorType<ContainerT>>(
+            combinations(*container_p_, set_size_));
+
+        comb_iter_ = get_begin(*comb_);
+        comb_end_ = get_end(*comb_);
+      }
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    iterator_deref<CombinatorType<ContainerT>> operator*() {
+      return *comb_iter_;
+    }
+
+    iterator_arrow<CombinatorType<ContainerT>> operator->() {
+      apply_arrow(comb_iter_);
+    }
+
+    bool operator!=(const Iterator& other) const {
+      return !(*this == other);
+    }
+
+    bool operator==(const Iterator& other) const {
+      return set_size_ == other.set_size_ && comb_iter_ == other.comb_iter_;
+    }
+#if 0
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return !(*this == other);
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return set_size_ == other.set_size_ && comb_iter_ == other.comb_iter_;
+    }
+#endif
+  };
+
+  Iterator<Container> begin() {
+    return {container_, 0};
+  }
+
+  Iterator<Container> end() {
+    return {container_, dumb_size(container_) + 1};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {impl::as_const(container_), 0};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {
+        impl::as_const(container_), dumb_size(impl::as_const(container_)) + 1};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/product.hpp b/src/external/cppitertools-1.0/product.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..af3deea64f051b626da3112b6cbeec42e909e91b
--- /dev/null
+++ b/src/external/cppitertools-1.0/product.hpp
@@ -0,0 +1,243 @@
+#ifndef ITER_PRODUCT_HPP_
+#define ITER_PRODUCT_HPP_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <array>
+#include <iterator>
+#include <tuple>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename... Containers>
+    class Productor;
+
+    template <typename Container, typename... RestContainers>
+    class Productor<Container, RestContainers...>;
+
+    template <>
+    class Productor<>;
+  }
+
+  template <typename... Containers>
+  impl::Productor<Containers...> product(Containers&&...);
+}
+
+// specialization for at least 1 template argument
+template <typename Container, typename... RestContainers>
+class iter::impl::Productor<Container, RestContainers...> {
+  friend Productor iter::product<Container, RestContainers...>(
+      Container&&, RestContainers&&...);
+
+  template <typename... RC>
+  friend class Productor;
+
+  template <typename T>
+  using ProdIterDeref =
+      std::tuple<iterator_deref<T>, iterator_deref<RestContainers>...>;
+
+ private:
+  Container container_;
+  Productor<RestContainers...> rest_products_;
+  Productor(Container&& container, RestContainers&&... rest)
+      : container_(std::forward<Container>(container)),
+        rest_products_{std::forward<RestContainers>(rest)...} {}
+
+ public:
+  Productor(Productor&&) = default;
+
+ private:
+  template <typename ContainerT, typename RestIter>
+  class IteratorTempl {
+   private:
+    template <typename, typename>
+    friend class IteratorTempl;
+    IteratorWrapper<ContainerT> sub_iter_;
+    IteratorWrapper<ContainerT> sub_begin_;
+
+    RestIter rest_iter_;
+    RestIter rest_end_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = ProdIterDeref<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    constexpr static const bool is_base_iter = false;
+    IteratorTempl(IteratorWrapper<ContainerT>&& sub_iter, RestIter&& rest_iter,
+        RestIter&& rest_end)
+        : sub_iter_{sub_iter},
+          sub_begin_{sub_iter},
+          rest_iter_{rest_iter},
+          rest_end_{rest_end} {}
+
+    void reset() {
+      sub_iter_ = sub_begin_;
+    }
+
+    IteratorTempl& operator++() {
+      ++rest_iter_;
+      if (!(rest_iter_ != rest_end_)) {
+        rest_iter_.reset();
+        ++sub_iter_;
+      }
+      return *this;
+    }
+
+    IteratorTempl operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T, typename U>
+    bool operator!=(const IteratorTempl<T, U>& other) const {
+      return sub_iter_ != other.sub_iter_
+             && (RestIter::is_base_iter || rest_iter_ != other.rest_iter_);
+    }
+
+    template <typename T, typename U>
+    bool operator==(const IteratorTempl<T, U>& other) const {
+      return !(*this != other);
+    }
+
+    ProdIterDeref<ContainerT> operator*() {
+      return std::tuple_cat(
+          std::tuple<iterator_deref<ContainerT>>{*sub_iter_}, *rest_iter_);
+    }
+
+    ArrowProxy<ProdIterDeref<ContainerT>> operator->() {
+      return {**this};
+    }
+  };
+
+  using RestIter = typename Productor<RestContainers...>::Iterator;
+  using RestConstIter = typename Productor<RestContainers...>::ConstIterator;
+
+ public:
+  using Iterator = IteratorTempl<Container, RestIter>;
+  using ConstIterator = IteratorTempl<AsConst<Container>, RestConstIter>;
+
+  Iterator begin() {
+    return {get_begin(container_), get_begin(rest_products_),
+        get_end(rest_products_)};
+  }
+
+  Iterator end() {
+    return {
+        get_end(container_), get_end(rest_products_), get_end(rest_products_)};
+  }
+
+  ConstIterator begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_begin(impl::as_const(rest_products_)),
+        get_end(impl::as_const(rest_products_))};
+  }
+
+  ConstIterator end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(rest_products_)),
+        get_end(impl::as_const(rest_products_))};
+  }
+};
+
+template <>
+class iter::impl::Productor<> {
+ public:
+  Productor(Productor&&) = default;
+  class Iterator {
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = std::tuple<>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    constexpr static const bool is_base_iter = true;
+
+    void reset() {}
+
+    Iterator& operator++() {
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    // see note in zip about base case operator!=
+    bool operator!=(const Iterator&) const {
+      return false;
+    }
+
+    bool operator==(const Iterator& other) const {
+      return !(*this != other);
+    }
+
+    std::tuple<> operator*() const {
+      return {};
+    }
+  };
+  using ConstIterator = Iterator;
+
+  Iterator begin() {
+    return {};
+  }
+
+  Iterator end() {
+    return {};
+  }
+
+  ConstIterator begin() const {
+    return {};
+  }
+
+  ConstIterator end() const {
+    return {};
+  }
+};
+
+template <typename... Containers>
+iter::impl::Productor<Containers...> iter::product(Containers&&... containers) {
+  return {std::forward<Containers>(containers)...};
+}
+
+namespace iter {
+  namespace impl {
+    // rvalue must be copied, lvalue and const lvalue references can be bound
+    template <std::size_t... Is, typename Container>
+    decltype(auto) product_repeat(
+        std::index_sequence<Is...>, Container&& container) {
+      return product(((void)Is, Container(container))...);
+    }
+
+    template <std::size_t... Is, typename Container>
+    decltype(auto) product_repeat(
+        std::index_sequence<Is...>, Container& container) {
+      return product(((void)Is, container)...);
+    }
+
+    template <std::size_t... Is, typename Container>
+    decltype(auto) product_repeat(
+        std::index_sequence<Is...>, const Container& container) {
+      return product(((void)Is, container)...);
+    }
+  }
+  template <std::size_t N, typename Container>
+  decltype(auto) product(Container&& container) {
+    return impl::product_repeat(
+        std::make_index_sequence<N>{}, std::forward<Container>(container));
+  }
+
+  constexpr std::array<std::tuple<>, 1> product() {
+    return {{}};
+  }
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/range.hpp b/src/external/cppitertools-1.0/range.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9287e5f220c6567810e2000b994a3d523d4e9242
--- /dev/null
+++ b/src/external/cppitertools-1.0/range.hpp
@@ -0,0 +1,262 @@
+#ifndef ITER_RANGE_H_
+#define ITER_RANGE_H_
+
+#include "internal/iterbase.hpp"
+
+#include <cassert>
+#include <exception>
+#include <iterator>
+#include <type_traits>
+
+namespace iter {
+  namespace impl {
+    template <typename T>
+    class Range;
+  }
+
+  template <typename T>
+  constexpr impl::Range<T> range(T) noexcept;
+  template <typename T>
+  constexpr impl::Range<T> range(T, T) noexcept;
+  template <typename T>
+  constexpr impl::Range<T> range(T, T, T) noexcept;
+}
+
+namespace iter {
+  namespace detail {
+    template <typename T, bool IsFloat = std::is_floating_point<T>::value>
+    class RangeIterData;
+
+    // everything except floats
+    template <typename T>
+    class RangeIterData<T, false> {
+     private:
+      T value_{};
+      T step_{};
+
+     public:
+      constexpr RangeIterData() noexcept = default;
+      constexpr RangeIterData(T in_value, T in_step) noexcept
+          : value_{in_value},
+            step_{in_step} {}
+
+      constexpr T value() const noexcept {
+        return value_;
+      }
+
+      constexpr T step() const noexcept {
+        return step_;
+      }
+
+      void inc() noexcept {
+        value_ += step_;
+      }
+
+      constexpr bool operator==(const RangeIterData& other) const noexcept {
+        return value_ == other.value_;
+      }
+
+      constexpr bool operator!=(const RangeIterData& other) const noexcept {
+        return !(*this == other);
+      }
+    };
+
+    // float data
+    template <typename T>
+    class RangeIterData<T, true> {
+     private:
+      T start_{};
+      T value_{};
+      T step_{};
+      std::size_t steps_taken_{};
+
+     public:
+      constexpr RangeIterData() noexcept = default;
+      constexpr RangeIterData(T in_start, T in_step) noexcept
+          : start_{in_start},
+            value_{in_start},
+            step_{in_step} {}
+
+      constexpr T value() const noexcept {
+        return value_;
+      }
+
+      constexpr T step() const noexcept {
+        return step_;
+      }
+
+      void inc() noexcept {
+        ++steps_taken_;
+        value_ = start_ + (step_ * steps_taken_);
+      }
+
+      constexpr bool operator==(const RangeIterData& other) const noexcept {
+        // if the difference between the two values is less than the
+        // step_ size, they are considered equal
+        return (value_ < other.value_ ? other.value_ - value_
+                                      : value_ - other.value_)
+               < step_;
+      }
+
+      constexpr bool operator!=(const RangeIterData& other) const noexcept {
+        return !(*this == other);
+      }
+    };
+  }
+}
+
+template <typename T>
+class iter::impl::Range {
+  // see stackoverflow.com/questions/32174186 about why only specializations
+  // aren't marked as friend
+  template <typename U>
+  friend constexpr Range<U> iter::range(U) noexcept;
+  template <typename U>
+  friend constexpr Range<U> iter::range(U, U) noexcept;
+  template <typename U>
+  friend constexpr Range<U> iter::range(U, U, U) noexcept;
+
+ private:
+  const T start_;
+  const T stop_;
+  const T step_;
+
+  constexpr Range(T stop) noexcept : start_{0}, stop_{stop}, step_{1} {}
+
+  constexpr Range(T start, T stop, T step = 1) noexcept : start_{start},
+                                                          stop_{stop},
+                                                          step_{step} {}
+
+ public:
+  // the reference type here is T, which doesn't strictly follow all
+  // of the rules, but std::vector<bool>::iterator::reference isn't
+  // a reference type either, this isn't any worse
+
+  class Iterator {
+   private:
+    iter::detail::RangeIterData<T> data;
+    bool is_end;
+
+    // compare unsigned values
+    static bool not_equal_to_impl(const Iterator& iter,
+        const Iterator& end_iter, std::true_type) noexcept {
+      assert(!iter.is_end);
+      assert(end_iter.is_end);
+      return iter.data.value() < end_iter.data.value();
+    }
+
+    // compare signed values
+    static bool not_equal_to_impl(const Iterator& iter,
+        const Iterator& end_iter, std::false_type) noexcept {
+      assert(!iter.is_end);
+      assert(end_iter.is_end);
+      return !(iter.data.step() > 0
+                 && iter.data.value() >= end_iter.data.value())
+             && !(iter.data.step() < 0
+                    && iter.data.value() <= end_iter.data.value());
+    }
+
+    static bool not_equal_to_end(
+        const Iterator& lhs, const Iterator& rhs) noexcept {
+      if (rhs.is_end) {
+        return not_equal_to_impl(lhs, rhs, std::is_unsigned<T>{});
+      }
+      return not_equal_to_impl(rhs, lhs, std::is_unsigned<T>{});
+    }
+
+   public:
+    using iterator_category = std::forward_iterator_tag;
+    using value_type = T;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type;
+
+    constexpr Iterator() noexcept = default;
+
+    constexpr Iterator(T in_value, T in_step, bool in_is_end) noexcept
+        : data(in_value, in_step),
+          is_end{in_is_end} {}
+
+    constexpr T operator*() const noexcept {
+      return data.value();
+    }
+
+    constexpr ArrowProxy<T> operator->() const noexcept {
+      return {**this};
+    }
+
+    Iterator& operator++() noexcept {
+      data.inc();
+      return *this;
+    }
+
+    Iterator operator++(int)noexcept {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    // This operator would more accurately read as "in bounds"
+    // or "incomplete" because exact comparison with the end
+    // isn't good enough for the purposes of this Iterator.
+    // There are two odd cases that need to be handled
+    //
+    // 1) The Range is infinite, such as
+    // Range (-1, 0, -1) which would go forever down toward
+    // infinitely (theoretically).  If this occurs, the Range
+    // will instead effectively be empty
+    //
+    // 2) (stop_ - start_) % step_ != 0.  For
+    // example Range(1, 10, 2).  The iterator will never be
+    // exactly equal to the stop_ value.
+    //
+    // Another way to think about it is that the "end"
+    // iterator represents the range of values that are invalid
+    // So, if an iterator is not equal to that, it is valid
+    //
+    // Two end iterators will compare equal
+    //
+    // Two non-end iterators will compare by their stored values
+    bool operator!=(const Iterator& other) const noexcept {
+      if (is_end && other.is_end) {
+        return false;
+      }
+
+      if (!is_end && !other.is_end) {
+        return data != other.data;
+      }
+      return not_equal_to_end(*this, other);
+    }
+
+    bool operator==(const Iterator& other) const noexcept {
+      return !(*this != other);
+    }
+  };
+
+  constexpr Iterator begin() const noexcept {
+    return {start_, step_, false};
+  }
+
+  constexpr Iterator end() const noexcept {
+    return {stop_, step_, true};
+  }
+};
+
+template <typename T>
+constexpr iter::impl::Range<T> iter::range(T stop_) noexcept {
+  return {stop_};
+}
+
+template <typename T>
+constexpr iter::impl::Range<T> iter::range(T start_, T stop_) noexcept {
+  return {start_, stop_};
+}
+
+template <typename T>
+constexpr iter::impl::Range<T> iter::range(
+    T start_, T stop_, T step_) noexcept {
+  return step_ == T(0) ? impl::Range<T>{0}
+                       : impl::Range<T>{start_, stop_, step_};
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/repeat.hpp b/src/external/cppitertools-1.0/repeat.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..01739c6c3764fa1f39333b5b8335a44a63816770
--- /dev/null
+++ b/src/external/cppitertools-1.0/repeat.hpp
@@ -0,0 +1,169 @@
+#ifndef ITER_REPEAT_HPP_
+#define ITER_REPEAT_HPP_
+
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename T>
+    class RepeaterWithCount;
+  }
+
+  template <typename T>
+  constexpr impl::RepeaterWithCount<T> repeat(T&&, int);
+}
+
+template <typename T>
+class iter::impl::RepeaterWithCount {
+  // see stackoverflow.com/questions/32174186/ about why this isn't
+  // declaring just a specialization as friend
+  template <typename U>
+  friend constexpr RepeaterWithCount<U> iter::repeat(U&&, int);
+
+ private:
+  T elem_;
+  int count_;
+
+  constexpr RepeaterWithCount(T e, int c)
+      : elem_(std::forward<T>(e)), count_{c} {}
+
+  using TPlain = typename std::remove_reference<T>::type;
+
+ public:
+  RepeaterWithCount(RepeaterWithCount&&) = default;
+
+  class Iterator {
+   private:
+    const TPlain* elem_;
+    int count_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = const TPlain;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    constexpr Iterator(const TPlain* e, int c) : elem_{e}, count_{c} {}
+
+    Iterator& operator++() {
+      --this->count_;
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    constexpr bool operator!=(const Iterator& other) const {
+      return !(*this == other);
+    }
+
+    constexpr bool operator==(const Iterator& other) const {
+      return this->count_ == other.count_;
+    }
+
+    constexpr const TPlain& operator*() const {
+      return *this->elem_;
+    }
+
+    constexpr const TPlain* operator->() const {
+      return this->elem_;
+    }
+  };
+
+  constexpr Iterator begin() const {
+    return {&this->elem_, this->count_};
+  }
+
+  constexpr Iterator end() const {
+    return {&this->elem_, 0};
+  }
+};
+
+template <typename T>
+constexpr iter::impl::RepeaterWithCount<T> iter::repeat(T&& e, int count_) {
+  return {std::forward<T>(e), count_ < 0 ? 0 : count_};
+}
+
+namespace iter {
+  namespace impl {
+    template <typename T>
+    class Repeater;
+  }
+
+  template <typename T>
+  constexpr impl::Repeater<T> repeat(T&&);
+}
+
+template <typename T>
+class iter::impl::Repeater {
+  template <typename U>
+  friend constexpr Repeater<U> iter::repeat(U&&);
+
+ private:
+  using TPlain = typename std::remove_reference<T>::type;
+  T elem_;
+
+  constexpr Repeater(T e) : elem_(std::forward<T>(e)) {}
+
+ public:
+  Repeater(Repeater&&) = default;
+
+  class Iterator {
+   private:
+    const TPlain* elem_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = const TPlain;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    constexpr Iterator(const TPlain* e) : elem_{e} {}
+
+    constexpr const Iterator& operator++() const {
+      return *this;
+    }
+
+    constexpr Iterator operator++(int)const {
+      return *this;
+    }
+
+    constexpr bool operator!=(const Iterator&) const {
+      return true;
+    }
+
+    constexpr bool operator==(const Iterator&) const {
+      return false;
+    }
+
+    constexpr const TPlain& operator*() const {
+      return *this->elem_;
+    }
+
+    constexpr const TPlain* operator->() const {
+      return this->elem_;
+    }
+  };
+
+  constexpr Iterator begin() const {
+    return {&this->elem_};
+  }
+
+  constexpr Iterator end() const {
+    return {nullptr};
+  }
+};
+
+template <typename T>
+constexpr iter::impl::Repeater<T> iter::repeat(T&& e) {
+  return {std::forward<T>(e)};
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/reversed.hpp b/src/external/cppitertools-1.0/reversed.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b76ad8010469fdd13d8f11f5c6b31818c3d68dcb
--- /dev/null
+++ b/src/external/cppitertools-1.0/reversed.hpp
@@ -0,0 +1,137 @@
+#ifndef ITER_REVERSE_HPP_
+#define ITER_REVERSE_HPP_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <iterator>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename Container>
+    using reverse_iterator_type =
+        decltype(std::rbegin(std::declval<Container&>()));
+    template <typename Container>
+    using reverse_iterator_end_type =
+        decltype(std::rend(std::declval<Container&>()));
+
+    // If rbegin and rend return the same type, type will be
+    // reverse_iterator_type<Container>
+    // If rbegin and rend return different types, type will be
+    // IteratorWrapperImpl
+    template <typename Container, bool same_types>
+    struct ReverseIteratorWrapperImplType;
+
+    template <typename Container>
+    struct ReverseIteratorWrapperImplType<Container, true>
+        : type_is<reverse_iterator_type<Container>> {};
+
+    template <typename Container>
+    struct ReverseIteratorWrapperImplType<Container, false>
+        : type_is<IteratorWrapperImpl<reverse_iterator_type<Container>,
+              reverse_iterator_end_type<Container>>> {};
+
+    template <typename Container>
+    using ReverseIteratorWrapper =
+        typename ReverseIteratorWrapperImplType<Container,
+            std::is_same<impl::reverse_iterator_type<Container>,
+                                                    impl::
+                                                        reverse_iterator_end_type<Container>>{}>::
+            type;
+
+    template <typename Container>
+    class Reverser;
+
+    using ReversedFn = IterToolFn<Reverser>;
+  }
+  constexpr impl::ReversedFn reversed{};
+}
+
+template <typename Container>
+class iter::impl::Reverser {
+ private:
+  Container container_;
+  friend ReversedFn;
+
+  Reverser(Container&& container)
+      : container_(std::forward<Container>(container)) {}
+
+  template <typename T>
+  using reverse_iterator_deref =
+      decltype(*std::declval<reverse_iterator_type<T>&>());
+
+  template <typename T>
+  using reverse_iterator_traits_deref =
+      std::remove_reference_t<reverse_iterator_deref<T>>;
+
+  template <typename T>
+  using reverse_iterator_arrow = detail::arrow<reverse_iterator_type<T>>;
+
+ public:
+  Reverser(Reverser&&) = default;
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    ReverseIteratorWrapper<ContainerT> sub_iter_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = reverse_iterator_traits_deref<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(ReverseIteratorWrapper<ContainerT>&& sub_iter)
+        : sub_iter_{std::move(sub_iter)} {}
+
+    reverse_iterator_deref<ContainerT> operator*() {
+      return *sub_iter_;
+    }
+
+    reverse_iterator_arrow<ContainerT> operator->() {
+      return apply_arrow(sub_iter_);
+    }
+
+    Iterator& operator++() {
+      ++sub_iter_;
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {std::rbegin(container_)};
+  }
+
+  Iterator<Container> end() {
+    return {std::rend(container_)};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {std::rbegin(impl::as_const(container_))};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {std::rend(impl::as_const(container_))};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/slice.hpp b/src/external/cppitertools-1.0/slice.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5311494313fc591f85e4081aabc7580451b6aad4
--- /dev/null
+++ b/src/external/cppitertools-1.0/slice.hpp
@@ -0,0 +1,179 @@
+#ifndef ITER_SLICE_HPP_
+#define ITER_SLICE_HPP_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <iterator>
+#include <type_traits>
+
+namespace iter {
+  namespace impl {
+    template <typename Container, typename DifferenceType>
+    class Sliced;
+
+    struct SliceFn;
+  }
+}
+
+template <typename Container, typename DifferenceType>
+class iter::impl::Sliced {
+ private:
+  Container container_;
+  DifferenceType start_;
+  DifferenceType stop_;
+  DifferenceType step_;
+
+  friend SliceFn;
+
+  Sliced(Container&& container, DifferenceType start, DifferenceType stop,
+      DifferenceType step)
+      : container_(std::forward<Container>(container)),
+        start_{start < stop && step > 0 ? start : stop},
+        stop_{stop},
+        step_{step} {}
+
+ public:
+  Sliced(Sliced&&) = default;
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    IteratorWrapper<ContainerT> sub_iter_;
+    IteratorWrapper<ContainerT> sub_end_;
+    DifferenceType current_;
+    DifferenceType stop_;
+    DifferenceType step_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = iterator_traits_deref<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter,
+        IteratorWrapper<ContainerT>&& sub_end, DifferenceType start,
+        DifferenceType stop, DifferenceType step)
+        : sub_iter_{std::move(sub_iter)},
+          sub_end_{std::move(sub_end)},
+          current_{start},
+          stop_{stop},
+          step_{step} {}
+
+    iterator_deref<ContainerT> operator*() {
+      return *sub_iter_;
+    }
+
+    iterator_arrow<ContainerT> operator->() {
+      return apply_arrow(sub_iter_);
+    }
+
+    Iterator& operator++() {
+      dumb_advance(sub_iter_, sub_end_, step_);
+      current_ += step_;
+      if (stop_ < current_) {
+        current_ = stop_;
+      }
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_ && current_ != other.current_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<Container> begin() {
+    auto it = get_begin(container_);
+    dumb_advance(it, get_end(container_), start_);
+    return {std::move(it), get_end(container_), start_, stop_, step_};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_), stop_, stop_, step_};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    auto it = get_begin(impl::as_const(container_));
+    dumb_advance(it, get_end(impl::as_const(container_)), start_);
+    return {std::move(it), get_end(impl::as_const(container_)), start_, stop_,
+        step_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), stop_, stop_, step_};
+  }
+};
+
+struct iter::impl::SliceFn {
+ private:
+  template <typename DifferenceType>
+  class FnPartial : public Pipeable<FnPartial<DifferenceType>> {
+   public:
+    template <typename Container>
+    Sliced<Container, DifferenceType> operator()(Container&& container) const {
+      return {std::forward<Container>(container), start_, stop_, step_};
+    }
+
+   private:
+    friend SliceFn;
+    constexpr FnPartial(DifferenceType start, DifferenceType stop,
+        DifferenceType step) noexcept : start_{start},
+                                        stop_{stop},
+                                        step_{step} {}
+    DifferenceType start_;
+    DifferenceType stop_;
+    DifferenceType step_;
+  };
+
+ public:
+  template <typename Container, typename DifferenceType,
+      typename = std::enable_if_t<is_iterable<Container>>>
+  Sliced<Container, DifferenceType> operator()(Container&& container,
+      DifferenceType start, DifferenceType stop,
+      DifferenceType step = 1) const {
+    return {std::forward<Container>(container), start, stop, step};
+  }
+
+  // only given the end, assume step_ is 1 and begin is 0
+  template <typename Container, typename DifferenceType,
+      typename = std::enable_if_t<is_iterable<Container>>>
+  iter::impl::Sliced<Container, DifferenceType> operator()(
+      Container&& container, DifferenceType stop) const {
+    return {std::forward<Container>(container), 0, stop, 1};
+  }
+
+  template <typename DifferenceType,
+      typename = std::enable_if_t<!is_iterable<DifferenceType>>>
+  constexpr FnPartial<DifferenceType> operator()(DifferenceType stop) const
+      noexcept {
+    return {0, stop, 1};
+  }
+
+  template <typename DifferenceType,
+      typename = std::enable_if_t<!is_iterable<DifferenceType>>>
+  constexpr FnPartial<DifferenceType> operator()(DifferenceType start,
+      DifferenceType stop, DifferenceType step = 1) const noexcept {
+    return {start, stop, step};
+  }
+};
+
+namespace iter {
+  constexpr impl::SliceFn slice{};
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/sliding_window.hpp b/src/external/cppitertools-1.0/sliding_window.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cf8438942076dc6429d50895755245288e4749bc
--- /dev/null
+++ b/src/external/cppitertools-1.0/sliding_window.hpp
@@ -0,0 +1,124 @@
+#ifndef ITER_SLIDING_WINDOW_HPP_
+#define ITER_SLIDING_WINDOW_HPP_
+
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iteratoriterator.hpp"
+#include "internal/iterbase.hpp"
+
+#include <deque>
+#include <iterator>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename Container>
+    class WindowSlider;
+    using SlidingWindowFn = IterToolFnBindSizeTSecond<WindowSlider>;
+  }
+  constexpr impl::SlidingWindowFn sliding_window{};
+}
+
+template <typename Container>
+class iter::impl::WindowSlider {
+ private:
+  Container container_;
+  std::size_t window_size_;
+
+  friend SlidingWindowFn;
+
+  WindowSlider(Container&& container, std::size_t win_sz)
+      : container_(std::forward<Container>(container)), window_size_{win_sz} {}
+
+  template <typename T>
+  using IndexVector = std::deque<IteratorWrapper<T>>;
+  template <typename T>
+  using DerefVec = IterIterWrapper<IndexVector<T>>;
+
+ public:
+  WindowSlider(WindowSlider&&) = default;
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    IteratorWrapper<ContainerT> sub_iter_;
+    DerefVec<ContainerT> window_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = DerefVec<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter,
+        IteratorWrapper<ContainerT>&& sub_end, std::size_t window_sz)
+        : sub_iter_(std::move(sub_iter)) {
+      std::size_t i{0};
+      while (i < window_sz && sub_iter_ != sub_end) {
+        window_.get().push_back(sub_iter_);
+        ++i;
+        if (i != window_sz) {
+          ++sub_iter_;
+        }
+      }
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+
+    DerefVec<ContainerT>& operator*() {
+      return window_;
+    }
+
+    DerefVec<ContainerT>* operator->() {
+      return window_;
+    }
+
+    Iterator& operator++() {
+      ++sub_iter_;
+      window_.get().pop_front();
+      window_.get().push_back(sub_iter_);
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {
+        (window_size_ != 0 ? IteratorWrapper<Container>{get_begin(container_)}
+                           : IteratorWrapper<Container>{get_end(container_)}),
+        get_end(container_), window_size_};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_), window_size_};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {(window_size_ != 0 ? IteratorWrapper<AsConst<Container>>{get_begin(
+                                     impl::as_const(container_))}
+                               : IteratorWrapper<AsConst<Container>>{get_end(
+                                     impl::as_const(container_))}),
+        get_end(impl::as_const(container_)), window_size_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), window_size_};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/sorted.hpp b/src/external/cppitertools-1.0/sorted.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..24701488358876b0b8e9b49209813756f85031bb
--- /dev/null
+++ b/src/external/cppitertools-1.0/sorted.hpp
@@ -0,0 +1,172 @@
+#ifndef ITER_SORTED_HPP_
+#define ITER_SORTED_HPP_
+
+#include "internal/iteratoriterator.hpp"
+#include "internal/iterbase.hpp"
+
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+namespace iter {
+  namespace impl {
+    template <typename Container, typename CompareFunc>
+    class SortedView;
+    using SortedFn = IterToolFnOptionalBindSecond<SortedView, std::less<>>;
+  }
+  constexpr impl::SortedFn sorted{};
+}
+
+template <typename Container, typename CompareFunc>
+class iter::impl::SortedView {
+ private:
+  template <typename ContainerT, typename = void>
+  class SortedItersHolder {
+   public:
+    using IterIterWrap =
+        IterIterWrapper<std::vector<iterator_type<ContainerT>>>;
+    using ItIt = iterator_type<IterIterWrap>;
+    using ConstItIt = void;
+
+   private:
+    ContainerT container_;
+    IterIterWrap sorted_iters_;
+
+   public:
+    SortedItersHolder(ContainerT&& container, CompareFunc compare_func)
+        : container_(std::forward<ContainerT>(container)) {
+      // Fill the sorted_iters_ vector with an iterator to each
+      // element in the container_
+      for (auto iter = get_begin(container_); iter != get_end(container_);
+           ++iter) {
+        sorted_iters_.get().push_back(iter);
+      }
+
+      // sort by comparing the elements that the iterators point to
+      std::sort(get_begin(sorted_iters_.get()), get_end(sorted_iters_.get()),
+          [compare_func](iterator_type<Container> it1,
+                    iterator_type<Container> it2) {
+            return compare_func(*it1, *it2);
+          });
+    }
+
+    ItIt begin() {
+      return sorted_iters_.begin();
+    }
+
+    ItIt end() {
+      return sorted_iters_.end();
+    }
+  };
+
+  template <typename ContainerT>
+  class SortedItersHolder<ContainerT,
+      void_t<decltype(std::begin(std::declval<AsConst<ContainerT>&>()))>> {
+   public:
+    using IterIterWrap =
+        IterIterWrapper<std::vector<iterator_type<ContainerT>>>;
+    using ItIt = iterator_type<IterIterWrap>;
+
+    using ConstIterIterWrap =
+        IterIterWrapper<std::vector<iterator_type<AsConst<ContainerT>>>>;
+    using ConstItIt = iterator_type<ConstIterIterWrap>;
+
+   private:
+    ContainerT container_;
+    mutable CompareFunc compare_func_;
+    IterIterWrap sorted_iters_;
+    mutable ConstIterIterWrap const_sorted_iters_;
+
+    void populate_sorted_iters() const = delete;
+    void populate_sorted_iters() {
+      if (!sorted_iters_.empty()) {
+        return;
+      }
+      // Fill the sorted_iters_ vector with an iterator to each
+      // element in the container_
+      for (auto iter = get_begin(container_); iter != get_end(container_);
+           ++iter) {
+        sorted_iters_.get().push_back(iter);
+      }
+
+      // sort by comparing the elements that the iterators point to
+      std::sort(get_begin(sorted_iters_.get()), get_end(sorted_iters_.get()),
+          [this](iterator_type<ContainerT> it1, iterator_type<ContainerT> it2) {
+            return compare_func_(*it1, *it2);
+          });
+    }
+
+    void populate_const_sorted_iters() = delete;
+    void populate_const_sorted_iters() const {
+      if (!const_sorted_iters_.empty()) {
+        return;
+      }
+      for (auto iter = get_begin(impl::as_const(container_));
+           iter != get_end(impl::as_const(container_)); ++iter) {
+        const_sorted_iters_.get().push_back(iter);
+      }
+
+      // sort by comparing the elements that the iterators point to
+      std::sort(get_begin(const_sorted_iters_.get()),
+          get_end(const_sorted_iters_.get()),
+          [this](iterator_type<AsConst<ContainerT>> it1,
+                    iterator_type<AsConst<ContainerT>> it2) {
+            return compare_func_(*it1, *it2);
+          });
+    }
+
+   public:
+    SortedItersHolder(ContainerT&& container, CompareFunc compare_func)
+        : container_(std::forward<ContainerT>(container)),
+          compare_func_(std::move(compare_func)) {}
+
+    ItIt begin() {
+      populate_sorted_iters();
+      return sorted_iters_.begin();
+    }
+
+    ItIt end() {
+      populate_sorted_iters();
+      return sorted_iters_.end();
+    }
+
+    ConstItIt begin() const {
+      populate_const_sorted_iters();
+      return const_sorted_iters_.begin();
+    }
+
+    ConstItIt end() const {
+      populate_const_sorted_iters();
+      return const_sorted_iters_.end();
+    }
+  };
+
+  friend SortedFn;
+
+  SortedItersHolder<Container> sorted_iters_holder_;
+
+  SortedView(Container&& container, CompareFunc compare_func)
+      : sorted_iters_holder_{
+            std::forward<Container>(container), std::move(compare_func)} {}
+
+ public:
+  SortedView(SortedView&&) = default;
+
+  typename SortedItersHolder<Container>::ItIt begin() {
+    return sorted_iters_holder_.begin();
+  }
+
+  typename SortedItersHolder<Container>::ItIt end() {
+    return sorted_iters_holder_.end();
+  }
+
+  typename SortedItersHolder<Container>::ConstItIt begin() const {
+    return sorted_iters_holder_.begin();
+  }
+
+  typename SortedItersHolder<Container>::ConstItIt end() const {
+    return sorted_iters_holder_.end();
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/starmap.hpp b/src/external/cppitertools-1.0/starmap.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..66e2956aebfbd49ceff7f76a276c061a9d9ec44f
--- /dev/null
+++ b/src/external/cppitertools-1.0/starmap.hpp
@@ -0,0 +1,267 @@
+#ifndef ITER_STARMAP_H_
+#define ITER_STARMAP_H_
+
+#include "internal/iter_tuples.hpp"
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <array>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename Func, typename Container>
+    class StarMapper;
+
+    template <typename Func, typename TupType, std::size_t... Is>
+    class TupleStarMapper;
+
+    struct StarMapFn;
+  }
+}
+
+// NOTE I don't know why, but clang gets very confused by having  in the
+// Iterators' member functions for these classes
+
+// starmap with a container_<T> where T is one of tuple, pair, array
+template <typename Func, typename Container>
+class iter::impl::StarMapper {
+ private:
+  mutable Func func_;
+  Container container_;
+
+  using StarIterDeref = std::remove_reference_t<decltype(
+      call_with_tuple(func_, std::declval<iterator_deref<Container>>()))>;
+
+  StarMapper(Func f, Container&& c)
+      : func_(std::move(f)), container_(std::forward<Container>(c)) {}
+
+  friend StarMapFn;
+
+ public:
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    Func* func_;
+    IteratorWrapper<ContainerT> sub_iter_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = StarIterDeref;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(Func& f, IteratorWrapper<ContainerT>&& sub_iter)
+        : func_(&f), sub_iter_(std::move(sub_iter)) {}
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+
+    Iterator& operator++() {
+      ++sub_iter_;
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    decltype(auto) operator*() {
+      return call_with_tuple(*func_, *sub_iter_);
+    }
+
+    auto operator-> () -> ArrowProxy<decltype(**this)> {
+      return {**this};
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {func_, get_begin(container_)};
+  }
+
+  Iterator<Container> end() {
+    return {func_, get_end(container_)};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {func_, get_begin(impl::as_const(container_))};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {func_, get_end(impl::as_const(container_))};
+  }
+};
+
+// starmap for a tuple or pair of tuples or pairs
+template <typename Func, typename TupType, std::size_t... Is>
+class iter::impl::TupleStarMapper {
+ private:
+  mutable Func func_;
+  TupType tup_;
+
+ private:
+  static_assert(sizeof...(Is) == std::tuple_size<std::decay_t<TupType>>::value,
+      "tuple size doesn't match size of Is");
+
+  friend StarMapFn;
+
+  TupleStarMapper(Func f, TupType t)
+      : func_(std::move(f)), tup_(std::forward<TupType>(t)) {}
+
+  // this is a wrapper class to hold the aliases and functions needed for the
+  // Iterator.
+  template <typename TupTypeT>
+  class IteratorData {
+   public:
+    template <std::size_t Idx>
+    static decltype(auto) get_and_call_with_tuple(Func& f, TupTypeT& t) {
+      return call_with_tuple(f, std::get<Idx>(t));
+    }
+
+    using ResultType = decltype(get_and_call_with_tuple<0>(func_, tup_));
+    using CallerFunc = ResultType (*)(Func&, TupTypeT&);
+
+    constexpr static std::array<CallerFunc, sizeof...(Is)> callers{
+        {get_and_call_with_tuple<Is>...}};
+
+    using TraitsValue = std::remove_reference_t<ResultType>;
+
+    IteratorData() = delete;
+  };
+
+ public:
+  template <typename TupTypeT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    Func* func_;
+    std::remove_reference_t<TupTypeT>* tup_;
+    std::size_t index_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = typename IteratorData<TupTypeT>::TraitsValue;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(Func& f, TupTypeT& t, std::size_t i)
+        : func_{&f}, tup_{&t}, index_{i} {}
+
+    decltype(auto) operator*() {
+      return IteratorData<TupTypeT>::callers[index_](*func_, *tup_);
+    }
+
+    auto operator-> () {
+      return ArrowProxy<decltype(**this)>{**this};
+    }
+
+    Iterator& operator++() {
+      ++index_;
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return index_ != other.index_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<TupType> begin() {
+    return {func_, tup_, 0};
+  }
+
+  Iterator<TupType> end() {
+    return {func_, tup_, sizeof...(Is)};
+  }
+
+  Iterator<AsConst<TupType>> begin() const {
+    return {func_, impl::as_const(tup_), 0};
+  }
+
+  Iterator<AsConst<TupType>> end() const {
+    return {func_, impl::as_const(tup_), sizeof...(Is)};
+  }
+};
+
+template <typename Func, typename TupType, std::size_t... Is>
+template <typename T>
+constexpr std::array<typename iter::impl::TupleStarMapper<Func, TupType,
+                         Is...>::template IteratorData<T>::CallerFunc,
+    sizeof...(Is)>
+    iter::impl::TupleStarMapper<Func, TupType, Is...>::IteratorData<T>::callers;
+
+struct iter::impl::StarMapFn : PipeableAndBindFirst<StarMapFn> {
+ private:
+  template <typename Func, typename TupType, std::size_t... Is>
+  TupleStarMapper<Func, TupType, Is...> helper_with_tuples(
+      Func func, TupType&& tup, std::index_sequence<Is...>) const {
+    return {std::move(func), std::forward<TupType>(tup)};
+  }
+
+  // handles tuple-like types
+  template <typename Func, typename TupType>
+  auto helper(Func func, TupType&& tup, std::true_type) const {
+    return helper_with_tuples(std::move(func), std::forward<TupType>(tup),
+        std::make_index_sequence<std::tuple_size<std::decay_t<TupType>>::
+                                      value>{});
+  }
+
+  // handles everything else
+  template <typename Func, typename Container>
+  StarMapper<Func, Container> helper(
+      Func func, Container&& container, std::false_type) const {
+    return {std::move(func), std::forward<Container>(container)};
+  }
+
+  template <typename T, typename = void>
+  struct is_tuple_like : public std::false_type {};
+
+  template <typename T>
+  struct is_tuple_like<T,
+      void_t<decltype(std::tuple_size<std::decay_t<T>>::value)>>
+      : public std::true_type {};
+
+ public:
+  template <typename Func, typename Seq>
+  auto operator()(Func func, Seq&& sequence) const {
+    return helper(
+        std::move(func), std::forward<Seq>(sequence), is_tuple_like<Seq>{});
+  }
+
+  using PipeableAndBindFirst<StarMapFn>::operator();
+};
+
+namespace iter {
+  constexpr impl::StarMapFn starmap{};
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/takewhile.hpp b/src/external/cppitertools-1.0/takewhile.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..eb0fd801d44c46dd6f1eed6fd4073cb909305357
--- /dev/null
+++ b/src/external/cppitertools-1.0/takewhile.hpp
@@ -0,0 +1,128 @@
+#ifndef ITER_TAKEWHILE_H_
+#define ITER_TAKEWHILE_H_
+
+#include "filter.hpp"
+#include "internal/iterator_wrapper.hpp"
+#include "internal/iterbase.hpp"
+
+#include <iterator>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename FilterFunc, typename Container>
+    class Taker;
+
+    using TakeWhileFn = IterToolFnOptionalBindFirst<Taker, BoolTester>;
+  }
+  constexpr impl::TakeWhileFn takewhile{};
+}
+
+template <typename FilterFunc, typename Container>
+class iter::impl::Taker {
+ private:
+  Container container_;
+  mutable FilterFunc filter_func_;
+
+  friend TakeWhileFn;
+
+  Taker(FilterFunc filter_func, Container&& container)
+      : container_(std::forward<Container>(container)),
+        filter_func_(filter_func) {}
+
+ public:
+  Taker(Taker&&) = default;
+
+  template <typename ContainerT>
+  class Iterator {
+   private:
+    template <typename>
+    friend class Iterator;
+    using Holder = DerefHolder<iterator_deref<ContainerT>>;
+    IteratorWrapper<ContainerT> sub_iter_;
+    IteratorWrapper<ContainerT> sub_end_;
+    Holder item_;
+    FilterFunc* filter_func_;
+
+    void inc_sub_iter() {
+      ++sub_iter_;
+      if (sub_iter_ != sub_end_) {
+        item_.reset(*sub_iter_);
+      }
+    }
+
+    void check_current() {
+      if (sub_iter_ != sub_end_ && !(*filter_func_)(item_.get())) {
+        sub_iter_ = sub_end_;
+      }
+    }
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = iterator_traits_deref<ContainerT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorWrapper<ContainerT>&& sub_iter,
+        IteratorWrapper<ContainerT>&& sub_end, FilterFunc& filter_func)
+        : sub_iter_{std::move(sub_iter)},
+          sub_end_{std::move(sub_end)},
+          filter_func_(&filter_func) {
+      if (sub_iter_ != sub_end_) {
+        item_.reset(*sub_iter_);
+      }
+      check_current();
+    }
+
+    typename Holder::reference operator*() {
+      return item_.get();
+    }
+
+    typename Holder::pointer operator->() {
+      return item_.get_ptr();
+    }
+
+    Iterator& operator++() {
+      inc_sub_iter();
+      check_current();
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T>
+    bool operator!=(const Iterator<T>& other) const {
+      return sub_iter_ != other.sub_iter_;
+    }
+
+    template <typename T>
+    bool operator==(const Iterator<T>& other) const {
+      return !(*this != other);
+    }
+  };
+
+  Iterator<Container> begin() {
+    return {get_begin(container_), get_end(container_), filter_func_};
+  }
+
+  Iterator<Container> end() {
+    return {get_end(container_), get_end(container_), filter_func_};
+  }
+
+  Iterator<AsConst<Container>> begin() const {
+    return {get_begin(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), filter_func_};
+  }
+
+  Iterator<AsConst<Container>> end() const {
+    return {get_end(impl::as_const(container_)),
+        get_end(impl::as_const(container_)), filter_func_};
+  }
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/test/.gitignore b/src/external/cppitertools-1.0/test/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b08f9d55b9bf2a331a3f3281109bc56722fe2345
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/.gitignore
@@ -0,0 +1,8 @@
+*.o
+*.swp
+test_*
+!test_*.cpp
+.sconsign.dblite
+config.log
+.sconf_temp/
+catch.hpp
diff --git a/src/external/cppitertools-1.0/test/BUILD b/src/external/cppitertools-1.0/test/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..5c7c5d9cbf31fdcdaa10c5ee7c22cbc6999165f5
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/BUILD
@@ -0,0 +1,39 @@
+load(":make_tests.bzl", "itertools_tests")
+
+progs = [
+    "accumulate",
+    "chain",
+    "chunked",
+    "combinations",
+    "combinations_with_replacement",
+    "compress",
+    "count",
+    "cycle",
+    "dropwhile",
+    "enumerate",
+    "filter",
+    "filterfalse",
+    "groupby",
+    "imap",
+    "permutations",
+    "powerset",
+    "product",
+    "range",
+    "repeat",
+    "reversed",
+    "slice",
+    "sliding_window",
+    "starmap",
+    "sorted",
+    "takewhile",
+    "unique_everseen",
+    "unique_justseen",
+    "zip",
+    "iteratoriterator",
+    "iterator_wrapper",
+    "iterbase",
+    "mixed",
+    "helpers",
+]
+
+itertools_tests(progs)
diff --git a/src/external/cppitertools-1.0/test/SConstruct b/src/external/cppitertools-1.0/test/SConstruct
new file mode 100644
index 0000000000000000000000000000000000000000..d2198470a0ffe72dea3a3034aa336f3cd9f30739
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/SConstruct
@@ -0,0 +1,72 @@
+import os
+
+env = Environment(
+    ENV = os.environ,
+    CXXFLAGS= ['-g', '-Wall', '-Wextra',
+               '-pedantic', '-std=c++14',
+               '-I/usr/local/include', '-I.'],
+    CPPPATH='..',
+    LINKFLAGS=['-L/usr/local/lib'])
+
+# allows highighting to print to terminal from compiler output
+env['ENV']['TERM'] = os.environ['TERM']
+
+progs = Split(
+    '''
+    accumulate
+    chain
+    chunked
+    combinations
+    combinations_with_replacement
+    compress
+    count
+    cycle
+    dropwhile
+    enumerate
+    filter
+    filterfalse
+    groupby
+    imap
+    permutations
+    powerset
+    product
+    range
+    repeat
+    reversed
+    slice
+    sliding_window
+    starmap
+    sorted
+    takewhile
+    unique_everseen
+    unique_justseen
+    zip
+
+    iteratoriterator
+    iterator_wrapper
+    iterbase
+    mixed
+    helpers
+    '''
+)
+
+conf = Configure(env)
+
+# if catch isn't available, exit
+if not conf.CheckCXXHeader('catch.hpp'):
+    print("WARNING: catch.hpp not found, run ./download_catch.sh first")
+    print("note: you may receive this warning if the c++ compiler specified "
+          "by CXX  at the top of the SConstruct file is invalid.")
+    Exit(1)
+
+if conf.CheckCXXHeader('boost/optional.hpp'):
+    progs.append('zip_longest')
+
+env = conf.Finish()
+
+test_sources = ['test_{}.cpp'.format(p) for p in progs]
+
+for test_src in test_sources:
+    env.Program([test_src, 'test_main.cpp'])
+
+env.Program('test_all', ['test_main.cpp'] + test_sources)
diff --git a/src/external/cppitertools-1.0/test/download_catch.sh b/src/external/cppitertools-1.0/test/download_catch.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2988d1b660e480e7855440e416f4a83b36f4563c
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/download_catch.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env sh
+wget -c https://github.com/catchorg/Catch2/releases/download/v2.0.1/catch.hpp
diff --git a/src/external/cppitertools-1.0/test/helpers.hpp b/src/external/cppitertools-1.0/test/helpers.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e1d5a3cf5bd2228c13605e8eabaa981f7a6724fd
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/helpers.hpp
@@ -0,0 +1,357 @@
+#ifndef TEST_HELPER_H_
+#define TEST_HELPER_H_
+
+#include <internal/iterbase.hpp>
+
+#include <stdexcept>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace itertest {
+
+  // non-copyable. non-movable. non-default-constructible
+  class SolidInt {
+   private:
+    const int i;
+
+   public:
+    constexpr SolidInt(int n) : i{n} {}
+
+    constexpr int getint() const {
+      return this->i;
+    }
+
+    SolidInt() = delete;
+    SolidInt(const SolidInt&) = delete;
+    SolidInt& operator=(const SolidInt&) = delete;
+    SolidInt& operator=(SolidInt&&) = delete;
+    SolidInt(SolidInt&&) = delete;
+  };
+
+  namespace {
+    struct DoubleDereferenceError : std::exception {
+      const char* what() const noexcept override {
+        return "Iterator dereferenced twice without increment";
+      }
+    };
+
+    // this class's iterator will throw if it's dereference twice without
+    // an increment in between
+    class InputIterable {
+     public:
+      class Iterator {
+       private:
+        int i;
+        bool was_incremented = true;
+
+       public:
+        Iterator(int n) : i{n} {}
+
+        Iterator& operator++() {
+          ++this->i;
+          this->was_incremented = true;
+          return *this;
+        }
+
+        int operator*() {
+          if (!this->was_incremented) {
+            throw DoubleDereferenceError{};
+          }
+          this->was_incremented = false;
+          return this->i;
+        }
+
+        bool operator!=(const Iterator& other) const {
+          return this->i != other.i;
+        }
+      };
+
+      Iterator begin() {
+        return {0};
+      }
+
+      Iterator end() {
+        return {5};
+      }
+    };
+  }
+
+  // BasicIterable provides a minimal forward iterator
+  // operator++(), operator!=(const BasicIterable&), operator*()
+  // move constructible only
+  // not copy constructible, move assignable, or copy assignable
+  template <typename T>
+  class BasicIterable {
+   private:
+    T* data;
+    std::size_t size;
+    bool was_moved_from_ = false;
+    mutable bool was_copied_from_ = false;
+
+   public:
+    BasicIterable(std::initializer_list<T> il)
+        : data{new T[il.size()]}, size{il.size()} {
+      // would like to use enumerate, can't because it's for unit
+      // testing enumerate
+      std::size_t i = 0;
+      for (auto&& e : il) {
+        data[i] = e;
+        ++i;
+      }
+    }
+
+    BasicIterable& operator=(BasicIterable&&) = delete;
+    BasicIterable& operator=(const BasicIterable&) = delete;
+
+#ifndef DEFINE_BASIC_ITERABLE_COPY_CTOR
+    BasicIterable(const BasicIterable&) = delete;
+#else
+    BasicIterable(const BasicIterable& other)
+        : data{new T[other.size]}, size{other.size} {
+      other.was_copied_from_ = true;
+      auto o_it = begin(other);
+      for (auto it = begin(*this); o_it != end(other);
+           ++it, ++o_it) {
+        *it = *o_it;
+      }
+    }
+#endif
+
+    BasicIterable(BasicIterable&& other) : data{other.data}, size{other.size} {
+      other.data = nullptr;
+      other.was_moved_from_ = true;
+    }
+
+    bool was_moved_from() const {
+      return this->was_moved_from_;
+    }
+
+    bool was_copied_from() const {
+      return this->was_copied_from_;
+    }
+
+    ~BasicIterable() {
+      delete[] this->data;
+    }
+
+    template <typename U>
+    class Iterator {
+     private:
+      U* p;
+
+     public:
+#ifdef DEFINE_DEFAULT_ITERATOR_CTOR
+      Iterator() = default;
+#endif
+      Iterator(U* b) : p{b} {}
+      bool operator!=(const Iterator& other) const {
+        return this->p != other.p;
+      }
+
+      Iterator& operator++() {
+        ++this->p;
+        return *this;
+      }
+
+      U& operator*() {
+        return *this->p;
+      }
+    };
+
+    friend BasicIterable::Iterator<T> begin(BasicIterable& b) {
+      return {b.data};
+    }
+
+    friend BasicIterable::Iterator<T> end(BasicIterable& b) {
+      return {b.data + b.size};
+    }
+
+#ifdef DEFINE_BASIC_ITERABLE_CONST_BEGIN_AND_END
+    friend BasicIterable::Iterator<const T> begin(const BasicIterable& b) {
+      return {b.data};
+    }
+
+    friend BasicIterable::Iterator<const T> end(const BasicIterable& b) {
+      return {b.data + b.size};
+    }
+#endif
+
+#ifdef DECLARE_REVERSE_ITERATOR
+    Iterator<T> rbegin();
+    Iterator<T> rend();
+#endif  // ifdef DECLARE_REVERSE_ITERATOR
+  };
+
+  using iter::impl::void_t;
+
+  template <typename, typename = void>
+  struct IsIterator : std::false_type {};
+
+  template <typename T>
+  struct IsIterator<T,
+      void_t<decltype(T(std::declval<const T&>())),                 // copyctor
+          decltype(std::declval<T&>() = std::declval<const T&>()),  // copy =
+          decltype(*std::declval<T&>()),                            // operator*
+          decltype(std::declval<T&>().operator->()),  // operator->
+          decltype(++std::declval<T&>()),             // prefix ++
+          decltype(std::declval<T&>()++),             // postfix ++
+          decltype(
+              std::declval<const T&>() != std::declval<const T&>()),      //  !=
+          decltype(std::declval<const T&>() == std::declval<const T&>())  //  ==
+          >> : std::true_type {};
+
+  template <typename T>
+  struct IsForwardIterator
+      : std::integral_constant<bool,
+            IsIterator<T>::value && std::is_default_constructible<T>::value> {};
+
+  template <typename T>
+  struct IsMoveConstructibleOnly
+      : std::integral_constant<bool,
+            !std::is_copy_constructible<T>::value
+                && !std::is_copy_assignable<T>::value
+                && !std::is_move_assignable<T>::value
+                && std::is_move_constructible<T>::value> {};
+}
+template <typename T, typename Inc>
+class DiffEndRange {
+ private:
+  T start_;
+  T stop_;
+  std::vector<T> all_results_;
+
+ public:
+  constexpr DiffEndRange(T start, T stop) : start_{start}, stop_{stop} {
+    while (start < stop_) {
+      all_results_.push_back(start);
+      Inc{}(start);
+    }
+  }
+
+  class Iterator;
+  class EndIterator;
+
+  class Iterator {
+    using SubIter = typename std::vector<T>::iterator;
+
+   private:
+    SubIter it_;
+    SubIter end_;
+
+   public:
+#ifdef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
+    Iterator() = default;
+#endif
+    Iterator(SubIter it, SubIter end_it) : it_{it}, end_{end_it} {}
+
+    T& operator*() const {
+      return *it_;
+    }
+    T* operator->() const {
+      return &*it_;
+    }
+
+    Iterator& operator++() {
+      ++it_;
+      return *this;
+    }
+
+    bool operator!=(const Iterator& other) const {
+      return it_ != other.it_;
+    }
+
+    bool operator!=(const EndIterator&) const {
+      return it_ != end_;
+    }
+
+    friend bool operator!=(const EndIterator& lhs, const Iterator& rhs) {
+      return rhs != lhs;
+    }
+  };
+
+  class ReverseIterator {
+    using SubIter = typename std::vector<T>::reverse_iterator;
+
+   private:
+    SubIter it_;
+    SubIter end_;
+
+   public:
+#ifdef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
+    ReverseIterator() = default;
+#endif
+    ReverseIterator(SubIter it, SubIter end_it) : it_{it}, end_{end_it} {}
+
+    T& operator*() const {
+      return *it_;
+    }
+    T* operator->() const {
+      return &*it_;
+    }
+
+    Iterator& operator++() {
+      ++it_;
+      return *this;
+    }
+
+    bool operator!=(const Iterator& other) const {
+      return it_ != other.it_;
+    }
+
+    bool operator!=(const EndIterator&) const {
+      return it_ != end_;
+    }
+
+    friend bool operator!=(const EndIterator& lhs, const Iterator& rhs) {
+      return rhs != lhs;
+    }
+  };
+
+  class EndIterator {};
+  class ReverseEndIterator {};
+
+  Iterator begin() {
+    return {std::begin(all_results_), std::end(all_results_)};
+  }
+
+  EndIterator end() {
+    return {};
+  }
+
+  ReverseIterator rbegin() {
+    return {std::rbegin(all_results_), std::rend(all_results_)};
+  }
+
+  ReverseEndIterator rend() {
+    return {};
+  }
+};
+
+struct CharInc {
+  void operator()(char& c) {
+    ++c;
+  }
+};
+
+// A range from 'a' to stop, begin() and end() are different
+class CharRange : public DiffEndRange<char, CharInc> {
+ public:
+  constexpr CharRange(char stop) : DiffEndRange<char, CharInc>('a', stop) {}
+};
+
+struct IncIntCharPair {
+  void operator()(std::pair<int, char>& p) {
+    ++p.first;
+    ++p.second;
+  }
+};
+
+class IntCharPairRange
+    : public DiffEndRange<std::pair<int, char>, IncIntCharPair> {
+ public:
+  IntCharPairRange(std::pair<int, char> stop)
+      : DiffEndRange<std::pair<int, char>, IncIntCharPair>({0, 'a'}, stop) {}
+};
+
+#endif
diff --git a/src/external/cppitertools-1.0/test/make_tests.bzl b/src/external/cppitertools-1.0/test/make_tests.bzl
new file mode 100644
index 0000000000000000000000000000000000000000..809195221a2bfe551b7d16e8602a011368a293cc
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/make_tests.bzl
@@ -0,0 +1,8 @@
+def itertools_tests(progs):
+  for p in progs:
+    native.cc_test(
+        name = "test_{}".format(p),
+        srcs = ["test_{}.cpp".format(p), "test_main.cpp", "catch.hpp", "helpers.hpp"],
+        deps = ["//:cppitertools",],
+        copts = ["-I.",  "-std=c++14", "-Wall", "-Wextra", "-pedantic", "-g"],
+    )
diff --git a/src/external/cppitertools-1.0/test/test_accumulate.cpp b/src/external/cppitertools-1.0/test/test_accumulate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..98058dbdeea5f3ac734bc166ac6f60f98938ba19
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_accumulate.cpp
@@ -0,0 +1,145 @@
+#include <accumulate.hpp>
+#include "helpers.hpp"
+
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::accumulate;
+using itertest::BasicIterable;
+
+using Vec = const std::vector<int>;
+TEST_CASE("Simple sum", "[accumulate]") {
+  Vec ns{1, 2, 3, 4, 5};
+
+  std::vector<int> v;
+  SECTION("Normal call") {
+    auto a = accumulate(ns);
+    v.assign(std::begin(a), std::end(a));
+  }
+  SECTION("Pipe") {
+    auto a = ns | accumulate;
+    v.assign(std::begin(a), std::end(a));
+  }
+
+  Vec vc{1, 3, 6, 10, 15};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("accumulate: With subtraction lambda", "[accumulate]") {
+  Vec ns{5, 4, 3, 2, 1};
+  std::vector<int> v;
+
+  SECTION("Normal call") {
+    auto a = accumulate(ns, [](int a, int b) { return a - b; });
+    v.assign(std::begin(a), std::end(a));
+  }
+  SECTION("Pipe") {
+    auto a = ns | accumulate([](int a, int b) { return a - b; });
+    v.assign(std::begin(a), std::end(a));
+  }
+
+  Vec vc{5, 1, -2, -4, -5};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("accumulate: const iterators", "[accumulate][const]") {
+  std::vector<int> v;
+  SECTION("lvalue") {
+    Vec ns{1, 2, 3, 4, 5};
+    const auto a = accumulate(ns);
+    v.assign(std::begin(a), std::end(a));
+  }
+  SECTION("rvalue") {
+    const auto a = accumulate(Vec{1, 2, 3, 4, 5});
+    v.assign(std::begin(a), std::end(a));
+  }
+  SECTION("const lvalue") {
+    const Vec ns{1, 2, 3, 4, 5};
+    const auto a = accumulate(ns);
+    v.assign(std::begin(a), std::end(a));
+  }
+  Vec vc{1, 3, 6, 10, 15};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("accumulate: const iterators can be compared", "[accumulate][const]") {
+  auto e = accumulate(std::string("hello"));
+  const auto& ce = e;
+  (void)(std::begin(e) == std::end(ce));
+}
+
+struct Integer {
+  const int value;
+  constexpr Integer(int i) : value{i} {}
+  constexpr Integer operator+(Integer other) const noexcept {
+    return {this->value + other.value};
+  }
+};
+
+TEST_CASE("accumulate: intermidate type need not be default constructible",
+    "[accumulate]") {
+  std::vector<Integer> v = {{2}, {3}, {10}};
+  auto a = accumulate(v, std::plus<Integer>{});
+  auto it = std::begin(a);
+}
+
+TEST_CASE("accumulate: binds reference when it should", "[accumulate]") {
+  BasicIterable<int> bi{1, 2};
+  accumulate(bi);
+  REQUIRE_FALSE(bi.was_moved_from());
+}
+
+TEST_CASE("accumulate: moves rvalues when it should", "[accumulate]") {
+  BasicIterable<int> bi{1, 2};
+  accumulate(std::move(bi));
+  REQUIRE(bi.was_moved_from());
+}
+
+TEST_CASE("accumulate: operator==", "[accumulate]") {
+  Vec v;
+  auto a = accumulate(v);
+  REQUIRE(std::begin(a) == std::end(a));
+}
+
+TEST_CASE("accumulate: postfix ++", "[accumulate]") {
+  Vec ns{2, 3};
+  auto a = accumulate(ns);
+  auto it = std::begin(a);
+  it++;
+  REQUIRE(*it == 5);
+}
+
+TEST_CASE("accumulate: operator->", "[accumulate]") {
+  Vec ns{7, 3};
+  auto a = accumulate(ns);
+  auto it = std::begin(a);
+  const int* p = it.operator->();
+  REQUIRE(*p == 7);
+}
+
+TEST_CASE("accumulate: iterator meets requirements", "[accumulate]") {
+  Vec ns{};
+  auto a = accumulate(ns, [](int a, int b) { return a + b; });
+  auto it = std::begin(a);
+  it = std::begin(a);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(a))>::value);
+}
+
+TEST_CASE(
+    "accumulate: Works with different begin and end types", "[accumulate]") {
+  CharRange cr{'d'};
+  auto a = accumulate(cr);
+  Vec v(a.begin(), a.end());
+  Vec vc{'a', 'a' + 'b', 'a' + 'b' + 'c'};
+  REQUIRE(v == vc);
+}
+
+template <typename T>
+using ImpT = decltype(accumulate(std::declval<T>()));
+TEST_CASE("accumulate: has correct ctor and assign ops", "[accumulate]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_chain.cpp b/src/external/cppitertools-1.0/test/test_chain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..32b9f184c1363a23bc9f7a0dd6bcd74e71433393
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_chain.cpp
@@ -0,0 +1,306 @@
+#include <chain.hpp>
+#include "helpers.hpp"
+
+#include <iterator>
+#include <list>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::chain;
+using itertest::SolidInt;
+using itertest::BasicIterable;
+using Vec = const std::vector<char>;
+
+TEST_CASE("chain: three strings", "[chain]") {
+  std::string s1{"abc"};
+  std::string s2{"mno"};
+  std::string s3{"xyz"};
+  auto ch = chain(s1, s2, s3);
+
+  Vec v(std::begin(ch), std::end(ch));
+  Vec vc{'a', 'b', 'c', 'm', 'n', 'o', 'x', 'y', 'z'};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("chain: const iteration", "[chain][const]") {
+  std::string s1{"abc"};
+  /* const */ std::string s2{"mno"};
+  auto ch = chain(s1, s2, std::string{"xyz"});
+
+  Vec v(std::begin(ch), std::end(ch));
+  Vec vc{'a', 'b', 'c', 'm', 'n', 'o', 'x', 'y', 'z'};
+
+  REQUIRE(v == vc);
+}
+
+// TODO make this work
+#if 0
+TEST_CASE("chain: const iterators can be compared to non-const itertors", "[chain][const]") {
+  auto ch = chain(std::string{}, std::string{});
+  const auto& cch = ch;
+  (void)(std::begin(ch) == std::end(cch));
+}
+#endif
+
+TEST_CASE("chain: with different container types", "[chain]") {
+  std::string s1{"abc"};
+  std::list<char> li{'m', 'n', 'o'};
+  std::vector<char> vec{'x', 'y', 'z'};
+  auto ch = chain(s1, li, vec);
+
+  Vec v(std::begin(ch), std::end(ch));
+  Vec vc{'a', 'b', 'c', 'm', 'n', 'o', 'x', 'y', 'z'};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "chain: where one container has different begin and end types", "[chain]") {
+  std::string s1{"abc"};
+  std::list<char> li{'m', 'n', 'o'};
+  CharRange cr('e');
+  auto ch = chain(s1, li, cr);
+
+  Vec v(std::begin(ch), std::end(ch));
+  Vec vc{'a', 'b', 'c', 'm', 'n', 'o', 'a', 'b', 'c', 'd'};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("chain: handles empty containers", "[chain]") {
+  std::string emp;
+  std::string a{"a"};
+  std::string b{"b"};
+  std::string c{"c"};
+  Vec vc{'a', 'b', 'c'};
+
+  SECTION("Empty container at front") {
+    auto ch = chain(emp, a, b, c);
+    Vec v(std::begin(ch), std::end(ch));
+
+    REQUIRE(v == vc);
+  }
+
+  SECTION("Empty container at back") {
+    auto ch = chain(a, b, c, emp);
+    Vec v(std::begin(ch), std::end(ch));
+
+    REQUIRE(v == vc);
+  }
+
+  SECTION("Empty container in middle") {
+    auto ch = chain(a, emp, b, emp, c);
+    Vec v(std::begin(ch), std::end(ch));
+
+    REQUIRE(v == vc);
+  }
+
+  SECTION("Consecutive empty containers at front") {
+    auto ch = chain(emp, emp, a, b, c);
+    Vec v(std::begin(ch), std::end(ch));
+
+    REQUIRE(v == vc);
+  }
+
+  SECTION("Consecutive empty containers at back") {
+    auto ch = chain(a, b, c, emp, emp);
+    Vec v(std::begin(ch), std::end(ch));
+
+    REQUIRE(v == vc);
+  }
+
+  SECTION("Consecutive empty containers in middle") {
+    auto ch = chain(a, emp, emp, b, emp, emp, c);
+    Vec v(std::begin(ch), std::end(ch));
+
+    REQUIRE(v == vc);
+  }
+}
+
+TEST_CASE("chain: with only empty containers", "[chain]") {
+  std::string emp{};
+  SECTION("one empty container") {
+    auto ch = chain(emp);
+    REQUIRE_FALSE(std::begin(ch) != std::end(ch));
+  }
+
+  SECTION("two empty containers") {
+    auto ch = chain(emp, emp);
+    REQUIRE_FALSE(std::begin(ch) != std::end(ch));
+  }
+
+  SECTION("three empty containers") {
+    auto ch = chain(emp, emp, emp);
+    REQUIRE_FALSE(std::begin(ch) != std::end(ch));
+  }
+}
+
+TEST_CASE("chain: doesn't move or copy elements of iterable", "[chain]") {
+  constexpr SolidInt arr[] = {{6}, {7}, {8}};
+  for (auto&& i : chain(arr, arr)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("chain: binds reference to lvalue and moves rvalue", "[chain]") {
+  BasicIterable<char> bi{'x', 'y', 'z'};
+  BasicIterable<char> bi2{'a', 'j', 'm'};
+  SECTION("First moved, second ref'd") {
+    chain(std::move(bi), bi2);
+    REQUIRE(bi.was_moved_from());
+    REQUIRE_FALSE(bi2.was_moved_from());
+  }
+  SECTION("First ref'd, second moved") {
+    chain(bi, std::move(bi2));
+    REQUIRE_FALSE(bi.was_moved_from());
+    REQUIRE(bi2.was_moved_from());
+  }
+}
+
+TEST_CASE("chain: operator==", "[chain]") {
+  std::string emp{};
+  auto ch = chain(emp);
+  REQUIRE(std::begin(ch) == std::end(ch));
+}
+
+TEST_CASE("chain: postfix ++", "[chain]") {
+  std::string s1{"a"}, s2{"b"};
+  auto ch = chain(s1, s2);
+  auto it = std::begin(ch);
+  it++;
+  REQUIRE(*it == 'b');
+}
+
+TEST_CASE("chain: iterator meets requirements", "[chain]") {
+  Vec ns{};
+  auto c = chain(ns, ns);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename... Ts>
+using ImpT = decltype(chain(std::declval<Ts>()...));
+TEST_CASE("chain: has correct ctor and assign ops", "[chain]") {
+  using T = ImpT<std::string&, std::vector<char>, char(&)[10]>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T>::value);
+}
+
+TEST_CASE("chain.from_iterable: basic test", "[chain.from_iterable]") {
+  std::vector<std::string> sv{"abc", "xyz"};
+  std::vector<char> v;
+  SECTION("Normal call") {
+    auto ch = chain.from_iterable(sv);
+    v.assign(std::begin(ch), std::end(ch));
+  }
+  SECTION("Pipe") {
+    auto ch = sv | chain.from_iterable;
+    v.assign(std::begin(ch), std::end(ch));
+  }
+
+  std::vector<char> vc{'a', 'b', 'c', 'x', 'y', 'z'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "chain.from_iterable: const iteration", "[chain.from_iterable][const]") {
+  std::vector<std::string> sv{"abc", "xyz"};
+  const auto ch = chain.from_iterable(sv);
+  std::vector<char> v(std::begin(ch), std::end(ch));
+
+  std::vector<char> vc{'a', 'b', 'c', 'x', 'y', 'z'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "chain.from_iterable: const iterators can be compared to non-const "
+    "iterators",
+    "[chain.from_iterable][const]") {
+  std::vector<std::vector<int>> v{};
+  auto ch = chain.from_iterable(v);
+  const auto& cch = ch;
+  (void)(std::begin(ch) == std::end(cch));
+}
+
+TEST_CASE("chain.fromm_iterable: Works with different begin and end types",
+    "[chain.from_iterable]") {
+  std::vector<CharRange> crv = {{'c'}, {'d'}};
+  auto ch = chain.from_iterable(crv);
+  const std::vector<char> v(std::begin(ch), std::end(ch));
+  const std::vector<char> vc = {'a', 'b', 'a', 'b', 'c'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "chain.from_iterable: iterators cant be copy constructed "
+    "and assigned",
+    "[chain.from_iterable]") {
+  std::vector<std::string> sv{"abc", "xyz"};
+  auto ch = chain.from_iterable(sv);
+  auto it = std::begin(ch);
+
+  SECTION("Copy constructed") {
+    auto it2 = it;
+    ++it;
+    REQUIRE(it != it2);
+  }
+
+  SECTION("Copy assigned") {
+    auto it2 = std::end(ch);
+    it2 = it;
+    REQUIRE(it == it2);
+  }
+}
+
+TEST_CASE("chain.from_iterable: postfix ++", "[chain.from_iterable]") {
+  std::vector<std::string> sv{"a", "n"};
+  auto ch = chain.from_iterable(sv);
+  auto it = std::begin(ch);
+  it++;
+  REQUIRE(*it == 'n');
+}
+
+TEST_CASE("chain.from_iterable: operator->", "[chain.from_iterable]") {
+  std::vector<std::vector<std::string>> sv{{"a", "ab"}, {"abc"}};
+  auto ch = chain.from_iterable(sv);
+  auto it = std::begin(ch);
+  REQUIRE(it->size() == 1);
+}
+
+TEST_CASE("chain.from_iterable: moves rvalues and binds ref to lvalues",
+    "[chain.from_iterable]") {
+  BasicIterable<std::string> bi{"abc", "xyz"};
+  SECTION("Moves rvalue") {
+    chain.from_iterable(std::move(bi));
+    REQUIRE(bi.was_moved_from());
+  }
+  SECTION("Binds ref to lvalue") {
+    chain.from_iterable(bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+}
+
+TEST_CASE("chain.from_iterable: empty", "[chain.from_iterable]") {
+  const std::vector<std::string> v{};
+  auto ch = chain.from_iterable(v);
+  REQUIRE(std::begin(ch) == std::end(ch));
+}
+
+TEST_CASE("chain.from_iterable: iterator meets requirements",
+    "[chain.from_iterable]") {
+  const std::vector<std::string> v{};
+  auto c = chain.from_iterable(v);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T>
+using ImpT2 = decltype(chain.from_iterable(std::declval<T>()));
+TEST_CASE("chain.from_iterable: has correct ctor and assign ops",
+    "[chain.from_iterable]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT2<std::vector<std::string>>>::
+          value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT2<std::vector<std::string>&>>::
+          value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_chunked.cpp b/src/external/cppitertools-1.0/test/test_chunked.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e3165a0935a6ad33b1515f325140f470f8ae520
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_chunked.cpp
@@ -0,0 +1,111 @@
+#include <chunked.hpp>
+
+#include <array>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+#include "helpers.hpp"
+
+using iter::chunked;
+using Vec = std::vector<int>;
+using ResVec = std::vector<Vec>;
+
+TEST_CASE("chunked: basic test", "[chunked]") {
+  Vec ns = {1, 2, 3, 4, 5, 6};
+  ResVec results;
+  SECTION("Normal call") {
+    for (auto&& g : chunked(ns, 2)) {
+      results.emplace_back(std::begin(g), std::end(g));
+    }
+  }
+  SECTION("Pipe") {
+    for (auto&& g : ns | chunked(2)) {
+      results.emplace_back(std::begin(g), std::end(g));
+    }
+  }
+
+  ResVec rc = {{1, 2}, {3, 4}, {5, 6}};
+
+  REQUIRE(results == rc);
+}
+
+TEST_CASE("chunked: const chunked", "[chunked][const]") {
+  Vec ns = {1, 2, 3, 4, 5, 6};
+  ResVec results;
+  SECTION("Normal call") {
+    const auto& ch = chunked(ns, 2);
+    for (auto&& g : ch) {
+      results.emplace_back(std::begin(g), std::end(g));
+    }
+  }
+  ResVec rc = {{1, 2}, {3, 4}, {5, 6}};
+
+  REQUIRE(results == rc);
+}
+
+TEST_CASE("chunked: const iterators can be compared to non-const iterators",
+    "[chunked][const]") {
+  auto c = chunked(Vec{}, 1);
+  const auto& cc = c;
+  (void)(std::begin(c) == std::end(cc));
+}
+
+TEST_CASE("chunked: len(iterable) % groupsize != 0", "[chunked]") {
+  Vec ns = {1, 2, 3, 4, 5, 6, 7};
+  ResVec results;
+  for (auto&& g : chunked(ns, 3)) {
+    results.emplace_back(std::begin(g), std::end(g));
+  }
+
+  ResVec rc = {{1, 2, 3}, {4, 5, 6}, {7}};
+
+  REQUIRE(results == rc);
+}
+
+TEST_CASE("chunked: iterators can be compared", "[chunked]") {
+  Vec ns = {1, 2, 3, 4, 5, 6, 7};
+  auto g = chunked(ns, 3);
+  auto it = std::begin(g);
+  REQUIRE(it == std::begin(g));
+  REQUIRE_FALSE(it != std::begin(g));
+  ++it;
+  REQUIRE(it != std::begin(g));
+  REQUIRE_FALSE(it == std::begin(g));
+}
+
+TEST_CASE("chunked: size 0 is empty", "[chunked]") {
+  Vec ns{1, 2, 3};
+  auto g = chunked(ns, 0);
+  REQUIRE(std::begin(g) == std::end(g));
+}
+
+TEST_CASE("chunked: Works with different begin and end types", "[chunked]") {
+  CharRange cr{'f'};
+  std::vector<std::vector<char>> results;
+  for (auto&& g : chunked(cr, 3)) {
+    results.emplace_back(std::begin(g), std::end(g));
+  }
+  std::vector<std::vector<char>> rc = {{'a', 'b', 'c'}, {'d', 'e'}};
+  REQUIRE(results == rc);
+}
+
+TEST_CASE("chunked: empty iterable gives empty chunked", "[chunked]") {
+  Vec ns{};
+  auto g = chunked(ns, 1);
+  REQUIRE(std::begin(g) == std::end(g));
+}
+
+TEST_CASE("chunked: iterator meets requirements", "[chunked]") {
+  std::string s{};
+  auto c = chunked(s, 1);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(chunked(std::declval<T>(), 1));
+TEST_CASE("chunked: has correct ctor and assign ops", "[chunked]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_combinations.cpp b/src/external/cppitertools-1.0/test/test_combinations.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f5c153cfc0391ec5e3f720f54e8bba4b6cce96db
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_combinations.cpp
@@ -0,0 +1,146 @@
+#define DEFINE_DEFAULT_ITERATOR_CTOR
+#define CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
+#include "helpers.hpp"
+#undef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
+#undef DEFINE_DEFAULT_ITERATOR_CTOR
+
+#include <combinations.hpp>
+
+#include <iterator>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::combinations;
+using itertest::BasicIterable;
+using itertest::SolidInt;
+using CharCombSet = std::vector<std::vector<char>>;
+
+TEST_CASE("combinations: Simple combination of 4", "[combinations]") {
+  std::string s{"ABCD"};
+  CharCombSet sc;
+  SECTION("Normal call") {
+    for (auto&& v : combinations(s, 2)) {
+      sc.emplace_back(std::begin(v), std::end(v));
+    }
+  }
+  SECTION("Pipe") {
+    for (auto&& v : s | combinations(2)) {
+      sc.emplace_back(std::begin(v), std::end(v));
+    }
+  }
+
+  CharCombSet ans = {
+      {'A', 'B'}, {'A', 'C'}, {'A', 'D'}, {'B', 'C'}, {'B', 'D'}, {'C', 'D'}};
+  REQUIRE(ans == sc);
+}
+
+TEST_CASE("combinations: const iteration", "[combinations][const]") {
+  std::string s{"ABCD"};
+  CharCombSet sc;
+  const auto comb = combinations(s, 2);
+  for (auto&& v : comb) {
+    sc.emplace_back(std::begin(v), std::end(v));
+  }
+
+  CharCombSet ans = {
+      {'A', 'B'}, {'A', 'C'}, {'A', 'D'}, {'B', 'C'}, {'B', 'D'}, {'C', 'D'}};
+  REQUIRE(ans == sc);
+}
+
+TEST_CASE(
+    "combinations: const iterators can be compared to non-const iterators",
+    "[combinations][const]") {
+  std::string s{"ABC"};
+  auto c = combinations(s, 2);
+  const auto& cc = c;
+  (void)(std::begin(c) == std::end(cc));
+}
+
+TEST_CASE("combinations: Works with different begin and end types",
+    "[combinations]") {
+  CharRange cr{'e'};
+  CharCombSet sc;
+  for (auto&& v : combinations(cr, 2)) {
+    sc.emplace_back(std::begin(v), std::end(v));
+  }
+  CharCombSet ans = {
+      {'a', 'b'}, {'a', 'c'}, {'a', 'd'}, {'b', 'c'}, {'b', 'd'}, {'c', 'd'}};
+  REQUIRE(ans == sc);
+}
+
+TEST_CASE("combinations: iterators can be compared", "[combinations]") {
+  std::string s{"ABCD"};
+  auto c = combinations(s, 2);
+  auto it = std::begin(c);
+  REQUIRE(it == std::begin(c));
+  REQUIRE_FALSE(it != std::begin(c));
+  ++it;
+  REQUIRE(it != std::begin(c));
+  REQUIRE_FALSE(it == std::begin(c));
+}
+
+TEST_CASE("combinations: operator->", "[combinations]") {
+  std::string s{"ABCD"};
+  auto c = combinations(s, 2);
+  auto it = std::begin(c);
+  REQUIRE(it->size() == 2);
+}
+
+TEST_CASE("combinations: size too large gives no results", "[combinations]") {
+  std::string s{"ABCD"};
+  auto c = combinations(s, 5);
+  REQUIRE(std::begin(c) == std::end(c));
+}
+
+TEST_CASE("combinations: size 0 gives nothing", "[combinations]") {
+  std::string s{"ABCD"};
+  auto c = combinations(s, 0);
+  REQUIRE(std::begin(c) == std::end(c));
+}
+
+TEST_CASE(
+    "combinations: iterable without operator*() const", "[combinations]") {
+  BasicIterable<char> bi{'x', 'y', 'z'};
+  auto c = combinations(bi, 1);
+  auto it = std::begin(c);
+  ++it;
+  (*it)[0];
+}
+
+TEST_CASE("combinations: binds to lvalues, moves rvalues", "[combinations]") {
+  BasicIterable<char> bi{'x', 'y', 'z'};
+  SECTION("binds to lvalues") {
+    combinations(bi, 1);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+  SECTION("moves rvalues") {
+    combinations(std::move(bi), 1);
+    REQUIRE(bi.was_moved_from());
+  }
+}
+
+TEST_CASE("combinations: doesn't move or copy elements of iterable",
+    "[combinations]") {
+  constexpr SolidInt arr[] = {{6}, {7}, {8}};
+  for (auto&& i : combinations(arr, 1)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("combinations: iterator meets requirements", "[combinations]") {
+  std::string s{"abc"};
+  auto c = combinations(s, 1);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+  auto&& row = *std::begin(c);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(row))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(combinations(std::declval<T>(), 1));
+TEST_CASE("combinations: has correct ctor and assign ops", "[combinations]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_combinations_with_replacement.cpp b/src/external/cppitertools-1.0/test/test_combinations_with_replacement.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..61f3012067c47f2ae60f6696b5d4d492a2f0e8fe
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_combinations_with_replacement.cpp
@@ -0,0 +1,149 @@
+#include <combinations_with_replacement.hpp>
+
+#include <iterator>
+#include <set>
+#include <string>
+#include <vector>
+
+#define CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
+#include "helpers.hpp"
+#undef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
+#include "catch.hpp"
+
+using iter::combinations_with_replacement;
+using itertest::BasicIterable;
+using CharCombSet = std::vector<std::vector<char>>;
+
+TEST_CASE("combinations_with_replacement: Simple combination",
+    "[combinations_with_replacement]") {
+  std::string s{"ABC"};
+  CharCombSet sc;
+  SECTION("Normal call") {
+    for (auto v : combinations_with_replacement(s, 2)) {
+      sc.emplace_back(std::begin(v), std::end(v));
+    }
+  }
+  SECTION("Pipe") {
+    for (auto v : s | combinations_with_replacement(2)) {
+      sc.emplace_back(std::begin(v), std::end(v));
+    }
+  }
+  CharCombSet ans = {
+      {'A', 'A'}, {'A', 'B'}, {'A', 'C'}, {'B', 'B'}, {'B', 'C'}, {'C', 'C'}};
+  REQUIRE(ans == sc);
+}
+
+TEST_CASE("combinations_with_replacement: const iteration",
+    "[combinations_with_replacement]") {
+  std::string s{"ABC"};
+  CharCombSet sc;
+  const auto cwr = combinations_with_replacement(s, 2);
+  for (auto v : cwr) {
+    sc.emplace_back(std::begin(v), std::end(v));
+  }
+  CharCombSet ans = {
+      {'A', 'A'}, {'A', 'B'}, {'A', 'C'}, {'B', 'B'}, {'B', 'C'}, {'C', 'C'}};
+  REQUIRE(ans == sc);
+}
+
+TEST_CASE(
+    "combinations_with_replacement: const iterators can be compared to "
+    "non-const iterators",
+    "[combinations_with_replacement][const]") {
+  std::string s{"AB"};
+  auto cwr = combinations_with_replacement(s, 2);
+  const auto& ccwr = cwr;
+  (void)(std::begin(cwr) == std::end(ccwr));
+}
+
+TEST_CASE(
+    "combinations_with_replacement: Works with different begin and end types",
+    "[combinations_with_replacement]") {
+  CharRange cr{'d'};
+  CharCombSet sc;
+  for (auto&& v : combinations_with_replacement(cr, 2)) {
+    sc.emplace_back(std::begin(v), std::end(v));
+  }
+  CharCombSet ans = {
+      {'a', 'a'}, {'a', 'b'}, {'a', 'c'}, {'b', 'b'}, {'b', 'c'}, {'c', 'c'}};
+  REQUIRE(ans == sc);
+}
+
+TEST_CASE("combinations_with_replacement: iterators can be compared",
+    "[combinations_with_replacement]") {
+  std::string s{"ABCD"};
+  auto c = combinations_with_replacement(s, 2);
+  auto it = std::begin(c);
+  REQUIRE(it == std::begin(c));
+  REQUIRE_FALSE(it != std::begin(c));
+  ++it;
+  REQUIRE(it != std::begin(c));
+  REQUIRE_FALSE(it == std::begin(c));
+}
+
+TEST_CASE("combinations_with_replacement: big size is no problem",
+    "[combinations_with_replacement]") {
+  std::string s{"AB"};
+  CharCombSet sc;
+  for (auto v : combinations_with_replacement(s, 3)) {
+    sc.emplace_back(std::begin(v), std::end(v));
+  }
+  CharCombSet ans = {
+      {'A', 'A', 'A'}, {'A', 'A', 'B'}, {'A', 'B', 'B'}, {'B', 'B', 'B'}};
+  REQUIRE(ans == sc);
+}
+
+TEST_CASE("combinations_with_replacement: 0 size is empty",
+    "[combinations_with_replacement]") {
+  std::string s{"A"};
+  auto cwr = combinations_with_replacement(s, 0);
+  REQUIRE(std::begin(cwr) == std::end(cwr));
+}
+
+TEST_CASE("combinations_with_replacement: operator->",
+    "[combinations_with_replacement]") {
+  std::string s{"ABCD"};
+  auto c = combinations_with_replacement(s, 2);
+  auto it = std::begin(c);
+  REQUIRE(it->size() == 2);
+}
+
+TEST_CASE("combinations_with_replacement: binds to lvalues, moves rvalues",
+    "[combinations_with_replacement]") {
+  BasicIterable<char> bi{'x', 'y', 'z'};
+  SECTION("binds to lvalues") {
+    combinations_with_replacement(bi, 1);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+  SECTION("moves rvalues") {
+    combinations_with_replacement(std::move(bi), 1);
+    REQUIRE(bi.was_moved_from());
+  }
+}
+
+TEST_CASE(
+    "combinations_with_replacement: "
+    "doesn't move or copy elements of iterable",
+    "[combinations_with_replacement]") {
+  constexpr itertest::SolidInt arr[] = {{6}, {7}, {8}};
+  for (auto&& i : combinations_with_replacement(arr, 1)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("combinations_with_replacement: iterator meets requirements",
+    "[combinations_with_replacement]") {
+  std::string s{"abc"};
+  auto c = combinations_with_replacement(s, 1);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+  auto&& row = *std::begin(c);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(row))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(combinations_with_replacement(std::declval<T>(), 1));
+TEST_CASE("combinations_with_replacement: has correct ctor and assign ops",
+    "[combinations_with_replacement]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_compress.cpp b/src/external/cppitertools-1.0/test/test_compress.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1330e9c2792da04cdd97f8991bc3000ea1165767
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_compress.cpp
@@ -0,0 +1,170 @@
+#include <compress.hpp>
+#include "helpers.hpp"
+
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::compress;
+using itertest::SolidInt;
+using itertest::BasicIterable;
+using Vec = const std::vector<int>;
+
+TEST_CASE("compress: alternating", "[compress]") {
+  std::vector<int> ivec{1, 2, 3, 4, 5, 6};
+  std::vector<bool> bvec{true, false, true, false, true, false};
+  auto c = compress(ivec, bvec);
+  Vec v(std::begin(c), std::end(c));
+  Vec vc = {1, 3, 5};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("compress: const iteration ", "[compress][const]") {
+  std::vector<int> ivec{1, 2, 3, 4, 5, 6};
+  std::vector<bool> bvec{true, false, true, false, true, false};
+  const auto c = compress(ivec, bvec);
+  Vec v(std::begin(c), std::end(c));
+  Vec vc = {1, 3, 5};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("compress: const iterators can be compared to non-const iterators",
+    "[compress][const]") {
+  auto c = compress(std::vector<int>{}, std::vector<bool>{});
+  const auto& cc = c;
+  (void)(std::begin(c) == std::end(cc));
+}
+
+TEST_CASE("compress: consecutive falses", "[compress]") {
+  std::vector<int> ivec{1, 2, 3, 4, 5};
+  std::vector<bool> bvec{true, false, false, false, true};
+  auto c = compress(ivec, bvec);
+  Vec v(std::begin(c), std::end(c));
+  Vec vc = {1, 5};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("compress: consecutive trues", "[compress]") {
+  std::vector<int> ivec{1, 2, 3, 4, 5};
+  std::vector<bool> bvec{false, true, true, true, false};
+  auto c = compress(ivec, bvec);
+  Vec v(std::begin(c), std::end(c));
+  Vec vc = {2, 3, 4};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("compress: all true", "[compress]") {
+  std::vector<int> ivec{1, 2, 3, 4, 5};
+  std::vector<bool> bvec(ivec.size(), true);
+  auto c = compress(ivec, bvec);
+  Vec v(std::begin(c), std::end(c));
+
+  REQUIRE(v == ivec);
+}
+
+TEST_CASE("compress: all false", "[compress]") {
+  std::vector<int> ivec{1, 2, 3, 4, 5};
+  std::vector<bool> bvec(ivec.size(), false);
+  auto c = compress(ivec, bvec);
+  REQUIRE(std::begin(c) == std::end(c));
+}
+
+TEST_CASE("compress: operator->", "[compress") {
+  std::vector<std::string> svec = {"a", "abc", "abcde"};
+  std::vector<bool> bvec = {false, false, true};
+  auto c = compress(svec, bvec);
+  auto it = std::begin(c);
+  REQUIRE(it->size() == 5);
+}
+
+TEST_CASE("compress: binds to lvalues, moves rvalues", "[compress]") {
+  BasicIterable<char> bi{'x', 'y', 'z'};
+  std::vector<bool> bl{true, true, true};
+  SECTION("binds to lvalues") {
+    compress(bi, bl);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+  SECTION("moves rvalues") {
+    compress(std::move(bi), bl);
+    REQUIRE(bi.was_moved_from());
+  }
+}
+
+struct BoolLike {
+ public:
+  bool state;
+  explicit operator bool() const {
+    return this->state;
+  }
+};
+
+TEST_CASE("compress: workds with truthy and falsey values", "[compress]") {
+  std::vector<BoolLike> bvec{{true}, {false}, {true}, {false}};
+
+  Vec ivec{1, 2, 3, 4};
+
+  auto c = compress(ivec, bvec);
+  Vec v(std::begin(c), std::end(c));
+
+  Vec vc = {1, 3};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("compress: terminates on shorter selectors", "[compress]") {
+  std::vector<int> ivec{1, 2, 3, 4, 5};
+  std::vector<bool> bvec{true};
+  auto c = compress(ivec, bvec);
+  REQUIRE(std::distance(std::begin(c), std::end(c)) == 1);
+}
+
+TEST_CASE("compress: terminates on shorter data", "[compress]") {
+  std::vector<int> ivec{1};
+  std::vector<bool> bvec{true, true, true, true, true};
+  auto c = compress(ivec, bvec);
+  REQUIRE(std::distance(std::begin(c), std::end(c)) == 1);
+}
+
+TEST_CASE("compress: nothing on empty selectors", "[compress]") {
+  std::vector<int> ivec{1, 2, 3};
+  std::vector<bool> bvec{};
+  auto c = compress(ivec, bvec);
+  REQUIRE(std::begin(c) == std::end(c));
+}
+
+TEST_CASE("compress: nothing on empty data", "[compress]") {
+  std::vector<int> ivec{};
+  std::vector<bool> bvec{true, true, true};
+  auto c = compress(ivec, bvec);
+  REQUIRE(std::begin(c) == std::end(c));
+}
+
+TEST_CASE("compress: iterator meets requirements", "[compress]") {
+  std::string s{};
+  std::vector<bool> bv;
+  auto c = compress(s, bv);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+TEST_CASE("compress: Works with different begin and end types", "[compress]") {
+  CharRange cr{'d'};
+  auto c = compress(cr, std::vector<bool>{true, false, true});
+  Vec v(c.begin(), c.end());
+  Vec vc{'a', 'c'};
+  REQUIRE(v == vc);
+}
+
+template <typename T, typename U>
+using ImpT = decltype(compress(std::declval<T>(), std::declval<U>()));
+TEST_CASE("compress: has correct ctor and assign ops", "[compress]") {
+  using T1 = ImpT<std::string&, std::vector<bool>&>;
+  using T2 = ImpT<std::string, std::vector<bool>>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T1>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<T2>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_count.cpp b/src/external/cppitertools-1.0/test/test_count.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f0f0de68dd93d24baf6701b2350dfe431b2986f2
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_count.cpp
@@ -0,0 +1,83 @@
+#include <count.hpp>
+#include "helpers.hpp"
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::count;
+
+TEST_CASE("count: watch for 10 elements", "[count]") {
+  std::vector<int> v{};
+  for (auto i : count()) {
+    v.push_back(i);
+    if (i == 9) break;
+  }
+
+  const std::vector<int> vc{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("count: const watch for 10 elements", "[count][const]") {
+  std::vector<int> v{};
+  const auto c = count();
+  for (auto i : c) {
+    v.push_back(i);
+    if (i == 9) break;
+  }
+
+  const std::vector<int> vc{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("count: start at 10", "[count]") {
+  std::vector<int> v{};
+  for (auto i : count(10)) {
+    v.push_back(i);
+    if (i == 14) break;
+  }
+
+  const std::vector<int> vc{10, 11, 12, 13, 14};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("count: with step", "[count]") {
+  std::vector<int> v{};
+  for (auto i : count(2, -1)) {
+    v.push_back(i);
+    if (i == -3) break;
+  }
+
+  const std::vector<int> vc{2, 1, 0, -1, -2, -3};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("count: with step > 1", "[count]") {
+  std::vector<int> v{};
+  for (auto i : count(10, 2)) {
+    v.push_back(i);
+    if (i == 16) break;
+  }
+
+  const std::vector<int> vc{10, 12, 14, 16};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("count: can bo constexpr", "[count]") {
+  constexpr auto c = count();
+  constexpr auto c2 = count(5);
+  (void)c2;
+  constexpr auto c3 = count(5, 2);
+  (void)c3;
+
+  constexpr auto it = c.begin();
+  constexpr auto i = *it;
+  static_assert(i == 0, "count begin not correct value");
+}
+
+TEST_CASE("count: iterator meets requirements", "[count]") {
+  auto c = count();
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_cycle.cpp b/src/external/cppitertools-1.0/test/test_cycle.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..849818785ed60074f57b8ad4ab6323fbb0143f32
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_cycle.cpp
@@ -0,0 +1,132 @@
+#include <cycle.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::cycle;
+
+TEST_CASE("cycle: iterate twice", "[cycle]") {
+  std::vector<int> ns{2, 4, 6};
+  std::vector<int> v{};
+  std::size_t count = 0;
+  for (auto i : cycle(ns)) {
+    v.push_back(i);
+    ++count;
+    if (count == ns.size() * 2) {
+      break;
+    }
+  }
+
+  auto vc = ns;
+  vc.insert(std::end(vc), std::begin(ns), std::end(ns));
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("cycle: const iteration, iterate twice", "[cycle][const]") {
+  std::vector<int> ns{2, 4, 6};
+  std::vector<int> v{};
+  std::size_t count = 0;
+  const auto c = cycle(ns);
+  for (auto i : c) {
+    v.push_back(i);
+    ++count;
+    if (count == ns.size() * 2) {
+      break;
+    }
+  }
+
+  auto vc = ns;
+  vc.insert(std::end(vc), std::begin(ns), std::end(ns));
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("cycle: const iterators can be compared to non-const iterators",
+    "[cycle][const]") {
+  auto c = cycle(std::vector<int>{});
+  const auto& cc = c;
+  (void)(std::begin(c) == std::end(cc));
+}
+
+TEST_CASE("cycle: Works with different begin and end types", "[cycle]") {
+  constexpr auto sz = 'd' - 'a';
+  CharRange cr{'d'};
+  const std::vector<char> vc{'a', 'b', 'c', 'a', 'b', 'c'};
+  std::vector<char> v;
+  std::size_t count = 0;
+  for (auto i : cycle(cr)) {
+    v.push_back(i);
+    ++count;
+    if (count == sz * 2) {
+      break;
+    }
+  }
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("cycle: with pipe", "[cycle]") {
+  std::vector<int> ns{2, 4, 6};
+  std::vector<int> v;
+  std::size_t count = 0;
+  for (auto i : ns | cycle) {
+    v.push_back(i);
+    ++count;
+    if (count == ns.size() * 2) {
+      break;
+    }
+  }
+
+  auto vc = ns;
+  vc.insert(std::end(vc), std::begin(ns), std::end(ns));
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("cycle: empty cycle terminates", "[cycle]") {
+  std::vector<int> ns;
+  auto c = cycle(ns);
+  std::vector<int> v(std::begin(c), std::end(c));
+  REQUIRE(v.empty());
+}
+
+TEST_CASE("cycle: binds to lvalues, moves rvalues", "[cycle]") {
+  itertest::BasicIterable<char> bi{'x', 'y', 'z'};
+  SECTION("binds to lvalues") {
+    cycle(bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+  SECTION("moves rvalues") {
+    cycle(std::move(bi));
+    REQUIRE(bi.was_moved_from());
+  }
+}
+
+TEST_CASE("cycle: doesn't move or copy elements of iterable", "[cycle]") {
+  constexpr itertest::SolidInt arr[] = {{6}, {7}, {8}};
+  auto c = cycle(arr);
+  *std::begin(c);
+}
+
+TEST_CASE("cycle: iterator meets requirements", "[cycle]") {
+  std::string s{};
+  auto c = cycle(s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+TEST_CASE("cycle: arrow works", "[cycle]") {
+  std::vector<std::string> v = {"hello"};
+  auto c = cycle(v);
+  auto it = std::begin(c);
+  REQUIRE(it->size() == 5);
+}
+
+template <typename T>
+using ImpT = decltype(cycle(std::declval<T>()));
+TEST_CASE("cycle: has correct ctor and assign ops", "[cycle]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_dropwhile.cpp b/src/external/cppitertools-1.0/test/test_dropwhile.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1b4edcb504c72aa3a457e4283fde6ab220268800
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_dropwhile.cpp
@@ -0,0 +1,161 @@
+#include <dropwhile.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::dropwhile;
+
+using Vec = const std::vector<int>;
+
+namespace {
+  class LessThanValue {
+   private:
+    int compare_val;
+
+   public:
+    LessThanValue(int v) : compare_val(v) {}
+
+    bool operator()(int i) {
+      return i < this->compare_val;
+    }
+  };
+}
+
+TEST_CASE("dropwhile: skips initial elements", "[dropwhile]") {
+  Vec ns{1, 2, 3, 4, 5, 6, 7, 8};
+  std::vector<int> v;
+  SECTION("Normal call") {
+    auto d = dropwhile([](int i) { return i < 5; }, ns);
+    v.assign(std::begin(d), std::end(d));
+  }
+  SECTION("Pipe") {
+    auto d = ns | dropwhile([](int i) { return i < 5; });
+    v.assign(std::begin(d), std::end(d));
+  }
+  Vec vc = {5, 6, 7, 8};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("dropwhile: const iteration", "[dropwhile][const]") {
+  Vec ns{1, 2, 3, 4, 5, 6, 7, 8};
+  const auto d = dropwhile(LessThanValue{5}, ns);
+  Vec v(std::begin(d), std::end(d));
+  Vec vc = {5, 6, 7, 8};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("dropwhile: const iterators can be compared to non-const iterators",
+    "[dropwhile][const]") {
+  auto d = dropwhile(LessThanValue{5}, Vec{});
+  const auto& cd = d;
+  (void)(std::begin(d) == std::end(cd));
+}
+
+TEST_CASE(
+    "dropwhile: Works with different begin and end types", "[dropwhile]") {
+  CharRange cr{'f'};
+  auto d = dropwhile([](char c) { return c < 'c'; }, cr);
+  Vec v(d.begin(), d.end());
+  Vec vc{'c', 'd', 'e'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("dropwhile: doesn't skip anything if it shouldn't", "[dropwhile]") {
+  Vec ns{3, 4, 5, 6};
+  auto d = dropwhile([](int i) { return i < 3; }, ns);
+  Vec v(std::begin(d), std::end(d));
+  Vec vc = {3, 4, 5, 6};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("dropwhile: skips all elements when all are true under predicate",
+    "[dropwhile]") {
+  Vec ns{3, 4, 5, 6};
+  auto d = dropwhile([](int i) { return i != 0; }, ns);
+  REQUIRE(std::begin(d) == std::end(d));
+}
+
+TEST_CASE("dropwhile: identity", "[dropwhile]") {
+  Vec ns{1, 2, 0, 3, 1, 0};
+  auto d = dropwhile(ns);
+  Vec v(std::begin(d), std::end(d));
+  Vec vc = {0, 3, 1, 0};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("dropwhile: empty case is empty", "[dropwhile]") {
+  Vec ns{};
+  auto d = dropwhile([](int i) { return i != 0; }, ns);
+  REQUIRE(std::begin(d) == std::end(d));
+}
+
+TEST_CASE("dropwhile: only drops from beginning", "[dropwhile]") {
+  Vec ns{1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+  auto d = dropwhile([](int i) { return i < 5; }, ns);
+  Vec v(std::begin(d), std::end(d));
+  Vec vc = {5, 6, 5, 4, 3, 2, 1};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("dropwhile: operator->", "[dropwhile]") {
+  std::vector<std::string> vs = {"a", "ab", "abcdef", "abcdefghi"};
+  auto d = dropwhile([](const std::string& str) { return str.size() < 3; }, vs);
+  auto it = std::begin(d);
+  REQUIRE(it->size() == 6);
+}
+
+namespace {
+  int less_than_five(int i) {
+    return i < 5;
+  }
+}
+
+TEST_CASE("dropwhile: works with function pointer", "[dropwhile]") {
+  Vec ns{1, 2, 3, 4, 5, 6, 7, 8};
+  auto d = dropwhile(less_than_five, ns);
+  Vec v(std::begin(d), std::end(d));
+  Vec vc = {5, 6, 7, 8};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("dropwhile: binds to lvalues, moves rvalues", "[dropwhile]") {
+  itertest::BasicIterable<int> bi{1, 2, 3, 4};
+  SECTION("binds to lvalues") {
+    dropwhile(less_than_five, bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+  SECTION("moves rvalues") {
+    dropwhile(less_than_five, std::move(bi));
+    REQUIRE(bi.was_moved_from());
+  }
+}
+
+TEST_CASE(
+    "dropwhile: doesn't move or copy elements of iterable", "[dropwhile]") {
+  constexpr itertest::SolidInt arr[] = {{6}, {7}, {8}};
+  for (auto&& i :
+      dropwhile([](const itertest::SolidInt&) { return false; }, arr)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("dropwhile: iterator meets requirements", "[dropwhile]") {
+  std::string s{};
+  auto c = dropwhile([] { return true; }, s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T, typename U>
+using ImpT = decltype(dropwhile(std::declval<T>(), std::declval<U>()));
+TEST_CASE("dropwhile: has correct ctor and assign ops", "[dropwhile]") {
+  using T1 = ImpT<bool (*)(char c), std::string&>;
+  auto lam = [](char) { return false; };
+  using T2 = ImpT<decltype(lam), std::string>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T1>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<T2>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_enumerate.cpp b/src/external/cppitertools-1.0/test/test_enumerate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d0b12a7aab899d0bb47bca6d3e30849303c36f4
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_enumerate.cpp
@@ -0,0 +1,242 @@
+#include <enumerate.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace Catch {
+  template <typename A, typename B>
+  std::string toString(const std::pair<A, B>& p) {
+    std::ostringstream oss;
+    oss << '{' << p.first << ", " << p.second << '}';
+    return oss.str();
+  }
+}
+
+#include "catch.hpp"
+
+using Vec = std::vector<std::pair<std::size_t, char>>;
+using iter::enumerate;
+
+using itertest::BasicIterable;
+using itertest::SolidInt;
+
+TEST_CASE("Basic Functioning enumerate", "[enumerate]") {
+  std::string str = "abc";
+  auto e = enumerate(str);
+  Vec v(std::begin(e), std::end(e));
+  Vec vc{{0, 'a'}, {1, 'b'}, {2, 'c'}};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("const enumerate", "[enumerate][const]") {
+  Vec v;
+  SECTION("lvalue") {
+    std::string str = "abc";
+    const auto e = enumerate(str);
+    v.assign(std::begin(e), std::end(e));
+  }
+  SECTION("rvalue") {
+    const auto e = enumerate(std::string("abc"));
+    v.assign(std::begin(e), std::end(e));
+  }
+  SECTION("const lvalue") {
+    const std::string str = "abc";
+    const auto e = enumerate(str);
+    v.assign(std::begin(e), std::end(e));
+  }
+
+  Vec vc{{0, 'a'}, {1, 'b'}, {2, 'c'}};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("enumerate: const iterators can be compared", "[enumerate][const]") {
+  auto e = enumerate(std::string("hello"));
+  const auto& ce = e;
+  (void)(std::begin(e) == std::end(ce));
+}
+
+TEST_CASE("enumerate: has .index, .element, .first, and .second") {
+  std::string s = "abc";
+  auto e = enumerate(s);
+  auto it = std::begin(e);
+  REQUIRE(it->index == it->first);
+  REQUIRE(&it->element == &it->second);
+}
+
+TEST_CASE("Empty enumerate", "[enumerate]") {
+  std::string emp{};
+  auto e = enumerate(emp);
+  REQUIRE(std::begin(e) == std::end(e));
+}
+
+TEST_CASE("Postfix ++ enumerate", "[enumerate]") {
+  std::string s{"amz"};
+  auto e = enumerate(s);
+  auto it = std::begin(e);
+  it++;
+  REQUIRE((*it).first == 1);
+}
+
+TEST_CASE("enumerate: structured bindings", "[enumerate]") {
+  std::string s{"amz"};
+  auto e = enumerate(s);
+  auto it = std::begin(e);
+  REQUIRE(std::tuple_size<std::decay_t<decltype(*it)>>{} == 2);
+}
+
+TEST_CASE("enumerate: with starting value", "[enumerate]") {
+  std::string str = "hey";
+  auto e = enumerate(str, 5u);
+  Vec v(std::begin(e), std::end(e));
+  Vec vc{{5, 'h'}, {6, 'e'}, {7, 'y'}};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("Modifications through enumerate affect container", "[enumerate]") {
+  std::vector<int> v{1, 2, 3, 4};
+  std::vector<int> vc(v.size(), -1);
+  for (auto&& p : enumerate(v)) {
+    p.second = -1;
+  }
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("enumerate with static array works", "[enumerate]") {
+  char arr[] = {'w', 'x', 'y'};
+
+  SECTION("Conversion to vector") {
+    auto e = enumerate(arr);
+    Vec v(std::begin(e), std::end(e));
+    Vec vc{{0, 'w'}, {1, 'x'}, {2, 'y'}};
+    REQUIRE(v == vc);
+  }
+
+  SECTION("Modification through enumerate") {
+    for (auto&& p : enumerate(arr)) {
+      p.second = 'z';
+    }
+    std::vector<char> v(std::begin(arr), std::end(arr));
+    decltype(v) vc(v.size(), 'z');
+    REQUIRE(v == vc);
+  }
+}
+
+TEST_CASE("binds reference when it should", "[enumerate]") {
+  BasicIterable<char> bi{'x', 'y', 'z'};
+  auto e = enumerate(bi);
+  (void)e;
+  REQUIRE_FALSE(bi.was_moved_from());
+}
+
+TEST_CASE("moves rvalues into enumerable object", "[enumerate]") {
+  BasicIterable<char> bi{'x', 'y', 'z'};
+  auto e = enumerate(std::move(bi));
+  REQUIRE(bi.was_moved_from());
+  (void)e;
+}
+
+TEST_CASE("enumerate: operator->", "[enumerate]") {
+  std::vector<int> ns = {50, 60, 70};
+  auto e = enumerate(ns);
+  auto it = std::begin(e);
+  REQUIRE(it->first == 0);
+  REQUIRE(it->second == 50);
+}
+
+TEST_CASE("enumerate: index and element", "[enumerate]") {
+  std::string s{"ace"};
+  auto e = enumerate(s);
+  auto it = std::begin(e);
+  REQUIRE((*it).index == 0);
+  REQUIRE((*it).element == 'a');
+
+  Vec v;
+  for (auto&& p : enumerate(s)) {
+    v.emplace_back(p.index, p.element);
+  }
+  Vec vc{{0, 'a'}, {1, 'c'}, {2, 'e'}};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("enumerate: index and element through arrow", "[enumerate]") {
+  std::string s{"ace"};
+  auto e = enumerate(s);
+  SECTION("One inspection") {
+    auto it = std::begin(e);
+    REQUIRE(it->index == 0);
+    REQUIRE(it->element == 'a');
+  }
+
+  SECTION("full loop") {
+    Vec v;
+    for (auto it = std::begin(e), end_it = std::end(e); it != end_it; ++it) {
+      v.emplace_back(it->index, it->element);
+    }
+    Vec vc{{0, 'a'}, {1, 'c'}, {2, 'e'}};
+    REQUIRE(v == vc);
+  }
+}
+
+TEST_CASE("Works with const iterable", "[enumerate]") {
+  const std::string s{"ace"};
+  auto e = enumerate(s);
+  Vec v(std::begin(e), std::end(e));
+  Vec vc{{0, 'a'}, {1, 'c'}, {2, 'e'}};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("Doesn't move or copy elements of iterable", "[enumerate]") {
+  constexpr SolidInt arr[] = {{6}, {7}, {8}};
+  for (auto&& i : enumerate(arr)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("enumerate: iterator meets requirements", "[enumerate]") {
+  std::string s{};
+  auto c = enumerate(s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+TEST_CASE("enumerate: works with pipe", "[enumerate]") {
+  constexpr char str[] = {'a', 'b', 'c'};
+  auto e = str | enumerate;
+  Vec v(std::begin(e), std::end(e));
+  Vec vc{{0, 'a'}, {1, 'b'}, {2, 'c'}};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("enumerate: works index and pipe", "[enumerate]") {
+  constexpr char str[] = {'a', 'b', 'c'};
+  auto e = str | enumerate(2);
+  Vec v(std::begin(e), std::end(e));
+  Vec vc{{2, 'a'}, {3, 'b'}, {4, 'c'}};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "enumerate: Works with different begin and end types", "[enumerate]") {
+  CharRange cr{'d'};
+  auto e = enumerate(cr);
+  Vec v(e.begin(), e.end());
+  Vec vc{{0, 'a'}, {1, 'b'}, {2, 'c'}};
+  REQUIRE(v == vc);
+}
+
+template <typename T>
+using ImpT = decltype(enumerate(std::declval<T>()));
+TEST_CASE("enumerate: has correct ctor and assign ops", "[enumerate]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_filter.cpp b/src/external/cppitertools-1.0/test/test_filter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..62b5f9a156658b7f454c826162a28d87fbb97f32
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_filter.cpp
@@ -0,0 +1,185 @@
+#include <filter.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::filter;
+
+using Vec = const std::vector<int>;
+
+namespace {
+  bool less_than_five(int i) {
+    return i < 5;
+  }
+
+  class LessThanValue {
+   private:
+    int compare_val;
+
+   public:
+    LessThanValue(int v) : compare_val(v) {}
+
+    bool operator()(int i) {
+      return i < this->compare_val;
+    }
+  };
+}
+
+TEST_CASE("filter: handles different callable types", "[filter]") {
+  Vec ns = {1, 2, 5, 6, 3, 1, 7, -1, 5};
+  Vec vc = {1, 2, 3, 1, -1};
+  SECTION("with function pointer") {
+    auto f = filter(less_than_five, ns);
+    Vec v(std::begin(f), std::end(f));
+    REQUIRE(v == vc);
+  }
+
+  SECTION("with callable object") {
+    auto f = filter(LessThanValue{5}, ns);
+    Vec v(std::begin(f), std::end(f));
+    REQUIRE(v == vc);
+  }
+
+  SECTION("with lambda") {
+    auto ltf = [](int i) { return i < 5; };
+    auto f = filter(ltf, ns);
+    Vec v(std::begin(f), std::end(f));
+    REQUIRE(v == vc);
+  }
+}
+
+TEST_CASE("filter: const iteration", "[filter][const]") {
+  Vec ns = {1, 2, 5, 6, 3, 1, 7, -1, 5};
+  const auto f = filter(LessThanValue{5}, ns);
+  Vec v(std::begin(f), std::end(f));
+  Vec vc = {1, 2, 3, 1, -1};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("filter: const iterator can be compared to non-const iterator",
+    "[filter][const]") {
+  auto f = filter(LessThanValue{5}, Vec{});
+  const auto& cf = f;
+  (void)(std::begin(f) == std::end(cf));
+}
+
+TEST_CASE("filter: iterator with lambda can be assigned", "[filter]") {
+  Vec ns{};
+  auto ltf = [](int i) { return i < 5; };
+  auto f = filter(ltf, ns);
+  auto it = std::begin(f);
+  it = std::begin(f);
+}
+
+TEST_CASE("filter: using identity", "[filter]") {
+  Vec ns{0, 1, 2, 0, 3, 0, 0, 0, 4, 5, 0};
+  auto f = filter(ns);
+  Vec v(std::begin(f), std::end(f));
+
+  Vec vc = {1, 2, 3, 4, 5};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("filter: skips null pointers", "[filter]") {
+  int a = 1;
+  int b = 2;
+  const std::vector<int*> ns = {0, &a, nullptr, nullptr, &b, nullptr};
+
+  auto f = filter(ns);
+  const std::vector<int*> v(std::begin(f), std::end(f));
+  const std::vector<int*> vc = {&a, &b};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("filter: binds to lvalues, moves rvales", "[filter]") {
+  itertest::BasicIterable<int> bi{1, 2, 3, 4};
+
+  SECTION("one-arg binds to lvalues") {
+    filter(bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+
+  SECTION("two-arg binds to lvalues") {
+    filter(less_than_five, bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+
+  SECTION("one-arg moves rvalues") {
+    filter(std::move(bi));
+    REQUIRE(bi.was_moved_from());
+  }
+
+  SECTION("two-arg moves rvalues") {
+    filter(less_than_five, std::move(bi));
+    REQUIRE(bi.was_moved_from());
+  }
+}
+
+TEST_CASE("filter: operator->", "[filter]") {
+  std::vector<std::string> vs = {"ab", "abc", "abcdef"};
+  auto f = filter([](const std::string& str) { return str.size() > 4; }, vs);
+  auto it = std::begin(f);
+  REQUIRE(it->size() == 6);
+}
+
+TEST_CASE("filter: all elements fail predicate", "[filter]") {
+  Vec ns{10, 20, 30, 40, 50};
+  auto f = filter(less_than_five, ns);
+
+  REQUIRE(std::begin(f) == std::end(f));
+}
+
+TEST_CASE("filter: doesn't move or copy elements of iterable", "[filter]") {
+  constexpr itertest::SolidInt arr[] = {{1}, {0}, {2}};
+  for (auto&& i :
+      filter([](const itertest::SolidInt& si) { return si.getint(); }, arr)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("filter: works with pipe", "[filter]") {
+  Vec ns = {1, 2, 5, 6, 3, 1, 7, -1, 5};
+  Vec vc = {1, 2, 3, 1, -1};
+
+  auto f = ns | filter(LessThanValue{5});
+  Vec v(std::begin(f), std::end(f));
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("filter: using identity and pipe", "[filter]") {
+  Vec ns{0, 1, 2, 0, 3, 0, 0, 0, 4, 5, 0};
+  auto f = ns | filter;
+  Vec v(std::begin(f), std::end(f));
+
+  Vec vc = {1, 2, 3, 4, 5};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("filter: Works with different begin and end types", "[filter]") {
+  CharRange cr{'d'};
+  auto f = filter([](char c) { return c != 'b'; }, cr);
+  Vec v(f.begin(), f.end());
+  Vec vc{'a', 'c'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("filter: iterator meets requirements", "[filter]") {
+  std::string s{};
+  auto c = filter([] { return true; }, s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T, typename U>
+using ImpT = decltype(filter(std::declval<T>(), std::declval<U>()));
+TEST_CASE("filter: has correct ctor and assign ops", "[filter]") {
+  using T1 = ImpT<bool (*)(char c), std::string&>;
+  auto lam = [](char) { return false; };
+  using T2 = ImpT<decltype(lam), std::string>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T1>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<T2>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_filterfalse.cpp b/src/external/cppitertools-1.0/test/test_filterfalse.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9e230ee12bd867e2cf13e1f532d1073aa7f8d14e
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_filterfalse.cpp
@@ -0,0 +1,149 @@
+#include <filterfalse.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::filterfalse;
+
+using Vec = const std::vector<int>;
+
+namespace {
+  bool less_than_five(int i) {
+    return i < 5;
+  }
+
+  class LessThanValue {
+   private:
+    int compare_val;
+
+   public:
+    LessThanValue(int v) : compare_val(v) {}
+
+    bool operator()(int i) {
+      return i < this->compare_val;
+    }
+  };
+}
+
+TEST_CASE("filterfalse: handles different callable types", "[filterfalse]") {
+  Vec ns = {1, 2, 5, 6, 3, 1, 7, -1, 5};
+  Vec vc = {5, 6, 7, 5};
+  SECTION("with function pointer") {
+    auto f = filterfalse(less_than_five, ns);
+    Vec v(std::begin(f), std::end(f));
+    REQUIRE(v == vc);
+  }
+
+  SECTION("with callable object") {
+    std::vector<int> v;
+    SECTION("Normal call") {
+      auto f = filterfalse(LessThanValue{5}, ns);
+      v.assign(std::begin(f), std::end(f));
+    }
+    SECTION("Pipe") {
+      auto f = ns | filterfalse(LessThanValue{5});
+      v.assign(std::begin(f), std::end(f));
+    }
+    REQUIRE(v == vc);
+  }
+
+  SECTION("with lambda") {
+    auto ltf = [](int i) { return i < 5; };
+    auto f = filterfalse(ltf, ns);
+    Vec v(std::begin(f), std::end(f));
+    REQUIRE(v == vc);
+  }
+}
+
+TEST_CASE("filterfalse: const iteration", "[filterfalse][const]") {
+  Vec ns = {1, 2, 5, 6, 3, 1, 7, -1, 5};
+  const auto f = filterfalse(LessThanValue{5}, ns);
+  Vec v(std::begin(f), std::end(f));
+  Vec vc = {5, 6, 7, 5};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("filterfalse: const iterator and non-const iterator can be compared",
+    "[filterfalse][const]") {
+  auto f = filterfalse(LessThanValue{5}, Vec{});
+  const auto& cf = f;
+  (void)(std::begin(f) == std::end(cf));
+}
+
+TEST_CASE(
+    "filterfalse: Works with different begin and end types", "[filterfalse]") {
+  CharRange cr{'d'};
+  auto f = filterfalse([](char c) { return c == 'b'; }, cr);
+  Vec v(f.begin(), f.end());
+  Vec vc{'a', 'c'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("filterfalse: using identity", "[filterfalse]") {
+  Vec ns{0, 1, 2, 0, 3, 0, 0, 0, 4, 5, 0};
+  std::vector<int> v;
+  SECTION("Normal call") {
+    auto f = filterfalse(ns);
+    v.assign(std::begin(f), std::end(f));
+  }
+
+  SECTION("Pipe") {
+    auto f = ns | filterfalse;
+    v.assign(std::begin(f), std::end(f));
+  }
+
+  Vec vc = {0, 0, 0, 0, 0, 0};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("filterfalse: binds to lvalues, moves rvales", "[filterfalse]") {
+  itertest::BasicIterable<int> bi{1, 2, 3, 4};
+
+  SECTION("one-arg binds to lvalues") {
+    filterfalse(bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+
+  SECTION("two-arg binds to lvalues") {
+    filterfalse(less_than_five, bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+
+  SECTION("one-arg moves rvalues") {
+    filterfalse(std::move(bi));
+    REQUIRE(bi.was_moved_from());
+  }
+
+  SECTION("two-arg moves rvalues") {
+    filterfalse(less_than_five, std::move(bi));
+    REQUIRE(bi.was_moved_from());
+  }
+}
+
+TEST_CASE("filterfalse: all elements pass predicate", "[filterfalse]") {
+  Vec ns{0, 1, 2, 3, 4};
+  auto f = filterfalse(less_than_five, ns);
+
+  REQUIRE(std::begin(f) == std::end(f));
+}
+
+TEST_CASE("filterfalse: iterator meets requirements", "[filterfalse]") {
+  std::string s{};
+  auto c = filterfalse([] { return true; }, s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T, typename U>
+using ImpT = decltype(filterfalse(std::declval<T>(), std::declval<U>()));
+TEST_CASE("filterfalse: has correct ctor and assign ops", "[filterfalse]") {
+  using T1 = ImpT<bool (*)(char c), std::string&>;
+  auto lam = [](char) { return false; };
+  using T2 = ImpT<decltype(lam), std::string>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T1>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<T2>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_groupby.cpp b/src/external/cppitertools-1.0/test/test_groupby.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d6d508faf73ca60eed85717a0683d469d247c86c
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_groupby.cpp
@@ -0,0 +1,309 @@
+#include <groupby.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::groupby;
+
+namespace {
+  int length(const std::string& s) {
+    return s.size();
+  }
+
+  struct Sizer {
+    int operator()(const std::string& s) {
+      return s.size();
+    }
+  };
+
+  const std::vector<std::string> vec = {
+      "hi", "ab", "ho", "abc", "def", "abcde", "efghi"};
+}
+
+TEST_CASE("groupby: works with lambda, callable, and function pointer") {
+  std::vector<int> keys;
+  std::vector<std::vector<std::string>> groups;
+
+  SECTION("Function pointer") {
+    SECTION("Normal call") {
+      for (auto&& gb : groupby(vec, length)) {
+        keys.push_back(gb.first);
+        groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+      }
+    }
+    SECTION("Pipe") {
+      for (auto&& gb : vec | groupby(length)) {
+        keys.push_back(gb.first);
+        groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+      }
+    }
+  }
+
+  SECTION("Callable object") {
+    for (auto&& gb : groupby(vec, Sizer{})) {
+      keys.push_back(gb.first);
+      groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+    }
+  }
+
+  SECTION("lambda function") {
+    for (auto&& gb :
+        groupby(vec, [](const std::string& s) { return s.size(); })) {
+      keys.push_back(gb.first);
+      groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+    }
+  }
+
+  const std::vector<int> kc = {2, 3, 5};
+  REQUIRE(keys == kc);
+
+  const std::vector<std::vector<std::string>> gc = {
+      {"hi", "ab", "ho"}, {"abc", "def"}, {"abcde", "efghi"},
+  };
+
+  REQUIRE(groups == gc);
+}
+
+TEST_CASE("groupby: const iteration", "[groupby][const]") {
+  std::vector<int> keys;
+  std::vector<std::vector<std::string>> groups;
+
+  SECTION("Function pointer") {
+    SECTION("lvalue") {
+      std::vector<std::string> local_vec(vec);
+      const auto g = groupby(local_vec, length);
+      for (auto&& gb : g) {
+        keys.push_back(gb.first);
+        groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+      }
+    }
+    SECTION("rvalue") {
+      const auto g = groupby(std::vector<std::string>(vec), length);
+      for (auto&& gb : g) {
+        keys.push_back(gb.first);
+        groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+      }
+    }
+  }
+
+  SECTION("Callable object") {
+    const auto g = groupby(vec, Sizer{});
+    for (auto&& gb : g) {
+      keys.push_back(gb.first);
+      groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+    }
+  }
+
+  SECTION("lambda function") {
+    const auto g = groupby(vec, [](const std::string& s) { return s.size(); });
+    for (auto&& gb : g) {
+      keys.push_back(gb.first);
+      groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+    }
+  }
+
+  const std::vector<int> kc = {2, 3, 5};
+  REQUIRE(keys == kc);
+
+  const std::vector<std::vector<std::string>> gc = {
+      {"hi", "ab", "ho"}, {"abc", "def"}, {"abcde", "efghi"},
+  };
+
+  REQUIRE(groups == gc);
+}
+
+TEST_CASE("groupby: iterators compare equal to non-const iterators",
+    "[groupby][const]") {
+  auto gb = groupby(std::vector<std::string>{"hi"}, length);
+  const auto& cgb = gb;
+
+  auto gb_it = std::begin(gb);
+  (void)(gb_it == std::end(cgb));
+
+// TODO figure out how to make GroupIterator<Container> and
+// GroupIterator<AsConst<Container>> comparable
+#if 0
+  auto group = std::begin(gb_it->second);
+  const auto& cgroup = group;
+  (void)(std::begin(group) == std::begin(cgroup));
+#endif
+}
+
+TEST_CASE("groupby: Works with different begin and end types", "[groupby]") {
+  CharRange cr{'f'};
+  std::vector<bool> keys;
+  std::vector<std::vector<char>> groups;
+  for (auto&& gb : groupby(cr, [](char c) { return c == 'c'; })) {
+    keys.push_back(gb.first);
+    groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+  }
+  const std::vector<bool> kc = {false, true, false};
+  const std::vector<std::vector<char>> gc = {{'a', 'b'}, {'c'}, {'d', 'e'}};
+  REQUIRE(keys == kc);
+  REQUIRE(groups == gc);
+}
+
+TEST_CASE("groupby: groups can be skipped completely", "[groupby]") {
+  std::vector<int> keys;
+  std::vector<std::vector<std::string>> groups;
+  for (auto&& gb : groupby(vec, &length)) {
+    if (gb.first == 3) {
+      continue;
+    }
+    keys.push_back(gb.first);
+    groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+  }
+
+  const std::vector<int> kc = {2, 5};
+  REQUIRE(keys == kc);
+
+  const std::vector<std::vector<std::string>> gc = {
+      {"hi", "ab", "ho"}, {"abcde", "efghi"},
+  };
+
+  REQUIRE(groups == gc);
+}
+
+TEST_CASE("groupby: groups can be skipped partially", "[groupby]") {
+  std::vector<int> keys;
+  std::vector<std::vector<std::string>> groups;
+  for (auto&& gb : groupby(vec, &length)) {
+    keys.push_back(gb.first);
+    if (gb.first == 3) {
+      std::vector<std::string> cut_short = {*std::begin(gb.second)};
+      groups.push_back(cut_short);
+    } else {
+      groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+    }
+  }
+
+  const std::vector<int> kc = {2, 3, 5};
+  REQUIRE(keys == kc);
+
+  const std::vector<std::vector<std::string>> gc = {
+      {"hi", "ab", "ho"}, {"abc"}, {"abcde", "efghi"},
+  };
+
+  REQUIRE(groups == gc);
+}
+
+TEST_CASE("groupby: single argument uses elements as keys", "[groupby]") {
+  std::vector<int> ivec = {5, 5, 6, 6, 19, 19, 19, 19, 69, 0, 10, 10};
+  std::vector<int> keys;
+  std::vector<std::vector<int>> groups;
+  SECTION("Normal call") {
+    for (auto&& gb : groupby(ivec)) {
+      keys.push_back(gb.first);
+      groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+    }
+  }
+  SECTION("Pipe") {
+    for (auto&& gb : ivec | groupby) {
+      keys.push_back(gb.first);
+      groups.emplace_back(std::begin(gb.second), std::end(gb.second));
+    }
+  }
+
+  const std::vector<int> kc = {5, 6, 19, 69, 0, 10};
+  REQUIRE(keys == kc);
+
+  std::vector<std::vector<int>> gc = {
+      {5, 5}, {6, 6}, {19, 19, 19, 19}, {69}, {0}, {10, 10},
+  };
+
+  REQUIRE(groups == gc);
+}
+
+TEST_CASE("groupby: empty iterable yields nothing", "[groupby]") {
+  std::vector<int> ivec{};
+  auto g = groupby(ivec);
+  REQUIRE(std::begin(g) == std::end(g));
+}
+
+TEST_CASE("groupby: inner iterator (group) not used", "[groupby]") {
+  std::vector<int> keys;
+  for (auto&& gb : groupby(vec, length)) {
+    keys.push_back(gb.first);
+  }
+
+  std::vector<int> kc = {2, 3, 5};
+  REQUIRE(keys == kc);
+}
+
+TEST_CASE("groupby: doesn't double dereference", "[groupby]") {
+  itertest::InputIterable seq;
+  for (auto&& kg : groupby(seq, [](int i) { return i < 3; })) {
+    for (auto&& e : kg.second) {
+      (void)e;
+    }
+  }
+}
+
+TEST_CASE("grouby: iterator doesn't need to be dereferenced before advanced",
+    "[groupby]") {
+  std::vector<int> ns = {2, 4, 7};
+  auto g = groupby(ns);
+  auto it = std::begin(g);
+  ++it;
+  REQUIRE((*it).first == 4);
+}
+
+TEST_CASE("groupby: iterator can be dereferenced multiple times", "[groupby]") {
+  std::vector<int> ns = {2, 4, 7};
+  auto g = groupby(ns);
+  auto it = std::begin(g);
+  auto k1 = (*it).first;
+  auto k2 = (*it).first;
+  REQUIRE(k1 == k2);
+}
+
+TEST_CASE(
+    "groupby: copy constructed iterators behave as expected", "[groupby]") {
+  std::vector<int> ns = {2, 3, 4, 5};
+  auto g = groupby(ns);
+  auto it = std::begin(g);
+  REQUIRE(it->first == 2);
+  {
+    auto it2 = it;
+    REQUIRE(it2->first == 2);
+    ++it;
+    REQUIRE(it->first == 3);
+    REQUIRE(*std::begin(it->second) == 3);
+  }
+  REQUIRE(it->first == 3);
+  REQUIRE(*std::begin(it->second) == 3);
+}
+
+TEST_CASE("groupby: operator-> on both iterator types", "[groupby]") {
+  std::vector<std::string> ns = {"a", "abc"};
+  auto g = groupby(ns, [](const std::string& str) { return str.size(); });
+  auto it = std::begin(g);
+  REQUIRE(it->first == 1);
+  auto it2 = std::begin(it->second);
+  REQUIRE(it2->size() == 1);
+}
+
+TEST_CASE("groupby: iterator and groupiterator are correct", "[groupby]") {
+  std::string s{"abc"};
+  auto c = groupby(s);
+  auto it = std::begin(c);
+  REQUIRE(itertest::IsIterator<decltype(it)>::value);
+  auto&& gp = (*it).second;
+  auto it2 = std::begin(gp);
+  REQUIRE(itertest::IsIterator<decltype(it2)>::value);
+}
+
+template <typename T, typename U>
+using ImpT = decltype(groupby(std::declval<T>(), std::declval<U>()));
+TEST_CASE("groupby: has correct ctor and assign ops", "[groupby]") {
+  using T1 = ImpT<std::string&, bool (*)(char c)>;
+  auto lam = [](char) { return false; };
+  using T2 = ImpT<std::string, decltype(lam)>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T1>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<T2>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_helpers.cpp b/src/external/cppitertools-1.0/test/test_helpers.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f0e36990f0a13c925f40fe43bfc859a3d573b6c7
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_helpers.cpp
@@ -0,0 +1,127 @@
+#include <utility>
+
+#include "catch.hpp"
+#include "helpers.hpp"
+
+using itertest::SolidInt;
+using itertest::IsIterator;
+using itertest::IsMoveConstructibleOnly;
+
+namespace {
+
+  class ValidIter {
+   private:
+    int i;
+
+   public:
+    ValidIter& operator++();    // prefix
+    ValidIter operator++(int);  // postfix
+    bool operator==(const ValidIter&) const;
+    bool operator!=(const ValidIter&) const;
+    int operator*();
+    void* operator->();
+  };
+}
+
+TEST_CASE("IsIterator fails when missing prefix ++", "[helpers]") {
+  struct InvalidIter : ValidIter {
+    InvalidIter& operator++() = delete;
+  };
+
+  REQUIRE(!IsIterator<InvalidIter>::value);
+}
+
+TEST_CASE("IsIterator fails when missing postfix ++", "[helpers]") {
+  struct InvalidIter : ValidIter {
+    InvalidIter operator++(int) = delete;
+  };
+
+  REQUIRE(!IsIterator<InvalidIter>::value);
+}
+
+TEST_CASE("IsIterator fails when missing ==", "[helpers]") {
+  struct InvalidIter : ValidIter {
+    bool operator==(const InvalidIter&) const = delete;
+  };
+
+  REQUIRE(!IsIterator<InvalidIter>::value);
+}
+
+TEST_CASE("IsIterator fails when missing !=", "[helpers]") {
+  struct InvalidIter : ValidIter {
+    bool operator!=(const InvalidIter&) const = delete;
+  };
+
+  REQUIRE(!IsIterator<InvalidIter>::value);
+}
+
+TEST_CASE("IsIterator fails when missing *", "[helpers]") {
+  struct InvalidIter : ValidIter {
+    int operator*() = delete;
+  };
+
+  REQUIRE(!IsIterator<InvalidIter>::value);
+}
+
+TEST_CASE("IsIterator fails when missing copy-ctor", "[helpers]") {
+  struct InvalidIter : ValidIter {
+    InvalidIter(const InvalidIter&) = delete;
+  };
+
+  REQUIRE(!IsIterator<InvalidIter>::value);
+}
+
+TEST_CASE("IsIterator fails when missing copy assignment", "[helpers]") {
+  struct InvalidIter : ValidIter {
+    InvalidIter& operator=(const InvalidIter&) = delete;
+  };
+
+  REQUIRE(!IsIterator<InvalidIter>::value);
+}
+
+TEST_CASE("IsIterator passes a valid iterator", "[helpers]") {
+  REQUIRE(IsIterator<ValidIter>::value);
+}
+
+struct HasNothing {
+  HasNothing(const HasNothing&) = delete;
+  HasNothing& operator=(const HasNothing&) = delete;
+};
+
+struct HasMoveAndCopyCtor {
+  HasMoveAndCopyCtor(const HasMoveAndCopyCtor&);
+  HasMoveAndCopyCtor(HasMoveAndCopyCtor&&);
+};
+
+struct HasMoveCtorAndAssign {
+  HasMoveCtorAndAssign(HasMoveCtorAndAssign&&);
+  HasMoveCtorAndAssign& operator=(HasMoveCtorAndAssign&&);
+};
+
+struct HasMoveCtorAndCopyAssign {
+  HasMoveCtorAndCopyAssign(HasMoveCtorAndCopyAssign&&);
+  HasMoveCtorAndCopyAssign& operator=(const HasMoveCtorAndCopyAssign&);
+};
+
+struct HasMoveCtorOnly {
+  HasMoveCtorOnly(HasMoveCtorOnly&&);
+};
+
+TEST_CASE("IsMoveConstructibleOnly false without move ctor", "[helpers]") {
+  REQUIRE_FALSE(IsMoveConstructibleOnly<HasNothing>::value);
+}
+
+TEST_CASE("IsMoveConstructibleOnly false with copy ctor", "[helpers]") {
+  REQUIRE_FALSE(IsMoveConstructibleOnly<HasMoveAndCopyCtor>::value);
+}
+
+TEST_CASE("IsMoveConstructibleOnly false with move assign", "[helpers]") {
+  REQUIRE_FALSE(IsMoveConstructibleOnly<HasMoveCtorAndAssign>::value);
+}
+TEST_CASE("IsMoveConstructibleOnly false with copy assign", "[helpers]") {
+  REQUIRE_FALSE(IsMoveConstructibleOnly<HasMoveCtorAndCopyAssign>::value);
+}
+
+TEST_CASE("IsMoveConstructibleOnly true when met", "[helpers]") {
+  REQUIRE(IsMoveConstructibleOnly<HasMoveCtorOnly>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_imap.cpp b/src/external/cppitertools-1.0/test/test_imap.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..db37d3f5815f9c13b8aae43039a64b66e4a75d15
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_imap.cpp
@@ -0,0 +1,183 @@
+#include <imap.hpp>
+
+#include "helpers.hpp"
+
+#include <cctype>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::imap;
+using Vec = const std::vector<int>;
+
+namespace {
+  int plusone(int i) {
+    return i + 1;
+  }
+
+  class PlusOner {
+   public:
+    int operator()(int i) {
+      return i + 1;
+    }
+  };
+
+  int power(int b, int e) {
+    int acc = 1;
+    for (int i = 0; i < e; ++i) {
+      acc *= b;
+    }
+    return acc;
+  }
+}
+
+TEST_CASE("imap: works with lambda, callable, and function", "[imap]") {
+  Vec ns = {10, 20, 30};
+  std::vector<int> v;
+  SECTION("with lambda") {
+    auto im = imap([](int i) { return i + 1; }, ns);
+    v.assign(std::begin(im), std::end(im));
+  }
+
+  SECTION("with callable") {
+    SECTION("Normal call") {
+      auto im = imap(PlusOner{}, ns);
+      v.assign(std::begin(im), std::end(im));
+    }
+    SECTION("Pipe") {
+      auto im = ns | imap(PlusOner{});
+      v.assign(std::begin(im), std::end(im));
+    }
+  }
+
+  SECTION("with function") {
+    auto im = imap(PlusOner{}, ns);
+    v.assign(std::begin(im), std::end(im));
+  }
+
+  Vec vc = {11, 21, 31};
+  REQUIRE(v == vc);
+}
+
+// TODO enable once zip supports const
+#if 0
+TEST_CASE("imap: supports const iteration", "[imap][const]") {
+  Vec ns = {10, 20, 30};
+  const auto m = imap(PlusOner{}, ns);
+  Vec v(std::begin(m), std::end(m));
+  Vec vc = {11, 21, 31};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("imap: const iterators can be compared to non-const iterators", "[imap][const]") {
+  auto m = imap(PlusOner{}, Vec{});
+  const auto& cm = m;
+  (void)(std::begin(m) == std::end(cm));
+}
+#endif
+  
+
+TEST_CASE("imap: Works with different begin and end types", "[imap]") {
+  CharRange cr{'d'};
+  auto m = imap([](char c) { return std::toupper(c); }, cr);
+  Vec v(m.begin(), m.end());
+  Vec vc{'A', 'B', 'C'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("imap: works with multiple sequences", "[imap]") {
+  Vec bases = {0, 1, 2, 3};
+  Vec exps = {1, 2, 3, 4};
+
+  auto im = imap(power, bases, exps);
+  Vec v(std::begin(im), std::end(im));
+  Vec vc = {0, 1, 8, 81};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("imap: terminates on shortest squence", "[imap]") {
+  Vec ns1 = {1, 2, 3, 4};
+  Vec ns2 = {2, 4, 6, 8, 10};
+  Vec vc = {3, 6, 9, 12};
+  SECTION("shortest sequence first") {
+    auto im = imap([](int a, int b) { return a + b; }, ns1, ns2);
+    Vec v(std::begin(im), std::end(im));
+    REQUIRE(v == vc);
+  }
+  SECTION("shortest sequence second") {
+    auto im = imap([](int a, int b) { return a + b; }, ns2, ns1);
+    Vec v(std::begin(im), std::end(im));
+    REQUIRE(v == vc);
+  }
+}
+
+TEST_CASE("imap: operator->", "[imap]") {
+  std::vector<std::string> vs = {"ab", "abcd", "abcdefg"};
+  {
+    auto m = imap([](std::string& s) { return s; }, vs);
+    auto it = std::begin(m);
+    REQUIRE(it->size() == 2);
+  }
+
+  {
+    auto m = imap([](std::string& s) -> std::string& { return s; }, vs);
+    auto it = std::begin(m);
+    REQUIRE(it->size() == 2);
+  }
+}
+
+TEST_CASE("imap: empty sequence gives nothing", "[imap]") {
+  Vec v{};
+  auto im = imap(plusone, v);
+  REQUIRE(std::begin(im) == std::end(im));
+}
+
+TEST_CASE("imap: binds to lvalues, moves rvalues", "[imap]") {
+  itertest::BasicIterable<int> bi{1, 2};
+  SECTION("binds to lvalues") {
+    imap(plusone, bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+
+  SECTION("moves rvalues") {
+    imap(plusone, std::move(bi));
+    REQUIRE(bi.was_moved_from());
+  }
+}
+
+TEST_CASE("imap: doesn't move or copy elements of iterable", "[imap]") {
+  constexpr itertest::SolidInt arr[] = {{1}, {0}, {2}};
+  for (auto&& i :
+      imap([](const itertest::SolidInt& si) { return si.getint(); }, arr)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("imap: postfix ++", "[imap]") {
+  Vec ns = {10, 20};
+  auto im = imap(plusone, ns);
+  auto it = std::begin(im);
+  it++;
+  REQUIRE((*it) == 21);
+  it++;
+  REQUIRE(it == std::end(im));
+}
+
+TEST_CASE("imap: iterator meets requirements", "[imap]") {
+  std::string s{};
+  auto c = imap([](char) { return 1; }, s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T, typename U>
+using ImpT = decltype(imap(std::declval<T>(), std::declval<U>()));
+TEST_CASE("imap: has correct ctor and assign ops", "[imap]") {
+  using T1 = ImpT<bool (*)(char c), std::string&>;
+  auto lam = [](char) { return false; };
+  using T2 = ImpT<decltype(lam), std::string>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T1>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<T2>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_iterator_wrapper.cpp b/src/external/cppitertools-1.0/test/test_iterator_wrapper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6c79a2786c01fd60ad28ffe486ca83dfecf75a28
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_iterator_wrapper.cpp
@@ -0,0 +1,222 @@
+// NOTE this header tests implementation details
+
+#include "catch.hpp"
+#include "internal/iterator_wrapper.hpp"
+
+// I'm using a std::vector of 1 int instead of just an int in order to give
+// the iterator types non-trivial constructors, destructors, and assignment.
+
+// same begin() and end() types
+struct SameTypes {
+  struct iterator {
+    iterator(int) : value_(1) {}
+
+    bool operator!=(const iterator& other) const {
+      return value_ != other.value_;
+    }
+    iterator& operator++() {
+      ++value_.front();
+      return *this;
+    }
+    const int& operator*() const {
+      return value_.front();
+    }
+    std::vector<int> value_;  // non-trvial operations
+  };
+
+  iterator begin() const {
+    return {0};
+  }
+  iterator end() const {
+    return {0};
+  }
+};
+
+// different begin() and end() types
+struct DifferentTypes {
+  struct iterator;
+  struct end_iterator;
+  struct iterator {
+    iterator() : value_{} {
+      REQUIRE(false);
+    }
+    iterator(int i) : value_(1, i) {}
+
+    bool operator!=(const iterator& other) const {
+      return value() != other.value();
+    }
+    bool operator!=(const end_iterator&) const {
+      return value() != 3;
+    }
+    iterator& operator++() {
+      ++value_.front();
+      return *this;
+    }
+    const int& operator*() const {
+      return value();
+    }
+
+    const int& value() const {
+      return value_.front();
+    }
+    std::vector<int> value_;
+  };
+
+  struct end_iterator {
+    end_iterator() {
+      REQUIRE(false);
+    }
+    end_iterator(int) {}
+
+    bool operator!=(const end_iterator&) const {
+      return false;
+    }
+    bool operator!=(const iterator& other) const {
+      return other.value() != 3;
+    }
+    end_iterator& operator++() {
+      return *this;
+    }
+    const int& operator*() const {
+      assert(false);
+      return value();
+    }
+
+    const int& value() const {
+      return value_.front();
+    }
+    std::vector<int> value_{};
+  };
+
+  iterator begin() const {
+    return {0};
+  }
+  end_iterator end() const {
+    return {0};
+  }
+};
+
+// Explicit instatiations, which could cause failures if the implementation
+// details of the implementation details change.
+template class iter::impl::
+    IteratorWrapperImpl<iter::impl::iterator_type<DifferentTypes>,
+        iter::impl::iterator_end_type<DifferentTypes>>;
+
+using iter::impl::IteratorWrapper;
+
+TEST_CASE("ensure test type iterators are totally comparable", "[test_util") {
+  {
+    SameTypes s{};
+    auto it = s.begin();
+    (void)(it != it);
+  }
+
+  {
+    DifferentTypes d{};
+    auto b = d.begin();
+    auto e = d.end();
+    (void)(b != b);
+    (void)(e != e);
+    (void)(b != e);
+    (void)(e != b);
+  }
+}
+
+TEST_CASE(
+    "Operations on IteratorWrappers with SameTypes work", "[base_iterator]") {
+  SameTypes s;
+  IteratorWrapper<SameTypes> it(s.begin());
+  REQUIRE((std::is_same<std::decay_t<decltype(it)>,
+      std::decay_t<decltype(s.begin())>>{}));
+  REQUIRE(*it == 0);
+  ++it;
+  REQUIRE(*it == 1);
+}
+
+TEST_CASE("Operations on IteratorWrappers with DifferentTypes work",
+    "[base_iterator]") {
+  DifferentTypes d;
+  using BI = IteratorWrapper<DifferentTypes>;
+  BI it(d.begin());
+  REQUIRE((!std::is_same<std::decay_t<decltype(it)>,
+           std::decay_t<decltype(d.begin())>>{}));
+  REQUIRE(*it == 0);
+  ++it;
+  REQUIRE(*it == 1);
+
+  BI it2(d.begin());
+
+  REQUIRE(it != it2);
+  REQUIRE(it2 != it);
+  ++it2;
+
+  REQUIRE_FALSE(it != it2);
+  REQUIRE_FALSE(it2 != it);
+
+  BI bend(d.end());
+  REQUIRE(it != bend);
+  REQUIRE(bend != it);
+
+  ++it;
+  ++it;
+  REQUIRE_FALSE(it != bend);
+  REQUIRE_FALSE(bend != it);
+}
+
+TEST_CASE(
+    "Can copy construct a IteratorWrapper with SameTypes", "[base_iterator]") {
+  SameTypes s;
+  using BI = IteratorWrapper<SameTypes>;
+  BI it(s.begin());
+  BI it2(it);
+  REQUIRE_FALSE(it != it2);
+  ++it2;
+  REQUIRE(it != it2);
+}
+
+TEST_CASE(
+    "Can copy assign a IteratorWrapper with SameTypes", "[base_iterator]") {
+  SameTypes s;
+  using BI = IteratorWrapper<SameTypes>;
+  BI it(s.begin());
+  BI it2(s.begin());
+  REQUIRE_FALSE(it != it2);
+  ++it2;
+  REQUIRE(it != it2);
+  it = it2;
+  REQUIRE_FALSE(it != it2);
+}
+
+TEST_CASE("Can copy construct a IteratorWrapper with DifferentTypes",
+    "[base_iterator]") {
+  using BI = IteratorWrapper<DifferentTypes>;
+  DifferentTypes d;
+  BI it(d.begin());
+  BI it2(it);
+  REQUIRE_FALSE(it != it2);
+  ++it;
+  REQUIRE(it != it2);
+}
+
+TEST_CASE("Can copy construct a IteratorWrapper with DifferenTypes",
+    "[base_iterator]") {
+  using BI = IteratorWrapper<DifferentTypes>;
+  DifferentTypes d;
+  BI it(d.begin());
+  BI it2(it);
+  it = it2;
+  // break assignment into a different test
+  REQUIRE_FALSE(it != it2);
+  BI it_end(d.end());
+  REQUIRE(it != it_end);
+  SECTION("normal = end") {
+    it = it_end;
+    REQUIRE_FALSE(it != it_end);
+  }
+  SECTION("end = normal") {
+    it_end = BI{d.begin()};
+    REQUIRE_FALSE(it != it_end);
+  }
+}
+
+// TODO test move operations
diff --git a/src/external/cppitertools-1.0/test/test_iteratoriterator.cpp b/src/external/cppitertools-1.0/test/test_iteratoriterator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c4442f7a6c21c2b0a533fc1892d12115d33e794e
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_iteratoriterator.cpp
@@ -0,0 +1,63 @@
+#include <internal/iteratoriterator.hpp>
+
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::impl::IterIterWrapper;
+
+TEST_CASE("Iterator over a vector of vector iterators", "[iteratoriterator]") {
+  using std::vector;
+  vector<int> v = {2, 4, 6, 8};
+
+  IterIterWrapper<vector<vector<int>::iterator>> itr;
+  itr.get().push_back(std::begin(v) + 1);
+  itr.get().push_back(std::end(v) - 1);
+  itr.get().push_back(std::begin(v));
+
+  auto it = std::begin(itr);
+  REQUIRE(*it == 4);
+  REQUIRE(it != std::end(itr));
+  ++it;
+  REQUIRE(*it == 8);
+  it++;
+  REQUIRE(*it == 2);
+  ++it;
+  REQUIRE(it == std::end(itr));
+
+  REQUIRE(itr[0] == 4);
+  REQUIRE(itr[1] == 8);
+  REQUIRE(itr[2] == 2);
+
+  auto rit = itr.rbegin();
+
+  REQUIRE(*rit == 2);
+  REQUIRE(rit != itr.rend());
+  ++rit;
+  REQUIRE(*rit == 8);
+  ++rit;
+  REQUIRE(*rit == 4);
+  ++rit;
+  REQUIRE(rit == itr.rend());
+}
+
+TEST_CASE("IteratorIterator operator->", "[iteratoriterator]") {
+  using std::vector;
+  using std::string;
+  vector<string> v = {"hello", "everyone"};
+  IterIterWrapper<vector<vector<string>::iterator>> itritr;
+  itritr.get().push_back(std::end(v) - 1);
+  itritr.get().push_back(std::begin(v));
+  auto it = std::begin(itritr);
+  REQUIRE(it->size() == 8);
+}
+
+TEST_CASE("Iterate over a vector of string iterators", "[iteratoriterator]") {
+  std::string str = "hello world";
+  IterIterWrapper<std::vector<std::string::iterator>> itritr;
+  auto it = std::begin(itritr);
+  static_assert(std::is_same<decltype(*it),
+                    std::iterator_traits<decltype(it)>::reference>::value,
+      "iterator is mis marked");
+}
diff --git a/src/external/cppitertools-1.0/test/test_iterbase.cpp b/src/external/cppitertools-1.0/test/test_iterbase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fecfb9a4d350acd0a33ff7444740dedd66a767c1
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_iterbase.cpp
@@ -0,0 +1,96 @@
+// AGAIN the contents of iterbase are completely subject to change, do not rely
+// on any of this.  Users of the library must consider all of this undocumented
+//
+
+#include <enumerate.hpp>
+#include <internal/iterbase.hpp>
+#include <iterator>
+#include <list>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "catch.hpp"
+#include "helpers.hpp"
+
+namespace it = iter::impl;
+
+using IVec = std::vector<int>;
+
+template <typename T>
+using hrai = it::has_random_access_iter<T>;
+TEST_CASE("Detects random access iterators correctly", "[iterbase]") {
+  REQUIRE(hrai<std::vector<int>>::value);
+  REQUIRE(hrai<std::string>::value);
+  REQUIRE(hrai<int[10]>::value);
+
+  REQUIRE_FALSE(hrai<std::list<int>>::value);
+  REQUIRE_FALSE(hrai<decltype(iter::enumerate(std::list<int>{}))>::value);
+  REQUIRE_FALSE(hrai<itertest::BasicIterable<int>>::value);
+}
+
+TEST_CASE("Detects correct iterator types", "[iterbase]") {
+  REQUIRE((std::is_same<it::iterator_type<IVec>, IVec::iterator>::value));
+  REQUIRE((std::is_same<it::iterator_type<IVec>, IVec::iterator>::value));
+  REQUIRE((std::is_same<it::iterator_deref<IVec>,
+      IVec::iterator::reference>::value));
+  REQUIRE((std::is_same<it::const_iterator_deref<IVec>,
+      IVec::iterator::reference>::value));
+  REQUIRE((std::is_same<it::iterator_traits_deref<IVec>,
+      IVec::iterator::value_type>::value));
+
+  REQUIRE(
+      (std::is_same<it::iterator_arrow<IVec>, IVec::iterator::pointer>::value));
+  REQUIRE((std::is_same<it::iterator_arrow<int[10]>, int*>::value));
+}
+
+TEST_CASE("advance, next, size", "[iterbase]") {
+  IVec v = {2, 4, 6, 8, 10, 12, 14, 16, 18};
+  auto itr = std::begin(v);
+  REQUIRE(it::apply_arrow(itr) == &v[0]);
+
+  it::dumb_advance_unsafe(itr, 3);
+  REQUIRE(itr == (std::begin(v) + 3));
+  REQUIRE(it::dumb_next(std::begin(v), 3) == std::begin(v) + 3);
+  REQUIRE(it::dumb_size(v) == v.size());
+}
+
+TEST_CASE("are_same", "[iterbase]") {
+  REQUIRE((it::are_same<int, int, int, int>::value));
+  REQUIRE_FALSE((it::are_same<double, int, int, int>::value));
+  REQUIRE_FALSE((it::are_same<int, int, int, double>::value));
+  REQUIRE_FALSE((it::are_same<int, double, int, int>::value));
+}
+
+TEST_CASE("DerefHolder lvalue reference", "[iterbase]") {
+  it::DerefHolder<int&> dh;
+  int a = 2;
+  int b = 5;
+  REQUIRE_FALSE(dh);
+  dh.reset(a);
+  REQUIRE(dh);
+
+  REQUIRE(dh.get_ptr() == &a);
+  REQUIRE(&dh.get() == &a);
+  dh.reset(b);
+  REQUIRE(dh.get_ptr() == &b);
+  REQUIRE(&dh.get() == &b);
+}
+
+TEST_CASE("DerefHolder non-reference", "[iterbase]") {
+  it::DerefHolder<int> dh;
+  int a = 2;
+  int b = 5;
+  REQUIRE_FALSE(dh);
+  dh.reset(std::move(a));
+  REQUIRE(dh.get() == 2);
+  REQUIRE(&dh.get() != &a);
+
+  dh.reset(std::move(b));
+  REQUIRE(dh.get() == 5);
+}
+
+TEST_CASE("get_begin returns correct type", "[iterbase]") {
+  std::vector<int> v;
+  REQUIRE((std::is_same<decltype(it::get_begin(v)), decltype(v.begin())>{}));
+}
diff --git a/src/external/cppitertools-1.0/test/test_main.cpp b/src/external/cppitertools-1.0/test/test_main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0c7c351f437f5f43f3bb62beb254a9f1ecbec5a0
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_main.cpp
@@ -0,0 +1,2 @@
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
diff --git a/src/external/cppitertools-1.0/test/test_mixed.cpp b/src/external/cppitertools-1.0/test/test_mixed.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ecf650914d519a0bb4e629952e4f1ec09e0a92d1
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_mixed.cpp
@@ -0,0 +1,145 @@
+// mixing different itertools, there is nothing called iter::mixed()
+
+#include "itertools.hpp"
+
+#include "catch.hpp"
+
+#include <iostream>
+#include <vector>
+
+class MyUnMovable {
+  int val;
+
+ public:
+  constexpr MyUnMovable(int val) : val{val} {}
+
+  MyUnMovable(const MyUnMovable&) = delete;
+  MyUnMovable& operator=(const MyUnMovable&) = delete;
+
+  MyUnMovable(MyUnMovable&& other) : val{other.val} {}
+
+  constexpr int get_val() const {
+    return val;
+  }
+  void set_val(int val) {
+    this->val = val;
+  }
+
+  bool operator==(const MyUnMovable& other) const {
+    return this->val == other.val;
+  }
+
+  bool operator!=(const MyUnMovable& other) const {
+    return !(*this == other);
+  }
+};
+
+namespace {
+  auto inc_ten = [](MyUnMovable& el) -> MyUnMovable& {
+    int va = el.get_val();
+    el.set_val(va + 10);
+    return el;
+  };
+  auto dec_ten = [](MyUnMovable& el) -> MyUnMovable& {
+    int va = el.get_val();
+    el.set_val(va - 10);
+    return el;
+  };
+}
+
+TEST_CASE("filtering doesn't dereference multiple times", "[imap][filter]") {
+  using iter::filter;
+  using iter::imap;
+
+  // source data
+  std::array<MyUnMovable, 3> arr = {{{41}, {42}, {43}}};
+
+  auto transformed1 = imap(inc_ten, arr);
+  auto filtered = filter(
+      [](const MyUnMovable& el) { return 52 != el.get_val(); }, transformed1);
+  auto transformed2 = imap(dec_ten, filtered);
+
+  std::vector<int> v;
+  for (auto&& el : transformed2) {
+    // I would use imap again instead of the loop if this wasn't an imap
+    // test
+    v.push_back(el.get_val());
+  }
+
+  std::vector<int> vc = {41, 43};
+
+  REQUIRE(v == vc);
+
+  constexpr std::array<MyUnMovable, 3> arrc = {{{41}, {52}, {43}}};
+  REQUIRE(arr == arrc);
+}
+
+TEST_CASE("dropwhile doesn't dereference multiple times", "[imap][dropwhile]") {
+  using iter::imap;
+  using iter::dropwhile;
+
+  std::array<MyUnMovable, 3> arr = {{{41}, {42}, {43}}};
+
+  auto transformed1 = imap(inc_ten, arr);
+  auto filtered = dropwhile(
+      [](const MyUnMovable& el) { return 52 != el.get_val(); }, transformed1);
+  auto transformed2 = imap(dec_ten, filtered);
+
+  std::vector<int> v;
+  for (auto&& el : transformed2) {
+    v.push_back(el.get_val());
+  }
+
+  std::vector<int> vc = {42, 43};
+
+  std::vector<int> vsc = {51, 42, 43};
+  auto get_vals = imap([](const MyUnMovable& mv) { return mv.get_val(); }, arr);
+  std::vector<int> vs(std::begin(get_vals), std::end(get_vals));
+  REQUIRE(vs == vsc);
+}
+
+TEST_CASE("takewhile doesn't dereference multiple times", "[imap][takewhile]") {
+  using iter::imap;
+  using iter::takewhile;
+
+  std::array<MyUnMovable, 3> arr = {{{41}, {42}, {43}}};
+
+  auto transformed1 = imap(inc_ten, arr);
+  auto filtered = takewhile(
+      [](const MyUnMovable& el) { return 53 != el.get_val(); }, transformed1);
+  auto transformed2 = imap(dec_ten, filtered);
+
+  std::vector<int> v;
+  for (auto&& el : transformed2) {
+    v.push_back(el.get_val());
+  }
+
+  std::vector<int> vc = {41, 42};
+
+  std::vector<int> vsc = {41, 42, 53};
+  auto get_vals = imap([](const MyUnMovable& mv) { return mv.get_val(); }, arr);
+  std::vector<int> vs(std::begin(get_vals), std::end(get_vals));
+  REQUIRE(vs == vsc);
+}
+
+TEST_CASE("sorted(chain.from_iterable)", "[sorted][chain.from_iterable]") {
+  std::vector<std::vector<int>> v = {{2, 4, 6}};
+  auto s = iter::sorted(iter::chain.from_iterable(v));
+}
+
+TEST_CASE("filter into enumerate with pipe", "[filter][enumerate]") {
+  using iter::imap;
+  using iter::filter;
+  using iter::enumerate;
+
+  std::array<MyUnMovable, 4> arr = {{{41}, {42}, {43}, {44}}};
+  auto seq =
+      arr | filter([](const MyUnMovable& mv) { return mv.get_val() % 2 == 0; })
+      | enumerate | imap([](const auto& imv) {
+          return std::make_pair(imv.first, imv.second.get_val());
+        });
+  using Vec = std::vector<std::pair<std::size_t, int>>;
+  const Vec v(std::begin(seq), std::end(seq));
+  const Vec vc = {{0, 42}, {1, 44}};
+  REQUIRE(v == vc);
+}
diff --git a/src/external/cppitertools-1.0/test/test_permutations.cpp b/src/external/cppitertools-1.0/test/test_permutations.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5da85fc8e1d4a80024d033f9ee83f98012f36341
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_permutations.cpp
@@ -0,0 +1,138 @@
+#include <permutations.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::permutations;
+using IntPermSet = std::multiset<std::vector<int>>;
+
+TEST_CASE("permutations: basic test, 3 element sequence", "[permutations]") {
+  const std::vector<int> ns = {1, 7, 9};
+
+  IntPermSet v;
+  SECTION("Normal call") {
+    for (auto&& st : permutations(ns)) {
+      v.emplace(std::begin(st), std::end(st));
+    }
+  }
+  SECTION("Pipe") {
+    for (auto&& st : ns | permutations) {
+      v.emplace(std::begin(st), std::end(st));
+    }
+  }
+
+  const IntPermSet vc = {
+      {1, 7, 9}, {1, 9, 7}, {7, 1, 9}, {7, 9, 1}, {9, 1, 7}, {9, 7, 1}};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("permutations: const iteration", "[permutations][const]") {
+  const std::vector<int> ns = {1, 7, 9};
+
+  IntPermSet v;
+  const auto perm = permutations(ns);
+  for (auto&& st : perm) {
+    v.emplace(std::begin(st), std::end(st));
+  }
+  const IntPermSet vc = {
+      {1, 7, 9}, {1, 9, 7}, {7, 1, 9}, {7, 9, 1}, {9, 1, 7}, {9, 7, 1}};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "permutations: const iterators can be compared to non-const iteration",
+    "[permutations][const]") {
+  auto p = permutations(std::vector<int>{});
+  const auto& cp = p;
+  (void)(std::begin(p) == std::end(cp));
+}
+
+TEST_CASE("permutations: Works with different begin and end types",
+    "[permutations]") {
+  CharRange cr{'d'};
+  using CharPermSet = std::multiset<std::vector<char>>;
+  CharPermSet sc;
+  for (auto&& v : permutations(cr)) {
+    sc.emplace(std::begin(v), std::end(v));
+  }
+  const CharPermSet ans = {{'a', 'b', 'c'}, {'a', 'c', 'b'}, {'b', 'a', 'c'},
+      {'b', 'c', 'a'}, {'c', 'a', 'b'}, {'c', 'b', 'a'}};
+
+  REQUIRE(ans == sc);
+}
+
+TEST_CASE(
+    "permutations: empty sequence has one empy permutation", "[permutations]") {
+  const std::vector<int> ns{};
+  auto p = permutations(ns);
+  auto it = std::begin(p);
+  REQUIRE((*it).empty());
+  it++;
+  REQUIRE(it == std::end(p));
+}
+
+TEST_CASE("permutations: iterators can be compared", "[permutations]") {
+  const std::vector<int> ns = {1, 2};
+  auto p = permutations(ns);
+  auto it = std::begin(p);
+  REQUIRE(it == std::begin(p));
+  REQUIRE_FALSE(it != std::begin(p));
+  REQUIRE(it != std::end(p));
+  REQUIRE_FALSE(it == std::end(p));
+  ++it;
+  REQUIRE_FALSE(it == std::begin(p));
+  REQUIRE(it != std::begin(p));
+  REQUIRE_FALSE(it == std::end(p));
+  REQUIRE(it != std::end(p));
+  ++it;
+  REQUIRE(it == std::end(p));
+  REQUIRE_FALSE(it != std::end(p));
+}
+
+TEST_CASE("permutations: binds to lvalues, moves rvalues", "[permutations]") {
+  itertest::BasicIterable<int> bi{1, 2};
+  SECTION("binds to lvalues") {
+    permutations(bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+
+  SECTION("moves rvalues") {
+    permutations(std::move(bi));
+    REQUIRE(bi.was_moved_from());
+  }
+}
+
+namespace itertest {
+  bool operator<(const SolidInt& lhs, const SolidInt& rhs) {
+    return lhs.getint() < rhs.getint();
+  }
+}
+
+TEST_CASE("permutations doesn't move or copy elements of iterable",
+    "[permutations]") {
+  constexpr itertest::SolidInt arr[] = {{1}, {0}, {2}};
+  for (auto&& st : permutations(arr)) {
+    (void)st;
+  }
+}
+
+TEST_CASE("permutations: iterator meets requirements", "[permutations]") {
+  std::string s{"abc"};
+  auto c = permutations(s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+  auto&& row = *std::begin(c);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(row))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(permutations(std::declval<T>()));
+TEST_CASE("permutations: has correct ctor and assign ops", "[permutations]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_powerset.cpp b/src/external/cppitertools-1.0/test/test_powerset.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c18dd1f7cd525a1facf29217970a0d2c15e52737
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_powerset.cpp
@@ -0,0 +1,159 @@
+#include <powerset.hpp>
+
+#define CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
+#include "helpers.hpp"
+#undef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
+
+#include <iterator>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::powerset;
+using IntPermSet = std::multiset<std::multiset<int>>;
+
+TEST_CASE("powerset: basic test, [1, 2, 3]", "[powerset]") {
+  const std::vector<int> ns = {1, 2, 3};
+  IntPermSet v;
+  SECTION("Normal call") {
+    for (auto&& st : powerset(ns)) {
+      v.emplace(std::begin(st), std::end(st));
+    }
+  }
+  SECTION("Pipe") {
+    for (auto&& st : ns | powerset) {
+      v.emplace(std::begin(st), std::end(st));
+    }
+  }
+
+  const IntPermSet vc = {
+      std::multiset<int>{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("powerset: const iteration", "[powerset][const]") {
+  const std::vector<int> ns = {1, 2, 3};
+  IntPermSet v;
+  const auto ps = powerset(ns);
+  for (auto&& st : ps) {
+    v.emplace(std::begin(st), std::end(st));
+  }
+
+  const IntPermSet vc = {
+      std::multiset<int>{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}};
+  REQUIRE(v == vc);
+}
+
+// TODO this doesn't work because two different Powersetter::Iterator types use
+// two different Combinator types
+#if 0
+TEST_CASE("powerset: const iterators can be compared to non-const iterators", "[powerset][const]") {
+  auto ps = powerset(std::vector<int>{});
+  const auto& cps = ps;
+  (void)(std::begin(ps) == std::end(cps));
+}
+#endif
+
+TEST_CASE("powerset: Works with different begin and end types", "[powerset]") {
+  CharRange cr{'d'};
+  using CharPermSet = std::multiset<std::vector<char>>;
+  CharPermSet sc;
+  for (auto&& v : powerset(cr)) {
+    sc.emplace(std::begin(v), std::end(v));
+  }
+  const CharPermSet ans = {{}, {'a'}, {'b'}, {'c'}, {'a', 'b'}, {'a', 'c'},
+      {'b', 'c'}, {'a', 'b', 'c'}};
+
+  REQUIRE(ans == sc);
+}
+
+TEST_CASE("powerset: empty sequence gives only empty set", "[powerset]") {
+  const std::vector<int> ns = {};
+  auto ps = powerset(ns);
+  auto it = std::begin(ps);
+  REQUIRE(std::begin(*it) == std::end(*it));  // it's empty
+  ++it;
+  REQUIRE(it == std::end(ps));
+}
+
+TEST_CASE("powerset: iterators can be compared", "[powerset]") {
+  std::vector<int> ns = {1, 2};
+  auto p = powerset(ns);
+  {
+    auto it = std::begin(p);
+    REQUIRE(it == std::begin(p));
+    REQUIRE_FALSE(it != std::begin(p));
+    REQUIRE(it != std::end(p));
+    REQUIRE_FALSE(it == std::end(p));
+    ++it;
+    REQUIRE_FALSE(it == std::begin(p));
+    REQUIRE(it != std::begin(p));
+    REQUIRE_FALSE(it == std::end(p));
+    REQUIRE(it != std::end(p));
+    ++it;
+    ++it;
+    ++it;
+    REQUIRE(it == std::end(p));
+  }
+
+  ns.push_back(3);
+  {
+    auto it = std::begin(p);
+    auto it2 = std::begin(p);
+    std::advance(it, 4);
+    std::advance(it2, 4);
+    REQUIRE(it == it2);
+    ++it2;
+    REQUIRE(it != it2);
+  }
+}
+
+TEST_CASE("powerset: iterator copy ctor is correct", "[powerset]") {
+  // { {}, {1}, {2}, {1, 2} }
+  std::vector<int> ns = {1, 2};
+  auto p = powerset(ns);
+  auto it = std::begin(p);
+  auto it2(it);
+  REQUIRE(it == it2);
+  ++it2;
+  REQUIRE(it != it2);
+  REQUIRE(std::begin(*it) == std::end(*it));
+}
+
+TEST_CASE("powerset: binds to lvalues, moves rvalues", "[powerset]") {
+  itertest::BasicIterable<int> bi{1, 2};
+  SECTION("binds to lvalues") {
+    powerset(bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+  }
+  SECTION("moves rvalues") {
+    powerset(std::move(bi));
+    REQUIRE(bi.was_moved_from());
+  }
+}
+
+TEST_CASE("powerset: doesn't move or copy elements of iterable", "[powerset]") {
+  constexpr itertest::SolidInt arr[] = {{1}, {0}, {2}};
+  for (auto&& st : powerset(arr)) {
+    for (auto&& i : st) {
+      (void)i;
+    }
+  }
+}
+
+TEST_CASE("powerset: iterator meets requirements", "[powerset]") {
+  std::string s{"abc"};
+  auto c = powerset(s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+  auto&& row = *std::begin(c);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(row))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(powerset(std::declval<T>()));
+TEST_CASE("powerset: has correct ctor and assign ops", "[powerset]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_product.cpp b/src/external/cppitertools-1.0/test/test_product.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d7c113d94eee8a65296c712f94f52c41d44b47d7
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_product.cpp
@@ -0,0 +1,207 @@
+#include <product.hpp>
+
+#define DEFINE_BASIC_ITERABLE_COPY_CTOR
+#define DEFINE_BASIC_ITERABLE_CONST_BEGIN_AND_END
+#include "helpers.hpp"
+#undef DEFINE_BASIC_ITERABLE_CONST_BEGIN_AND_END
+#undef DEFINE_BASIC_ITERABLE_COPY_CTOR
+
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::product;
+using Vec = const std::vector<int>;
+
+TEST_CASE("product: basic test, two sequences", "[product]") {
+  using TP = std::tuple<int, char>;
+  using ResType = std::vector<TP>;
+
+  Vec n1 = {0, 1};
+  const std::string s{"abc"};
+
+  auto p = product(n1, s);
+  ResType v(std::begin(p), std::end(p));
+  ResType vc = {
+      TP{0, 'a'}, TP{0, 'b'}, TP{0, 'c'}, TP{1, 'a'}, TP{1, 'b'}, TP{1, 'c'}};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("product: const iteration", "[product][const]") {
+  using TP = std::tuple<int, char>;
+  using ResType = std::vector<TP>;
+
+  Vec n1 = {0, 1};
+  const std::string s{"abc"};
+
+  const auto p = product(n1, s);
+  ResType v(std::begin(p), std::end(p));
+  ResType vc = {
+      TP{0, 'a'}, TP{0, 'b'}, TP{0, 'c'}, TP{1, 'a'}, TP{1, 'b'}, TP{1, 'c'}};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("product: const iterators can be compared to non-const iterators",
+    "[product][const]") {
+  std::string s;
+  auto p = product(Vec{}, s);
+  const auto& cp = p;
+  (void)(std::begin(p) == std::end(cp));
+}
+
+TEST_CASE("product: two sequences where one has different begin and end",
+    "[product]") {
+  using TP = std::tuple<int, char>;
+  using ResType = std::vector<TP>;
+
+  Vec n1 = {0, 1};
+  CharRange cr('d');
+
+  auto p = product(n1, cr);
+  ResType v(std::begin(p), std::end(p));
+  ResType vc = {
+      TP{0, 'a'}, TP{0, 'b'}, TP{0, 'c'}, TP{1, 'a'}, TP{1, 'b'}, TP{1, 'c'}};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("product: three sequences", "[product]") {
+  using TP = std::tuple<int, char, int>;
+  using ResType = const std::vector<TP>;
+
+  Vec n1 = {0, 1};
+  const std::string s{"ab"};
+  Vec n2 = {2};
+
+  auto p = product(n1, s, n2);
+  ResType v(std::begin(p), std::end(p));
+
+  ResType vc = {TP{0, 'a', 2}, TP{0, 'b', 2}, TP{1, 'a', 2}, TP{1, 'b', 2}};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("product: with repeat", "[product]") {
+  using TP = std::tuple<char, char, char>;
+  using ResType = const std::vector<TP>;
+  const std::string s = "hop";
+  auto p = product<3>(s);
+  ResType v(std::begin(p), std::end(p));
+
+  ResType vc = {
+      TP{'h', 'h', 'h'}, TP{'h', 'h', 'o'}, TP{'h', 'h', 'p'},
+      TP{'h', 'o', 'h'}, TP{'h', 'o', 'o'}, TP{'h', 'o', 'p'},
+      TP{'h', 'p', 'h'}, TP{'h', 'p', 'o'}, TP{'h', 'p', 'p'},
+      TP{'o', 'h', 'h'}, TP{'o', 'h', 'o'}, TP{'o', 'h', 'p'},
+      TP{'o', 'o', 'h'}, TP{'o', 'o', 'o'}, TP{'o', 'o', 'p'},
+      TP{'o', 'p', 'h'}, TP{'o', 'p', 'o'}, TP{'o', 'p', 'p'},
+      TP{'p', 'h', 'h'}, TP{'p', 'h', 'o'}, TP{'p', 'h', 'p'},
+      TP{'p', 'o', 'h'}, TP{'p', 'o', 'o'}, TP{'p', 'o', 'p'},
+      TP{'p', 'p', 'h'}, TP{'p', 'p', 'o'}, TP{'p', 'p', 'p'},
+  };
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("product: empty when any iterable is empty", "[product]") {
+  Vec n1 = {0, 1};
+  Vec n2 = {0, 1, 2};
+  Vec emp = {};
+
+  SECTION("first iterable is empty") {
+    auto p = product(emp, n1, n2);
+    REQUIRE(std::begin(p) == std::end(p));
+  }
+
+  SECTION("middle iterable is empty") {
+    auto p = product(n1, emp, n2);
+    REQUIRE(std::begin(p) == std::end(p));
+  }
+
+  SECTION("last iterable is empty") {
+    auto p = product(n1, n2, emp);
+    REQUIRE(std::begin(p) == std::end(p));
+  }
+}
+
+TEST_CASE("product: single iterable", "[product]") {
+  const std::string s{"ab"};
+  using TP = std::tuple<char>;
+  using ResType = const std::vector<TP>;
+
+  auto p = product(s);
+  ResType v(std::begin(p), std::end(p));
+  ResType vc = {TP{'a'}, TP{'b'}};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("product: no arguments gives one empty tuple", "[product") {
+  auto p = product();
+  auto it = std::begin(p);
+  REQUIRE(it != std::end(p));
+  REQUIRE(*it == std::make_tuple());
+  ++it;
+  REQUIRE(it == std::end(p));
+}
+
+TEST_CASE("product: binds to lvalues and moves rvalues", "[product]") {
+  itertest::BasicIterable<char> bi{'x', 'y'};
+  itertest::BasicIterable<int> bi2{0, 1};
+
+  SECTION("First ref'd, second moved") {
+    product(bi, std::move(bi2));
+    REQUIRE_FALSE(bi.was_moved_from());
+    REQUIRE_FALSE(bi.was_copied_from());
+    REQUIRE(bi2.was_moved_from());
+  }
+
+  SECTION("First moved, second ref'd") {
+    product(std::move(bi), bi2);
+    REQUIRE(bi.was_moved_from());
+    REQUIRE_FALSE(bi2.was_copied_from());
+    REQUIRE_FALSE(bi2.was_moved_from());
+  }
+
+  SECTION("repeat, lvalue not moved or copied") {
+    product<2>(bi);
+    REQUIRE_FALSE(bi.was_moved_from());
+    REQUIRE_FALSE(bi.was_copied_from());
+  }
+
+  SECTION("repeat, const lvalue not moved or copied") {
+    const auto& r = bi;
+    product<2>(r);
+    REQUIRE_FALSE(bi.was_moved_from());
+    REQUIRE_FALSE(bi.was_copied_from());
+  }
+
+  SECTION("repeat, rvalue copied") {
+    product<2>(std::move(bi));
+    REQUIRE_FALSE(bi.was_moved_from());
+    REQUIRE(bi.was_copied_from());
+  }
+}
+
+TEST_CASE("product: doesn't move or copy elements of iterable", "[product]") {
+  constexpr itertest::SolidInt arr[] = {{1}, {0}, {2}};
+  for (auto&& t : product(arr)) {
+    (void)std::get<0>(t);
+  }
+}
+
+TEST_CASE("product: iterator meets requirements", "[product]") {
+  std::string s{"abc"};
+  auto c = product(s, s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename... Ts>
+using ImpT = decltype(product(std::declval<Ts>()...));
+TEST_CASE("product: has correct ctor and assign ops", "[product]") {
+  using T = ImpT<std::string&, std::vector<double>, std::vector<std::string>>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_range.cpp b/src/external/cppitertools-1.0/test/test_range.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dab748e35db1063ef85a745cd14d5fe9a9cc687a
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_range.cpp
@@ -0,0 +1,262 @@
+#include "range.hpp"
+
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+#include "catch.hpp"
+#include "helpers.hpp"
+
+using Vec = const std::vector<int>;
+using iter::range;
+
+TEST_CASE("range: works with only stop", "[range]") {
+  auto r = range(5);
+  Vec v(std::begin(r), std::end(r));
+  Vec vc{0, 1, 2, 3, 4};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("range: works with start and stop", "[range]") {
+  auto r = range(1, 5);
+  Vec v(std::begin(r), std::end(r));
+  Vec vc{1, 2, 3, 4};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("range: works with positive step > 1", "[range]") {
+  auto r = range(1, 10, 3);
+  Vec v(std::begin(r), std::end(r));
+  Vec vc{1, 4, 7};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("range(0) is empty", "[range]") {
+  auto r = iter::range(0);
+  REQUIRE(std::begin(r) == std::end(r));
+}
+
+TEST_CASE("range: postfix++", "[range]") {
+  auto r = iter::range(3);
+  auto it = std::begin(r);
+  it++;
+  REQUIRE(*it == 1);
+}
+
+TEST_CASE("start > stop produces empty range", "[range]") {
+  auto r = range(5, 0);
+  Vec v(std::begin(r), std::end(r));
+  REQUIRE(v.empty());
+}
+
+TEST_CASE("start < stop and step < 0 produces empty range", "[range]") {
+  auto r = range(0, 5, -1);
+  Vec v(std::begin(r), std::end(r));
+  REQUIRE(v.empty());
+}
+
+TEST_CASE("range: with only a negative stop is empty", "[range]") {
+  auto r = range(-3);
+  Vec v(std::begin(r), std::end(r));
+
+  REQUIRE(v.empty());
+}
+
+TEST_CASE("range: works with negative step", "[range]") {
+  auto r = range(5, -5, -3);
+  Vec v(std::begin(r), std::end(r));
+  Vec vc{5, 2, -1, -4};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("range: stops short when step doesn't divide stop-start", "[range]") {
+  auto r = range(0, 5, 2);
+  Vec v(std::begin(r), std::end(r));
+  Vec vc{0, 2, 4};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("range: stops short when step > stop-start", "[range]") {
+  auto r = range(0, 10, 20);
+  Vec v(std::begin(r), std::end(r));
+  REQUIRE(v.size() == 1);
+}
+
+TEST_CASE("range: step size of 0 gives an empty range", "[range]") {
+  auto r = range(0, 10, 0);
+  REQUIRE(std::begin(r) == std::end(r));
+  auto r2 = range(0, -10, 0);
+  REQUIRE(std::begin(r2) == std::end(r2));
+}
+
+TEST_CASE("range: can create constexpr ranges", "[range]") {
+  constexpr auto r = range(10);
+  (void)r;
+  constexpr auto r2 = range(4, 10);
+  (void)r2;
+  constexpr auto r3 = range(4, 10, 2);
+  (void)r3;
+
+  constexpr auto it = r2.begin();  // std::begin isn't constexpr
+  constexpr auto i = *it;
+  static_assert(i == 4, "range's begin has the wrong value");
+
+  constexpr auto rf = range(10.0);
+  constexpr auto itf = rf.begin();
+  constexpr auto f = *itf;
+  static_assert(f == 0.0, "range's begin has tho wrong value (float)");
+}
+
+TEST_CASE("range: const iterators compare to non-const iterators", "[range]") {
+  auto r = range(0);
+  const auto& cr = r;
+  (void)(std::begin(r) == std::end(cr));
+}
+
+TEST_CASE("range: works with a variable start, stop, and step", "[range]") {
+  constexpr int a = 10;
+  constexpr int b = 100;
+  constexpr int c = 50;
+  SECTION("Going up works") {
+    auto r = range(a, a + 2);
+    Vec v(std::begin(r), std::end(r));
+    Vec vc{a, a + 1};
+    REQUIRE(v == vc);
+  }
+
+  SECTION("Going down works") {
+    auto r = range(a + 2, a, -1);
+    Vec v(std::begin(r), std::end(r));
+    Vec vc{a + 2, a + 1};
+    REQUIRE(v == vc);
+  }
+
+  SECTION("Going down with -2 stop works") {
+    auto r = range(a + 4, a, -2);
+    Vec v(std::begin(r), std::end(r));
+    Vec vc{a + 4, a + 2};
+    REQUIRE(v == vc);
+  }
+
+  SECTION("Using three variable") {
+    auto r = range(a, b, c);
+    Vec v(std::begin(r), std::end(r));
+    REQUIRE(std::find(std::begin(v), std::end(v), a) != std::end(v));
+    REQUIRE(std::find(std::begin(v), std::end(v), b) == std::end(v));
+    REQUIRE(v.size() == 2);
+  }
+
+  SECTION("Using three with a unary negate on step") {
+    auto r = range(b, a, -c);
+    Vec v(std::begin(r), std::end(r));
+
+    REQUIRE(std::find(std::begin(v), std::end(v), b) != std::end(v));
+    REQUIRE(std::find(std::begin(v), std::end(v), a) == std::end(v));
+    REQUIRE(v.size() == 2);
+  }
+
+  SECTION("Using all three negated") {
+    auto r = range(-a, -b, -c);
+    Vec v(std::begin(r), std::end(r));
+
+    REQUIRE(std::find(std::begin(v), std::end(v), -a) != std::end(v));
+    REQUIRE(std::find(std::begin(v), std::end(v), -b) == std::end(v));
+    REQUIRE(v.size() == 2);
+  }
+}
+
+TEST_CASE("range: forward iterator checks", "[range]") {
+  auto r = range(10);
+  REQUIRE(std::end(r) == std::end(r));
+  auto it1 = std::begin(r);
+  auto it2 = std::begin(r);
+  REQUIRE_FALSE(it1 != it2);
+  REQUIRE(it1 == it2);
+  ++it1;
+  REQUIRE(it1 != it2);
+  ++it2;
+  REQUIRE(it1 == it2);
+  auto it3 = it1++;
+  REQUIRE(it3 == it2);
+  auto it4 = ++it3;
+  REQUIRE(it4 == it3);
+
+  auto it5 = std::begin(r);
+  const auto& v = *it5;
+  ++it5;
+  REQUIRE(v != *it5);
+}
+
+TEST_CASE("range: forward iterator with double, checks", "[range]") {
+  auto r = range(10.0);
+  REQUIRE(std::end(r) == std::end(r));
+  auto it1 = std::begin(r);
+  auto it2 = std::begin(r);
+  REQUIRE_FALSE(it1 != it2);
+  REQUIRE(it1 == it2);
+  ++it1;
+  REQUIRE(it1 != it2);
+  ++it2;
+  REQUIRE(it1 == it2);
+  auto it3 = it1++;
+  REQUIRE(it3 == it2);
+  auto it4 = ++it3;
+  REQUIRE(it4 == it3);
+}
+
+using FVec = const std::vector<double>;
+
+TEST_CASE("range: using doubles", "[range]") {
+  auto r = range(5.0);
+  FVec fv(std::begin(r), std::end(r));
+  FVec fvc = {0.0, 1.0, 2.0, 3.0, 4.0};
+  REQUIRE(fv == fvc);
+}
+
+TEST_CASE("range: using doubles with start and stop", "[range]") {
+  auto r = range(5.0, 10.0);
+  FVec fv(std::begin(r), std::end(r));
+  FVec fvc = {5.0, 6.0, 7.0, 8.0, 9.0};
+  REQUIRE(fv == fvc);
+}
+
+TEST_CASE("range: using doubles with start, stop and step", "[range]") {
+  auto r = range(1.0, 4.0, 0.5);
+  FVec fv(std::begin(r), std::end(r));
+  FVec fvc = {1.0, 1.5, 2.0, 2.5, 3.0, 3.5};
+  REQUIRE(fv == fvc);
+}
+
+TEST_CASE("range: using doubles with negative", "[range]") {
+  auto r = range(0.5, -2.0, -0.5);
+  FVec fv(std::begin(r), std::end(r));
+  FVec fvc = {0.5, 0.0, -0.5, -1.0, -1.5};
+  REQUIRE(fv == fvc);
+}
+
+TEST_CASE("range: using doubles with uneven step", "[range]") {
+  auto r = range(0.0, 1.75, 0.5);
+  FVec fv(std::begin(r), std::end(r));
+  FVec fvc = {0.0, 0.5, 1.0, 1.5};
+  REQUIRE(fv == fvc);
+}
+
+TEST_CASE("range: using doubles detects empty ranges", "[range]") {
+  auto r1 = range(0.0, -1.0);
+  REQUIRE(std::begin(r1) == std::end(r1));
+
+  auto r2 = range(0.0, 1.0, -1.0);
+  REQUIRE(std::begin(r2) == std::end(r2));
+}
+
+TEST_CASE("range: iterator meets forward iterator requirements", "[range]") {
+  auto r = range(5);
+  auto r2 = range(5.0);
+  REQUIRE(itertest::IsForwardIterator<decltype(std::begin(r))>::value);
+  REQUIRE(itertest::IsForwardIterator<decltype(std::begin(r2))>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_repeat.cpp b/src/external/cppitertools-1.0/test/test_repeat.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0fc62e72d995769d5530b845b43e2fbf404f0cea
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_repeat.cpp
@@ -0,0 +1,100 @@
+#include <repeat.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::repeat;
+
+TEST_CASE("repeat: one argument keeps giving value back", "[repeat]") {
+  auto r = repeat('a');
+  auto it = std::begin(r);
+  REQUIRE(*it == 'a');
+  ++it;
+  REQUIRE(*it == 'a');
+  ++it;
+  REQUIRE(*it == 'a');
+  ++it;
+  REQUIRE(*it == 'a');
+  ++it;
+  REQUIRE(*it == 'a');
+}
+
+TEST_CASE("repeat: can be used as constexpr", "[repeat]") {
+  static constexpr char c = 'a';
+  {
+    constexpr auto r = repeat(c);
+    constexpr auto i = r.begin();
+    constexpr char c2 = *i;
+    static_assert(c == c2, "repeat value not as expected");
+    constexpr auto i2 = ++i;
+    (void)i2;
+  }
+
+  {
+    constexpr static auto r = repeat('a');
+    constexpr auto i = r.begin();
+    constexpr char c2 = *i;
+    static_assert(c2 == 'a', "repeat value not as expected");
+  }
+
+  {
+    constexpr auto r = repeat(c, 2);
+    constexpr auto i = r.begin();
+    constexpr char c2 = *i;
+    static_assert(c2 == c, "repeat value not as expected");
+  }
+}
+
+TEST_CASE("repeat: iterators compare to const iterators", "[repeat]") {
+  auto r = repeat(1);
+  const auto& cr = r;
+  (void)(std::begin(r) == std::end(cr));
+}
+
+TEST_CASE("repeat: two argument repeats a number of times", "[repeat]") {
+  auto r = repeat('a', 3);
+  std::string s(std::begin(r), std::end(r));
+  REQUIRE(s == "aaa");
+}
+
+TEST_CASE("repeat: 0 count gives empty sequence", "[repeat]") {
+  auto r = repeat('a', 0);
+  REQUIRE(std::begin(r) == std::end(r));
+}
+
+TEST_CASE("repeat: negative count gives empty sequence", "[repeat]") {
+  auto r = repeat('a', -2);
+  REQUIRE(std::begin(r) == std::end(r));
+  auto r2 = repeat('a', -1);
+  REQUIRE(std::begin(r2) == std::end(r2));
+}
+
+TEST_CASE("repeat: doesn't duplicate item", "[repeat]") {
+  itertest::SolidInt si{2};
+  auto r = repeat(si);
+  auto it = std::begin(r);
+  (void)*it;
+}
+
+TEST_CASE("repeat: iterator meets requirements", "[repeat]") {
+  auto r = repeat(1);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(r))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(repeat(std::declval<T>()));
+
+template <typename T>
+using ImpT2 = decltype(repeat(std::declval<T>(), 1));
+
+TEST_CASE("repeat: has correct ctor and assign ops", "[repeat]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT2<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT2<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_reversed.cpp b/src/external/cppitertools-1.0/test/test_reversed.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..530a17fc87aed701a133e9588df5ddf587ba24a2
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_reversed.cpp
@@ -0,0 +1,115 @@
+#include <reversed.hpp>
+
+#include <array>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+
+#define DECLARE_REVERSE_ITERATOR
+#include "helpers.hpp"
+#undef DECLARE_REVERSE_ITERATOR
+
+using iter::reversed;
+using Vec = const std::vector<int>;
+
+TEST_CASE("reversed: can reverse a vector", "[reversed]") {
+  Vec ns = {10, 20, 30, 40};
+  std::vector<int> v;
+  SECTION("Normal call") {
+    auto r = reversed(ns);
+    v.assign(std::begin(r), std::end(r));
+  }
+  SECTION("Pipe") {
+    auto r = ns | reversed;
+    v.assign(std::begin(r), std::end(r));
+  }
+
+  Vec vc = {40, 30, 20, 10};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("reversed: const iteration", "[reversed][const]") {
+  Vec ns = {10, 20, 30, 40};
+  const auto r = reversed(ns);
+  Vec v(std::begin(r), std::end(r));
+  Vec vc = {40, 30, 20, 10};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("reversed: const iterators can be compared to non-const iterators",
+    "[reversed][const]") {
+  auto r = reversed(Vec{});
+  const auto& cr = r;
+  (void)(std::begin(r) == std::end(cr));
+}
+
+#if 0
+TEST_CASE("reversed: Works with different begin and end types",
+    "[reversed]") {
+  CharRange cr{'d'};
+  auto r = reversed(cr);
+  Vec v(r.begin(), r.end());
+  Vec vc{'c', 'b', 'a'};
+  REQUIRE(v == vc);
+}
+#endif
+
+TEST_CASE("reversed: can reverse an array", "[reversed]") {
+  int ns[] = {10, 20, 30, 40};
+  auto r = reversed(ns);
+
+  Vec v(std::begin(r), std::end(r));
+  Vec vc = {40, 30, 20, 10};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("reversed: empty when iterable is empty", "[reversed]") {
+  Vec emp{};
+  auto r = reversed(emp);
+  REQUIRE(std::begin(r) == std::end(r));
+}
+
+TEST_CASE("reversed: moves rvalues and binds to lvalues", "[reversed]") {
+  itertest::BasicIterable<int> bi{1, 2};
+  itertest::BasicIterable<int> bi2{1, 2};
+  reversed(bi);
+  REQUIRE_FALSE(bi.was_moved_from());
+
+  reversed(std::move(bi2));
+  REQUIRE(bi2.was_moved_from());
+}
+
+TEST_CASE("reversed: doesn't move or copy elements of array", "[reversed]") {
+  constexpr itertest::SolidInt arr[] = {{6}, {7}, {8}};
+  for (auto&& i : reversed(arr)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("reversed: with iterable doesn't move or copy elems", "[reversed]") {
+  constexpr std::array<itertest::SolidInt, 3> arr{{{6}, {7}, {8}}};
+  for (auto&& i : reversed(arr)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("reversed: iterator meets requirements", "[reversed]") {
+  Vec v;
+  auto r = reversed(v);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(r))>::value);
+
+  int a[1];
+  auto ra = reversed(a);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(ra))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(reversed(std::declval<T>()));
+TEST_CASE("reversed: has correct ctor and assign ops", "[reversed]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_slice.cpp b/src/external/cppitertools-1.0/test/test_slice.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..38386b7358b7409cb498bfef000291ea224fb1a3
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_slice.cpp
@@ -0,0 +1,161 @@
+#include <slice.hpp>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+#include "helpers.hpp"
+
+using iter::slice;
+using Vec = const std::vector<int>;
+
+TEST_CASE("slice: take from beginning", "[slice]") {
+  Vec ns = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+
+  std::vector<int> v;
+  SECTION("Normal call") {
+    auto sl = slice(ns, 5);
+    v.assign(std::begin(sl), std::end(sl));
+  }
+  SECTION("Pipe") {
+    auto sl = ns | slice(5);
+    v.assign(std::begin(sl), std::end(sl));
+  }
+
+  Vec vc = {10, 11, 12, 13, 14};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("slice: const iteration", "[slice][const]") {
+  Vec ns = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+
+  const auto sl = slice(ns, 5);
+  Vec v(std::begin(sl), std::end(sl));
+
+  Vec vc = {10, 11, 12, 13, 14};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("slice: const iterator can be compared to non-const iterator",
+    "[slice][const]") {
+  auto sl = slice(Vec{}, 1);
+  const auto& csl = sl;
+  (void)(std::begin(sl) == std::end(csl));
+}
+
+TEST_CASE("slice: start and stop", "[slice]") {
+  Vec ns = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+
+  std::vector<int> v;
+  SECTION("Normal call") {
+    auto sl = slice(ns, 2, 6);
+    v.assign(std::begin(sl), std::end(sl));
+  }
+  SECTION("Pipe") {
+    auto sl = ns | slice(2, 6);
+    v.assign(std::begin(sl), std::end(sl));
+  }
+
+  Vec vc = {12, 13, 14, 15};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("slice: Works with different begin and end types", "[slice]") {
+  CharRange cr{'z'};
+  auto sl = slice(cr, 2, 5);
+  std::vector<char> v(std::begin(sl), std::end(sl));
+  const std::vector<char> vc = {'c', 'd', 'e'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("slice: start, stop, step", "[slice]") {
+  Vec ns = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+  std::vector<int> v;
+
+  SECTION("Normal call") {
+    auto sl = slice(ns, 2, 8, 2);
+    v.assign(std::begin(sl), std::end(sl));
+  }
+  SECTION("Pipe") {
+    auto sl = ns | slice(2, 8, 2);
+    v.assign(std::begin(sl), std::end(sl));
+  }
+
+  Vec vc = {12, 14, 16};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("slice: empty iterable", "[slice]") {
+  Vec ns{};
+  auto sl = slice(ns, 3);
+  REQUIRE(std::begin(sl) == std::end(sl));
+}
+
+TEST_CASE("slice: stop is beyond end of iterable", "[slice]") {
+  Vec ns = {1, 2, 3};
+  auto sl = slice(ns, 10);
+
+  Vec v(std::begin(sl), std::end(sl));
+  REQUIRE(v == ns);
+}
+
+TEST_CASE("slice: start is beyond end of iterable", "[slice]") {
+  Vec ns = {1, 2, 3};
+  auto sl = slice(ns, 5, 10);
+  REQUIRE(std::begin(sl) == std::end(sl));
+}
+
+TEST_CASE("slice: (stop - start) % step != 0", "[slice]") {
+  Vec ns = {1, 2, 3, 4};
+  auto sl = slice(ns, 0, 2, 3);
+  Vec v(std::begin(sl), std::end(sl));
+  Vec vc = {1};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("slice: invalid ranges give 0 size slices", "[slice]") {
+  Vec ns = {1, 2, 3};
+  SECTION("negative step") {
+    auto sl = slice(ns, 1, 10, -1);
+    REQUIRE(std::begin(sl) == std::end(sl));
+  }
+  SECTION("stop < start") {
+    auto sl = slice(ns, 2, 0, 3);
+    REQUIRE(std::begin(sl) == std::end(sl));
+  }
+}
+
+TEST_CASE("slice: moves rvalues and binds to lvalues", "[slice]") {
+  itertest::BasicIterable<int> bi{1, 2, 3, 4};
+  slice(bi, 1, 3);
+  REQUIRE_FALSE(bi.was_moved_from());
+  auto sl = slice(std::move(bi), 1, 3);
+  REQUIRE(bi.was_moved_from());
+
+  Vec v(std::begin(sl), std::end(sl));
+  Vec vc = {2, 3};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("slice: with iterable doesn't move or copy elems", "[slice]") {
+  constexpr itertest::SolidInt arr[] = {{6}, {7}, {8}};
+  for (auto&& i : slice(arr, 2)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("slice: iterator meets requirements", "[slice]") {
+  std::string s{"abcdef"};
+  auto c = slice(s, 1, 3);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(slice(std::declval<T>(), 1));
+TEST_CASE("slice: has correct ctor and assign ops", "[slice]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_sliding_window.cpp b/src/external/cppitertools-1.0/test/test_sliding_window.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a302e1dc425a03b10e2f37c30a8f6c764d8318b3
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_sliding_window.cpp
@@ -0,0 +1,134 @@
+#include <sliding_window.hpp>
+
+#include <array>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+#include "helpers.hpp"
+
+using iter::sliding_window;
+using Vec = const std::vector<int>;
+
+TEST_CASE("sliding_window: window of size 3", "[sliding_window]") {
+  Vec ns = {10, 20, 30, 40, 50};
+  std::vector<std::vector<int>> vc = {{10, 20, 30}, {20, 30, 40}, {30, 40, 50}};
+  std::vector<std::vector<int>> v;
+  SECTION("Normal call") {
+    for (auto&& win : sliding_window(ns, 3)) {
+      v.emplace_back(std::begin(win), std::end(win));
+    }
+  }
+  SECTION("Pipe") {
+    for (auto&& win : ns | sliding_window(3)) {
+      v.emplace_back(std::begin(win), std::end(win));
+    }
+  }
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("sliding_window: const iteration", "[sliding_window][const]") {
+  Vec ns = {10, 20, 30, 40, 50};
+  std::vector<std::vector<int>> vc = {{10, 20, 30}, {20, 30, 40}, {30, 40, 50}};
+  std::vector<std::vector<int>> v;
+  const auto sw = sliding_window(ns, 3);
+  for (auto&& win : sw) {
+    v.emplace_back(std::begin(win), std::end(win));
+  }
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "sliding_window: const iterators can be compared to non-const iterators",
+    "[sliding_window][const]") {
+  auto sw = sliding_window(Vec{}, 2);
+  const auto& csw = sw;
+  (void)(std::begin(sw) == std::end(csw));
+}
+
+TEST_CASE("sliding_window: Works with different begin and end types",
+    "[sliding_window]") {
+  CharRange cr{'f'};
+  std::vector<std::vector<char>> results;
+  for (auto&& g : sliding_window(cr, 3)) {
+    results.emplace_back(std::begin(g), std::end(g));
+  }
+  std::vector<std::vector<char>> rc = {
+      {'a', 'b', 'c'}, {'b', 'c', 'd'}, {'c', 'd', 'e'}};
+  REQUIRE(results == rc);
+}
+
+TEST_CASE("sliding window: oversized window is empty", "[sliding_window]") {
+  Vec ns = {10, 20, 30};
+  auto sw = sliding_window(ns, 5);
+  REQUIRE(std::begin(sw) == std::end(sw));
+}
+
+TEST_CASE("sliding window: window size == len(iterable)", "[sliding_window]") {
+  Vec ns = {10, 20, 30};
+  auto sw = sliding_window(ns, 3);
+  auto it = std::begin(sw);
+  REQUIRE(it != std::end(sw));
+
+  Vec v(std::begin(*it), std::end(*it));
+
+  REQUIRE(ns == v);
+  ++it;
+  REQUIRE(it == std::end(sw));
+}
+
+TEST_CASE("sliding window: empty iterable is empty", "[sliding_window]") {
+  Vec ns{};
+  auto sw = sliding_window(ns, 1);
+  REQUIRE(std::begin(sw) == std::end(sw));
+}
+
+TEST_CASE("sliding window: window size of 1", "[sliding_window]") {
+  Vec ns = {10, 20, 30};
+  auto sw = sliding_window(ns, 1);
+  auto it = std::begin(sw);
+  REQUIRE(*std::begin(*it) == 10);
+  ++it;
+  REQUIRE(*std::begin(*it) == 20);
+  ++it;
+  REQUIRE(*std::begin(*it) == 30);
+  ++it;
+  REQUIRE(it == std::end(sw));
+}
+
+TEST_CASE("sliding window: window size of 0", "[sliding_window]") {
+  Vec ns = {10, 20, 30};
+  auto sw = sliding_window(ns, 0);
+  REQUIRE(std::begin(sw) == std::end(sw));
+}
+
+TEST_CASE(
+    "sliding window: moves rvalues and binds to lvalues", "[sliding_window]") {
+  itertest::BasicIterable<int> bi{1, 2};
+  sliding_window(bi, 1);
+  REQUIRE_FALSE(bi.was_moved_from());
+  sliding_window(std::move(bi), 1);
+  REQUIRE(bi.was_moved_from());
+}
+
+TEST_CASE("sliding window: doesn't copy elements", "[sliding_window]") {
+  constexpr std::array<itertest::SolidInt, 3> arr{{{6}, {7}, {8}}};
+  for (auto&& i : sliding_window(arr, 1)) {
+    (void)*std::begin(i);
+  }
+}
+
+TEST_CASE("sliding_window: iterator meets requirements", "[sliding_window]") {
+  std::string s{"abcdef"};
+  auto c = sliding_window(s, 2);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(sliding_window(std::declval<T>(), 1));
+TEST_CASE(
+    "sliding_window: has correct ctor and assign ops", "[sliding_window]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_sorted.cpp b/src/external/cppitertools-1.0/test/test_sorted.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..930ca4f8e5db05b470dcf1797eac1ef1a12b92c2
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_sorted.cpp
@@ -0,0 +1,229 @@
+#include <sorted.hpp>
+
+#include <array>
+#include <set>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+#include "helpers.hpp"
+
+using iter::sorted;
+
+using Vec = const std::vector<int>;
+
+TEST_CASE("sorted: iterates through a vector in sorted order", "[sorted]") {
+  Vec ns = {4, 0, 5, 1, 6, 7, 9, 3, 2, 8};
+  std::vector<int> v;
+  SECTION("Normal Call") {
+    auto s = sorted(ns);
+    v.assign(std::begin(s), std::end(s));
+  }
+  SECTION("Pipe") {
+    auto s = ns | sorted;
+    v.assign(std::begin(s), std::end(s));
+  }
+  Vec vc = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("sorted: const iteration", "[sorted][const]") {
+  Vec ns = {4, 0, 5, 1, 6, 7, 9, 3, 2, 8};
+  const auto s = sorted(ns);
+  Vec v(std::begin(s), std::end(s));
+  Vec vc = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("sorted: const iterators can be compared to non-const iterators",
+    "[sorted][const]") {
+  auto s = sorted(Vec{});
+  const auto& cs = s;
+  (void)(std::begin(s) == std::end(cs));
+}
+
+TEST_CASE("sorted: can modify elements through sorted", "[sorted]") {
+  std::vector<int> ns(3, 9);
+  for (auto&& n : sorted(ns)) {
+    n = -1;
+  }
+  Vec vc(3, -1);
+  REQUIRE(ns == vc);
+}
+
+char inc_vowels(char c) {
+  return c == 'a' || c == 'e' ? c + 10 : c;
+}
+
+TEST_CASE("sorted: Works with different begin and end types", "[sorted]") {
+  using Vec = std::vector<char>;
+  CharRange cr{'g'};
+  auto s =
+      sorted(cr, [](char x, char y) { return inc_vowels(x) < inc_vowels(y); });
+  Vec v(s.begin(), s.end());
+  Vec vc{'b', 'c', 'd', 'f', 'a', 'e'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("sorted: can iterate over unordered container", "[sorted]") {
+  std::unordered_set<int> ns = {1, 3, 2, 0, 4};
+  auto s = sorted(ns);
+
+  Vec v(std::begin(s), std::end(s));
+  Vec vc = {0, 1, 2, 3, 4};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("sorted: empty when iterable is empty", "[sorted]") {
+  Vec ns{};
+  auto s = sorted(ns);
+  REQUIRE(std::begin(s) == std::end(s));
+}
+
+namespace {
+  bool int_greater_than(int lhs, int rhs) {
+    return lhs > rhs;
+  }
+
+  struct IntGreaterThan {
+    bool operator()(int lhs, int rhs) const {
+      return lhs > rhs;
+    }
+  };
+}
+
+TEST_CASE("sorted: works with different callable types", "[sorted]") {
+  Vec ns = {4, 1, 3, 2, 0};
+  std::vector<int> v;
+  SECTION("with function pointer") {
+    auto s = sorted(ns, int_greater_than);
+    v.assign(std::begin(s), std::end(s));
+  }
+
+  SECTION("with callable object") {
+    SECTION("Normal call") {
+      auto s = sorted(ns, IntGreaterThan{});
+      v.assign(std::begin(s), std::end(s));
+    }
+    SECTION("Pipe") {
+      auto s = ns | sorted(IntGreaterThan{});
+      v.assign(std::begin(s), std::end(s));
+    }
+  }
+
+  SECTION("with lambda") {
+    auto s = sorted(ns, [](int lhs, int rhs) { return lhs > rhs; });
+    v.assign(std::begin(s), std::end(s));
+  }
+
+  Vec vc = {4, 3, 2, 1, 0};
+  REQUIRE(v == vc);
+}
+
+namespace {
+  template <typename T>
+  class BasicIterableWithConstDeref {
+   private:
+    T* data;
+    std::size_t size;
+    bool was_moved_from_ = false;
+
+   public:
+    BasicIterableWithConstDeref(std::initializer_list<T> il)
+        : data{new T[il.size()]}, size{il.size()} {
+      // would like to use enumerate, can't because it's for unit
+      // testing enumerate
+      std::size_t i = 0;
+      for (auto&& e : il) {
+        data[i] = e;
+        ++i;
+      }
+    }
+
+    BasicIterableWithConstDeref& operator=(
+        BasicIterableWithConstDeref&&) = delete;
+    BasicIterableWithConstDeref& operator=(
+        const BasicIterableWithConstDeref&) = delete;
+    BasicIterableWithConstDeref(const BasicIterableWithConstDeref&) = delete;
+
+    BasicIterableWithConstDeref(BasicIterableWithConstDeref&& other)
+        : data{other.data}, size{other.size} {
+      other.data = nullptr;
+      other.was_moved_from_ = true;
+    }
+
+    bool was_moved_from() const {
+      return this->was_moved_from_;
+    }
+
+    ~BasicIterableWithConstDeref() {
+      delete[] this->data;
+    }
+
+    class Iterator {
+     private:
+      T* p;
+
+     public:
+      Iterator(T* b) : p{b} {}
+      bool operator!=(const Iterator& other) const {
+        return this->p != other.p;
+      }
+
+      Iterator& operator++() {
+        ++this->p;
+        return *this;
+      }
+
+      T& operator*() {
+        return *this->p;
+      }
+
+      const T& operator*() const {
+        return *this->p;
+      }
+    };
+
+    Iterator begin() {
+      return {this->data};
+    }
+
+    Iterator end() {
+      return {this->data + this->size};
+    }
+  };
+}
+
+TEST_CASE("sorted: moves rvalues and binds to lvalues", "[sorted]") {
+  BasicIterableWithConstDeref<int> bi{1, 2};
+  sorted(bi);
+  REQUIRE_FALSE(bi.was_moved_from());
+
+  sorted(std::move(bi));
+  REQUIRE(bi.was_moved_from());
+}
+
+TEST_CASE("sorted: doesn't move or copy elements of iterable", "[sorted]") {
+  using itertest::SolidInt;
+  constexpr SolidInt arr[] = {{6}, {7}, {8}};
+  for (auto &&i : sorted(arr, [](const SolidInt &lhs, const SolidInt &rhs) {
+         return lhs.getint() < rhs.getint();
+       })) {
+    (void)i;
+  }
+}
+
+template <typename T>
+using ImpT = decltype(sorted(std::declval<T>()));
+TEST_CASE("sorted: has correct ctor and assign ops", "[sorted]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
+
+TEST_CASE("sorted: iterator meets requirements", "[sorted]") {
+  Vec v;
+  auto r = sorted(v);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(r))>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_starmap.cpp b/src/external/cppitertools-1.0/test/test_starmap.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..210694f57b014204f098341b3083e7392928f781
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_starmap.cpp
@@ -0,0 +1,173 @@
+#include <starmap.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::starmap;
+
+namespace {
+  long f(long d, int i) {
+    return d * i;
+  }
+
+  std::string g(const std::string& s, int i, char c) {
+    std::stringstream ss;
+    ss << s << ' ' << i << ' ' << c;
+    return ss.str();
+  }
+
+  struct Callable {
+    int operator()(int a, int b, int c) {
+      return a + b + c;
+    }
+
+    int operator()(int a, int b) {
+      return a + b;
+    }
+
+    int operator()(int a) {
+      return a;
+    }
+  };
+}
+
+TEST_CASE("starmap: works with function pointer and lambda", "[starmap]") {
+  using Vec = const std::vector<int>;
+  const std::vector<std::pair<double, int>> v1 = {{1l, 2}, {3l, 11}, {6l, 7}};
+  Vec vc = {2l, 33l, 42l};
+
+  std::vector<int> v;
+  SECTION("with function") {
+    SECTION("Normal call") {
+      auto sm = starmap(f, v1);
+      v.assign(std::begin(sm), std::end(sm));
+    }
+    SECTION("pipe") {
+      auto sm = v1 | starmap(f);
+      v.assign(std::begin(sm), std::end(sm));
+    }
+  }
+
+  SECTION("with lambda") {
+    auto sm = starmap([](long a, int b) { return a * b; }, v1);
+    v.assign(std::begin(sm), std::end(sm));
+  }
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("starmap: vector of pairs const iteration", "[starmap][const]") {
+  using Vec = const std::vector<int>;
+  const std::vector<std::pair<double, int>> v1 = {{1l, 2}, {3l, 11}, {6l, 7}};
+
+  const auto sm = starmap(Callable{}, v1);
+  std::vector<int> v(std::begin(sm), std::end(sm));
+  Vec vc = {3, 14, 13};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "starmap: vector of pairs const iterators can be compared to non-const "
+    "iterators",
+    "[starmap][const]") {
+  const std::vector<std::pair<double, int>> v1;
+  auto sm = starmap(Callable{}, v1);
+  const auto& csm = sm;
+  (void)(std::begin(sm) == std::end(csm));
+}
+
+TEST_CASE("starmap: Works with different begin and end types", "[starmap]") {
+  IntCharPairRange icr{{3, 'd'}};
+  using Vec = std::vector<std::string>;
+  auto sm = starmap([](int i, char c) { return std::to_string(i) + c; }, icr);
+  Vec v(sm.begin(), sm.end());
+  Vec vc{"0a", "1b", "2c"};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("starmap: tuple of tuples const iteration", "[starmap][const]") {
+  using Vec = const std::vector<int>;
+  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
+  const auto sm = starmap(Callable{}, tup);
+  Vec v(std::begin(sm), std::end(sm));
+}
+
+TEST_CASE(
+    "starmap: tuple of tuples const iterators can be compared to non-const "
+    "iterator",
+    "[starmap][const]") {
+  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
+  auto sm = starmap(Callable{}, tup);
+  const auto& csm = sm;
+  (void)(std::begin(sm) == std::end(csm));
+  (void)(std::begin(csm) == std::end(sm));
+}
+
+TEST_CASE("starmap: list of tuples", "[starmap]") {
+  using Vec = const std::vector<std::string>;
+  using T = std::tuple<std::string, int, double>;
+  std::list<T> li = {T{"hey", 42, 'a'}, T{"there", 3, 'b'}, T{"yall", 5, 'c'}};
+
+  auto sm = starmap(g, li);
+  Vec v(std::begin(sm), std::end(sm));
+  Vec vc = {"hey 42 a", "there 3 b", "yall 5 c"};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("starmap: tuple of tuples", "[starmap]") {
+  using Vec = const std::vector<int>;
+  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
+  std::vector<int> v;
+  SECTION("Normal call") {
+    auto sm = starmap(Callable{}, tup);
+    v.assign(std::begin(sm), std::end(sm));
+  }
+  SECTION("Pipe") {
+    auto sm = tup | starmap(Callable{});
+    v.assign(std::begin(sm), std::end(sm));
+  }
+
+  Vec vc = {89, 7};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("starmap: tuple of pairs", "[starmap]") {
+  using Vec = const std::vector<int>;
+  auto p =
+      std::make_pair(std::array<int, 3>{{15, 100, 2000}}, std::make_tuple(16));
+  Callable c;
+  auto sm = starmap(c, p);
+
+  Vec v(std::begin(sm), std::end(sm));
+  Vec vc = {2115, 16};
+
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("starmap: moves rvalues, binds to lvalues", "[starmap]") {
+  itertest::BasicIterable<std::tuple<int>> bi{};
+  starmap(Callable{}, bi);
+  REQUIRE_FALSE(bi.was_moved_from());
+  starmap(Callable{}, std::move(bi));
+  REQUIRE(bi.was_moved_from());
+}
+
+TEST_CASE("starmap: iterator meets requirements", "[starmap]") {
+  std::string s{};
+  const std::vector<std::pair<double, int>> v1;
+  auto sm = starmap([](long a, int b) { return a * b; }, v1);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(sm))>::value);
+}
+
+TEST_CASE(
+    "starmap:  tuple of tuples iterator meets requirements", "[starmap]") {
+  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
+  auto sm = starmap(Callable{}, tup);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(sm))>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_takewhile.cpp b/src/external/cppitertools-1.0/test/test_takewhile.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bea9e6e29f2c3a4bca904052a5b8c7f50ffa6064
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_takewhile.cpp
@@ -0,0 +1,160 @@
+#include <takewhile.hpp>
+
+#include <array>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+#include "helpers.hpp"
+
+using iter::takewhile;
+using Vec = const std::vector<int>;
+
+namespace {
+  bool under_ten(int i) {
+    return i < 10;
+  }
+
+  struct UnderTen {
+    bool operator()(int i) {
+      return i < 10;
+    }
+  };
+}
+
+TEST_CASE("takewhile: works with lambda, callable, and function pointer",
+    "[takewhile]") {
+  Vec ns = {1, 3, 5, 20, 2, 4, 6, 8};
+  SECTION("function pointer") {
+    auto tw = takewhile(under_ten, ns);
+    Vec v(std::begin(tw), std::end(tw));
+    Vec vc = {1, 3, 5};
+    REQUIRE(v == vc);
+  }
+
+  SECTION("callable object") {
+    std::vector<int> v;
+    SECTION("Normal call") {
+      auto tw = takewhile(UnderTen{}, ns);
+      v.assign(std::begin(tw), std::end(tw));
+    }
+
+    SECTION("Pipe") {
+      auto tw = ns | takewhile(UnderTen{});
+      v.assign(std::begin(tw), std::end(tw));
+    }
+
+    Vec vc = {1, 3, 5};
+    REQUIRE(v == vc);
+  }
+
+  SECTION("lambda") {
+    auto tw = takewhile([](int i) { return i < 10; }, ns);
+    Vec v(std::begin(tw), std::end(tw));
+    Vec vc = {1, 3, 5};
+    REQUIRE(v == vc);
+  }
+}
+
+TEST_CASE("takewhile: supports const iteration", "[takewhile][const]") {
+  Vec ns = {1, 3, 5, 20, 2, 4, 6, 8};
+  const auto tw = takewhile(UnderTen{}, ns);
+  Vec v(std::begin(tw), std::end(tw));
+  Vec vc = {1, 3, 5};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("takewhile: const iterator and non-const iterator are comparable",
+    "[takewhile][const]") {
+  auto tw = takewhile(UnderTen{}, Vec{});
+  const auto& ctw = tw;
+  (void)(std::begin(tw) == std::end(ctw));
+}
+
+TEST_CASE(
+    "takewhile: Works with different begin and end types", "[takewhile]") {
+  CharRange cr{'f'};
+  auto t = takewhile([](char c) { return c < 'd'; }, cr);
+  Vec v(t.begin(), t.end());
+  Vec vc{'a', 'b', 'c'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("takewhile: identity", "[takewhile]") {
+  Vec ns{1, 2, 3, 0, 4, 5, 0};
+  std::vector<int> v;
+  SECTION("Normal call") {
+    auto tw = takewhile(ns);
+    v.assign(std::begin(tw), std::end(tw));
+  }
+  SECTION("Pipe") {
+    auto tw = ns | takewhile;
+    v.assign(std::begin(tw), std::end(tw));
+  }
+  Vec vc = {1, 2, 3};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("takewhile: everything passes predicate", "[takewhile]") {
+  Vec ns{1, 2, 3};
+  auto tw = takewhile(under_ten, ns);
+  Vec v(std::begin(tw), std::end(tw));
+  Vec vc = {1, 2, 3};
+}
+
+TEST_CASE("takewhile: empty iterable is empty", "[takewhile]") {
+  Vec ns{};
+  auto tw = takewhile(under_ten, ns);
+  REQUIRE(std::begin(tw) == std::end(tw));
+}
+
+TEST_CASE(
+    "takewhile: when first element fails predicate, it's empty"
+    "[takewhile]") {
+  SECTION("First element is only element") {
+    Vec ns = {20};
+    auto tw = takewhile(under_ten, ns);
+    REQUIRE(std::begin(tw) == std::end(tw));
+  }
+
+  SECTION("First element followed by elements that pass") {
+    Vec ns = {20, 1, 1};
+    auto tw = takewhile(under_ten, ns);
+    REQUIRE(std::begin(tw) == std::end(tw));
+  }
+}
+
+TEST_CASE("takewhile: moves rvalues, binds to lvalues", "[takewhile]") {
+  itertest::BasicIterable<int> bi{1, 2};
+  takewhile(under_ten, bi);
+  REQUIRE_FALSE(bi.was_moved_from());
+
+  takewhile(under_ten, std::move(bi));
+  REQUIRE(bi.was_moved_from());
+}
+
+TEST_CASE(
+    "takewhile: with iterable doesn't move or copy elements", "[takewhile]") {
+  constexpr std::array<itertest::SolidInt, 3> arr{{{8}, {9}, {10}}};
+  auto func = [](const itertest::SolidInt& si) { return si.getint() < 10; };
+  for (auto&& i : takewhile(func, arr)) {
+    (void)i;
+  }
+}
+
+TEST_CASE("takewhile: iterator meets requirements", "[takewhile]") {
+  std::string s{};
+  auto c = takewhile([] { return true; }, s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T, typename U>
+using ImpT = decltype(takewhile(std::declval<T>(), std::declval<U>()));
+TEST_CASE("takewhile: has correct ctor and assign ops", "[takewhile]") {
+  using T1 = ImpT<bool (*)(char c), std::string&>;
+  auto lam = [](char) { return false; };
+  using T2 = ImpT<decltype(lam), std::string>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T1>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<T2>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_unique_everseen.cpp b/src/external/cppitertools-1.0/test/test_unique_everseen.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c34bb74d54896a343b2cca62fc4e71ff9a2a76c9
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_unique_everseen.cpp
@@ -0,0 +1,87 @@
+#include <unique_everseen.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::unique_everseen;
+
+using Vec = const std::vector<int>;
+
+TEST_CASE("unique everseen: adjacent repeating values", "[unique_everseen]") {
+  Vec ns = {1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 8, 8, 8, 9, 9};
+  auto ue = unique_everseen(ns);
+  Vec v(std::begin(ue), std::end(ue));
+  Vec vc = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("unique everseen: const iteration", "[unique_everseen][const]") {
+  Vec ns = {1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 8, 8, 8, 9, 9};
+  const auto ue = unique_everseen(ns);
+  Vec v(std::begin(ue), std::end(ue));
+  Vec vc = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "unique everseen: const iterators can be compared to non-const iterators",
+    "[unique_everseen][const]") {
+  auto ue = unique_everseen(std::vector<int>{});
+  const auto& cue = ue;
+  (void)(std::begin(ue) == std::end(cue));
+}
+
+TEST_CASE(
+    "unique everseen: nonadjacent repeating values", "[unique_everseen]") {
+  Vec ns = {1, 2, 3, 4, 3, 2, 1, 5, 6};
+  std::vector<int> v;
+  SECTION("Normal call") {
+    auto ue = unique_everseen(ns);
+    v.assign(std::begin(ue), std::end(ue));
+  }
+  SECTION("Pipe") {
+    auto ue = ns | unique_everseen;
+    v.assign(std::begin(ue), std::end(ue));
+  }
+  Vec vc = {1, 2, 3, 4, 5, 6};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "unique everseen: moves rvalues, binds to lvalues", "[unique_everseen]") {
+  itertest::BasicIterable<int> bi{1, 2};
+  unique_everseen(bi);
+  REQUIRE_FALSE(bi.was_moved_from());
+
+  unique_everseen(std::move(bi));
+  REQUIRE(bi.was_moved_from());
+}
+
+TEST_CASE("unique everseen: Works with different begin and end types",
+    "[unique_everseen]") {
+  CharRange cr{'d'};
+  using Vec = std::vector<char>;
+  auto ue = unique_everseen(cr);
+  Vec v(ue.begin(), ue.end());
+  Vec vc{'a', 'b', 'c'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("unique_everseen: iterator meets requirements", "[unique_everseen]") {
+  std::string s{};
+  auto c = unique_everseen(s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(unique_everseen(std::declval<T>()));
+TEST_CASE(
+    "unique_everseen: has correct ctor and assign ops", "[unique_everseen]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_unique_justseen.cpp b/src/external/cppitertools-1.0/test/test_unique_justseen.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2614aa8928e4b96e592b342d19a5b3bbac67c2f9
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_unique_justseen.cpp
@@ -0,0 +1,93 @@
+#include <unique_justseen.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::unique_justseen;
+
+using Vec = std::vector<int>;
+
+TEST_CASE("unique justseen: adjacent repeating values", "[unique_justseen]") {
+  Vec ns = {1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 8, 8, 8, 9, 9};
+  auto ue = unique_justseen(ns);
+  Vec v(std::begin(ue), std::end(ue));
+  Vec vc = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("unique justseen: const iteration", "[unique_justseen][const]") {
+  Vec ns = {1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 8, 8, 8, 9, 9};
+  const auto uj = unique_justseen(ns);
+  Vec v(std::begin(uj), std::end(uj));
+  Vec vc = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE(
+    "unique justseen: const iterator can be compared to non-const iterators",
+    "[unique_justseen][const]") {
+  auto uj = unique_justseen(Vec{});
+  const auto& cuj = uj;
+  (void)(std::begin(uj) == std::begin(cuj));
+}
+
+TEST_CASE("unique justseen: some repeating values", "[unique_justseen]") {
+  Vec ns = {1, 2, 2, 3, 4, 4, 5, 6, 6};
+  std::vector<int> v;
+  SECTION("Normal call") {
+    auto ue = unique_justseen(ns);
+    v.assign(std::begin(ue), std::end(ue));
+  }
+  SECTION("Pipe") {
+    auto ue = ns | unique_justseen;
+    v.assign(std::begin(ue), std::end(ue));
+  }
+  Vec vc = {1, 2, 3, 4, 5, 6};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("unique justseen: Works with different begin and end types",
+    "[unique_justseen]") {
+  CharRange cr{'d'};
+  using Vec = std::vector<char>;
+  auto uj = unique_justseen(cr);
+  Vec v(uj.begin(), uj.end());
+  Vec vc{'a', 'b', 'c'};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("unique justseen: doesn't omit non-adjacent duplicates",
+    "[unique_justseen]") {
+  Vec ns = {1, 2, 3, 2, 1, 2, 3, 2, 1};
+  auto ue = unique_justseen(ns);
+  Vec v(std::begin(ue), std::end(ue));
+  REQUIRE(v == ns);
+}
+
+TEST_CASE("unique justseen: moves and binds correctly", "[unique_justseen]") {
+  itertest::BasicIterable<int> bi{1, 2};
+  unique_justseen(bi);
+  REQUIRE_FALSE(bi.was_moved_from());
+
+  unique_justseen(std::move(bi));
+  REQUIRE(bi.was_moved_from());
+}
+
+TEST_CASE("unique_justseen: iterator meets requirements", "[unique_justseen]") {
+  std::string s{};
+  auto c = unique_justseen(s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+}
+
+template <typename T>
+using ImpT = decltype(unique_justseen(std::declval<T>()));
+TEST_CASE(
+    "unique_justseen: has correct ctor and assign ops", "[unique_justseen]") {
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string&>>::value);
+  REQUIRE(itertest::IsMoveConstructibleOnly<ImpT<std::string>>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_zip.cpp b/src/external/cppitertools-1.0/test/test_zip.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..74538cfa9c68c427f70b793f10a550e32c12a3a5
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_zip.cpp
@@ -0,0 +1,152 @@
+#include <zip.hpp>
+
+#include "helpers.hpp"
+
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::zip;
+using itertest::BasicIterable;
+using itertest::SolidInt;
+
+TEST_CASE("zip: Simple case, same length", "[zip]") {
+  using Tu = std::tuple<int, char, double>;
+  using ResVec = const std::vector<Tu>;
+  std::vector<int> iv{10, 20, 30};
+  std::string s{"hey"};
+  double arr[] = {1.0, 2.0, 4.0};
+
+  auto z = zip(iv, s, arr);
+  ResVec v(std::begin(z), std::end(z));
+  ResVec vc{Tu{10, 'h', 1.0}, Tu{20, 'e', 2.0}, Tu{30, 'y', 4.0}};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("zip: const iteration", "[zip][const]") {
+  using Tu = std::tuple<int, char, double>;
+  using ResVec = const std::vector<Tu>;
+  std::vector<int> iv{10, 20, 30};
+  std::string s{"hey"};
+  double arr[] = {1.0, 2.0, 4.0};
+
+  const auto z = zip(iv, s, arr);
+
+  ResVec v(std::begin(z), std::end(z));
+  ResVec vc{Tu{10, 'h', 1.0}, Tu{20, 'e', 2.0}, Tu{30, 'y', 4.0}};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("zip: const iterators can be compared to non-const iterators",
+    "[zip][const]") {
+  std::vector<int> v;
+  std::string s;
+  auto z = zip(std::vector<int>{}, s);
+  const auto& cz = z;
+  (void)(std::begin(z) == std::end(cz));
+}
+
+TEST_CASE(
+    "zip: three sequences, one sequence has different begin and end", "[zip]") {
+  using Tu = std::tuple<int, char, double>;
+  using ResVec = const std::vector<Tu>;
+  std::vector<int> iv{10, 20, 30};
+  CharRange cr('d');
+  double arr[] = {1.0, 2.0, 4.0};
+
+  auto z = zip(iv, cr, arr);
+  ResVec v(std::begin(z), std::end(z));
+  ResVec vc{Tu{10, 'a', 1.0}, Tu{20, 'b', 2.0}, Tu{30, 'c', 4.0}};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("zip: One empty, all empty", "[zip]") {
+  std::vector<int> iv = {1, 2, 3};
+  std::string s{};
+  auto z = zip(iv, s);
+  REQUIRE_FALSE(std::begin(z) != std::end(z));
+  auto z2 = zip(s, iv);
+  REQUIRE_FALSE(std::begin(z2) != std::end(z2));
+}
+
+TEST_CASE("zip: terminates on shortest sequence", "[zip]") {
+  std::vector<int> iv{1, 2, 3, 4, 5};
+  std::string s{"hi"};
+  auto z = zip(iv, s);
+
+  REQUIRE(std::distance(std::begin(z), std::end(z)) == 2);
+}
+
+TEST_CASE("zip: Empty", "[zip]") {
+  auto z = zip();
+  REQUIRE_FALSE(std::begin(z) != std::end(z));
+}
+
+TEST_CASE("zip: Modify sequence through zip", "[zip]") {
+  std::vector<int> iv{1, 2, 3};
+  std::vector<int> iv2{1, 2, 3, 4};
+  for (auto&& t : zip(iv, iv2)) {
+    std::get<0>(t) = -1;
+    std::get<1>(t) = -1;
+  }
+
+  const std::vector<int> vc{-1, -1, -1};
+  const std::vector<int> vc2{-1, -1, -1, 4};
+  REQUIRE(iv == vc);
+  REQUIRE(iv2 == vc2);
+}
+
+TEST_CASE("zip: binds reference when it should", "[zip]") {
+  BasicIterable<char> bi{'x', 'y', 'z'};
+  zip(bi);
+  REQUIRE_FALSE(bi.was_moved_from());
+}
+
+TEST_CASE("zip: moves rvalues", "[zip]") {
+  BasicIterable<char> bi{'x', 'y', 'z'};
+  zip(std::move(bi));
+  REQUIRE(bi.was_moved_from());
+}
+
+TEST_CASE("zip: Can bind ref and move in single zip", "[zip]") {
+  BasicIterable<char> b1{'x', 'y', 'z'};
+  BasicIterable<char> b2{'a', 'b'};
+  zip(b1, std::move(b2));
+  REQUIRE_FALSE(b1.was_moved_from());
+  REQUIRE(b2.was_moved_from());
+}
+
+TEST_CASE("zip: doesn't move or copy elements of iterable", "[zip]") {
+  constexpr SolidInt arr[] = {{6}, {7}, {8}};
+  for (auto&& t : zip(arr)) {
+    (void)std::get<0>(t);
+  }
+}
+
+TEST_CASE("zip: postfix ++", "[zip]") {
+  const std::vector<int> v = {1};
+  auto z = zip(v);
+  auto it = std::begin(z);
+  it++;
+  REQUIRE(it == std::end(z));
+}
+
+TEST_CASE("zip: iterator meets requirements", "[zip]") {
+  std::string s{};
+  auto c = zip(s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+  auto c2 = zip(s, s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c2))>::value);
+}
+
+template <typename... Ts>
+using ImpT = decltype(zip(std::declval<Ts>()...));
+TEST_CASE("zip: has correct ctor and assign ops", "[zip]") {
+  using T = ImpT<std::string&, std::vector<double>, std::vector<std::string>>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T>::value);
+}
diff --git a/src/external/cppitertools-1.0/test/test_zip_longest.cpp b/src/external/cppitertools-1.0/test/test_zip_longest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..be6091f5efbb574fa70020f4b5641c7cd2f6e7c6
--- /dev/null
+++ b/src/external/cppitertools-1.0/test/test_zip_longest.cpp
@@ -0,0 +1,188 @@
+#include <zip_longest.hpp>
+
+#include "helpers.hpp"
+
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "catch.hpp"
+
+using iter::zip_longest;
+
+// reopening boost is the only way I can find that gets this to print
+namespace boost {
+  template <typename T>
+  std::ostream& operator<<(std::ostream& out, const optional<T>& opt) {
+    if (opt) {
+      out << "Just " << *opt;
+    } else {
+      out << "Nothing";
+    }
+    return out;
+  }
+}
+
+template <typename... Ts>
+using const_opt_tuple = std::tuple<boost::optional<const Ts&>...>;
+
+TEST_CASE(
+    "zip longest: correctly detects longest at any position", "[zip_longest]") {
+  const std::vector<int> ivec{2, 4, 6, 8, 10, 12};
+  const std::vector<std::string> svec{"abc", "def", "xyz"};
+  const std::string str{"hello"};
+
+  SECTION("longest first") {
+    using TP = const_opt_tuple<int, std::string, char>;
+    using ResVec = std::vector<TP>;
+
+    auto zl = zip_longest(ivec, svec, str);
+    ResVec results(std::begin(zl), std::end(zl));
+    ResVec rc = {TP{{ivec[0]}, {svec[0]}, {str[0]}},
+        TP{{ivec[1]}, {svec[1]}, {str[1]}}, TP{{ivec[2]}, {svec[2]}, {str[2]}},
+        TP{{ivec[3]}, {}, {str[3]}}, TP{{ivec[4]}, {}, {str[4]}},
+        TP{{ivec[5]}, {}, {}}};
+
+    REQUIRE(results == rc);
+  }
+
+  SECTION("longest in middle") {
+    using TP = const_opt_tuple<std::string, int, char>;
+    using ResVec = std::vector<TP>;
+
+    auto zl = zip_longest(svec, ivec, str);
+    ResVec results(std::begin(zl), std::end(zl));
+    ResVec rc = {TP{{svec[0]}, {ivec[0]}, {str[0]}},
+        TP{{svec[1]}, {ivec[1]}, {str[1]}}, TP{{svec[2]}, {ivec[2]}, {str[2]}},
+        TP{{}, {ivec[3]}, {str[3]}}, TP{{}, {ivec[4]}, {str[4]}},
+        TP{{}, {ivec[5]}, {}}};
+
+    REQUIRE(results == rc);
+  }
+
+  SECTION("longest at end") {
+    using TP = const_opt_tuple<std::string, char, int>;
+    using ResVec = std::vector<TP>;
+
+    auto zl = zip_longest(svec, str, ivec);
+    ResVec results(std::begin(zl), std::end(zl));
+    ResVec rc = {TP{{svec[0]}, {str[0]}, {ivec[0]}},
+        TP{{svec[1]}, {str[1]}, {ivec[1]}}, TP{{svec[2]}, {str[2]}, {ivec[2]}},
+        TP{{}, {str[3]}, {ivec[3]}}, TP{{}, {str[4]}, {ivec[4]}},
+        TP{{}, {}, {ivec[5]}}};
+
+    REQUIRE(results == rc);
+  }
+}
+
+TEST_CASE(
+    "zip_longest: three sequences, one sequence has different begin and end",
+    "[zip_longest]") {
+  // using TP = const_opt_tuple</*int,*/ char>;
+  using TP = std::tuple<int, char>;
+  using ResVec = std::vector<TP>;
+
+  std::vector<int> iv{10, 20};
+  CharRange cr('c');
+
+  ResVec v;
+  for (auto&& p : zip_longest(iv, cr)) {
+    v.push_back(TP{*std::get<0>(p), *std::get<1>(p)});
+  }
+  ResVec vc{TP{10, 'a'}, TP{20, 'b'}};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("zip_longest: const iteration", "[zip_longest][const]") {
+  // using TP = const_opt_tuple</*int,*/ char>;
+  using TP = std::tuple<int, char>;
+  using ResVec = std::vector<TP>;
+
+  char cr[] = {'a', 'b'};
+
+  ResVec v;
+  const auto zl = zip_longest(std::vector<int>{10, 20}, cr);
+
+  for (auto&& p : zl) {
+    v.push_back(TP{*std::get<0>(p), *std::get<1>(p)});
+  }
+  ResVec vc{TP{10, 'a'}, TP{20, 'b'}};
+  REQUIRE(v == vc);
+}
+
+TEST_CASE("zip_longest: const iterators can be compared to non-const iterators",
+    "[zip_longest][const]") {
+  auto zl = zip_longest(std::vector<int>{});
+  const auto& czl = zl;
+  std::begin(zl);
+  std::begin(czl);
+  (void)(std::begin(zl) == std::end(czl));
+}
+
+TEST_CASE(
+    "zip longest: when all are empty, terminates right away", "[zip_longest]") {
+  const std::vector<int> ivec{};
+  const std::vector<std::string> svec{};
+  const std::string str{};
+
+  auto zl = zip_longest(ivec, svec, str);
+  REQUIRE(std::begin(zl) == std::end(zl));
+}
+
+TEST_CASE("zip longest: can modify zipped sequences", "[zip_longest]") {
+  std::vector<int> ns1 = {1, 2, 3};
+  std::vector<int> ns2 = {10, 11, 12};
+  for (auto&& t : zip_longest(ns1, ns2)) {
+    *std::get<0>(t) = -1;
+    *std::get<1>(t) = -1;
+  }
+
+  std::vector<int> vc = {-1, -1, -1};
+  REQUIRE(ns1 == vc);
+  REQUIRE(ns2 == vc);
+}
+
+TEST_CASE("zip longest: empty zip_longest() is empty", "[zip_longest]") {
+  auto zl = zip_longest();
+  REQUIRE(std::begin(zl) == std::end(zl));
+  REQUIRE_FALSE(std::begin(zl) != std::end(zl));
+}
+
+TEST_CASE("zip_longest: binds to lvalues, moves rvalues", "[zip_longest]") {
+  itertest::BasicIterable<char> b1{'x', 'y', 'z'};
+  itertest::BasicIterable<char> b2{'a', 'b'};
+  SECTION("bind to first, moves second") {
+    zip_longest(b1, std::move(b2));
+  }
+  SECTION("move first, bind to second") {
+    zip_longest(std::move(b2), b1);
+  }
+  REQUIRE_FALSE(b1.was_moved_from());
+  REQUIRE(b2.was_moved_from());
+}
+
+TEST_CASE("zip_longest: doesn't move or copy elements", "[zip_longest]") {
+  constexpr itertest::SolidInt arr[] = {{6}, {7}, {8}};
+  for (auto&& t : zip_longest(arr, arr)) {
+    (void)std::get<0>(t);
+  }
+}
+
+TEST_CASE("zip_longest: iterator meets requirements", "[zip_longest]") {
+  std::string s{};
+  auto c = zip_longest(s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
+  auto c2 = zip_longest(s, s);
+  REQUIRE(itertest::IsIterator<decltype(std::begin(c2))>::value);
+}
+
+template <typename... Ts>
+using ImpT = decltype(zip_longest(std::declval<Ts>()...));
+TEST_CASE("zip_longest: has correct ctor and assign ops", "[zip_longest]") {
+  using T = ImpT<std::string&, std::vector<double>, std::vector<std::string>>;
+  REQUIRE(itertest::IsMoveConstructibleOnly<T>::value);
+}
diff --git a/src/external/cppitertools-1.0/unique_everseen.hpp b/src/external/cppitertools-1.0/unique_everseen.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..35322ca839acd42e4a513f7754f7e6e12d36f6dd
--- /dev/null
+++ b/src/external/cppitertools-1.0/unique_everseen.hpp
@@ -0,0 +1,31 @@
+#ifndef ITER_UNIQUE_EVERSEEN_HPP_
+#define ITER_UNIQUE_EVERSEEN_HPP_
+
+#include "filter.hpp"
+#include "internal/iterbase.hpp"
+
+#include <functional>
+#include <iterator>
+#include <type_traits>
+#include <unordered_set>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    struct UniqueEverseenFn : Pipeable<UniqueEverseenFn> {
+      template <typename Container>
+      auto operator()(Container&& container) const {
+        using elem_type = impl::iterator_deref<Container>;
+        auto func = [elem_seen = std::unordered_set<std::decay_t<elem_type>>()](
+            const std::remove_reference_t<elem_type>& e) mutable {
+          return elem_seen.insert(e).second;
+        };
+        return filter(func, std::forward<Container>(container));
+      }
+    };
+  }
+
+  constexpr impl::UniqueEverseenFn unique_everseen{};
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/unique_justseen.hpp b/src/external/cppitertools-1.0/unique_justseen.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2d63b7eac8f5a0d1850f3f91d980d2cf997efa42
--- /dev/null
+++ b/src/external/cppitertools-1.0/unique_justseen.hpp
@@ -0,0 +1,25 @@
+#ifndef ITER_UNIQUE_JUSTSEEN_HPP
+#define ITER_UNIQUE_JUSTSEEN_HPP
+
+#include "groupby.hpp"
+#include "imap.hpp"
+
+#include <iterator>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    struct UniqueJustseenFn : Pipeable<UniqueJustseenFn> {
+      template <typename Container>
+      auto operator()(Container&& container) const {
+        // decltype(auto) return type in lambda so reference types are preserved
+        return imap([](auto&& group) -> decltype(
+                        auto) { return *get_begin(group.second); },
+            groupby(std::forward<Container>(container)));
+      }
+    };
+  }
+  constexpr impl::UniqueJustseenFn unique_justseen{};
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/zip.hpp b/src/external/cppitertools-1.0/zip.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a7f2f19d310aa37b8290cfd8294287160dd06a67
--- /dev/null
+++ b/src/external/cppitertools-1.0/zip.hpp
@@ -0,0 +1,133 @@
+#ifndef ITER_ZIP_HPP_
+#define ITER_ZIP_HPP_
+
+#include "internal/iter_tuples.hpp"
+#include "internal/iterbase.hpp"
+
+#include <algorithm>
+#include <iterator>
+#include <tuple>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename TupleType, std::size_t... Is>
+    class Zipped;
+
+    template <typename TupleType, std::size_t... Is>
+    Zipped<TupleType, Is...> zip_impl(TupleType&&, std::index_sequence<Is...>);
+  }
+
+  template <typename... Containers>
+  auto zip(Containers&&... containers);
+}
+
+template <typename TupleType, std::size_t... Is>
+class iter::impl::Zipped {
+ private:
+  TupleType containers_;
+  friend Zipped iter::impl::zip_impl<TupleType, Is...>(
+      TupleType&&, std::index_sequence<Is...>);
+
+  Zipped(TupleType&& containers) : containers_(std::move(containers)) {}
+
+ public:
+  Zipped(Zipped&&) = default;
+
+  // template templates here because I need to defer evaluation in the const
+  // iteration case for types that don't have non-const begin() and end(). If I
+  // passed in the actual types of the tuples of iterators and the type for
+  // deref they'd need to be known in the function declarations below.
+  template <typename TupleTypeT, template <typename> class IteratorTuple,
+      template <typename> class TupleDeref>
+  class Iterator {
+   private:
+    template <typename, template <typename> class, template <typename> class>
+    friend class Iterator;
+    IteratorTuple<TupleTypeT> iters_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = TupleDeref<TupleTypeT>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IteratorTuple<TupleTypeT>&& iters) : iters_(std::move(iters)) {}
+
+    Iterator& operator++() {
+      absorb(++std::get<Is>(iters_)...);
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T, template <typename> class IT,
+        template <typename> class TD>
+    bool operator!=(const Iterator<T, IT, TD>& other) const {
+      if (sizeof...(Is) == 0) return false;
+
+      bool results[] = {
+          true, (std::get<Is>(iters_) != std::get<Is>(other.iters_))...};
+      return std::all_of(
+          get_begin(results), get_end(results), [](bool b) { return b; });
+    }
+
+    template <typename T, template <typename> class IT,
+        template <typename> class TD>
+    bool operator==(const Iterator<T, IT, TD>& other) const {
+      return !(*this != other);
+    }
+
+    TupleDeref<TupleTypeT> operator*() {
+      return TupleDeref<TupleTypeT>{(*std::get<Is>(iters_))...};
+    }
+
+    auto operator-> () -> ArrowProxy<decltype(**this)> {
+      return {**this};
+    }
+  };
+
+  Iterator<TupleType, iterator_tuple_type, iterator_deref_tuple> begin() {
+    return {iterator_tuple_type<TupleType>{
+        get_begin(std::get<Is>(containers_))...}};
+  }
+
+  Iterator<TupleType, iterator_tuple_type, iterator_deref_tuple> end() {
+    return {
+        iterator_tuple_type<TupleType>{get_end(std::get<Is>(containers_))...}};
+  }
+
+  Iterator<AsConst<TupleType>, const_iterator_tuple_type,
+      const_iterator_deref_tuple>
+  begin() const {
+    return {const_iterator_tuple_type<AsConst<TupleType>>{
+        get_begin(impl::as_const(std::get<Is>(containers_)))...}};
+  }
+
+  Iterator<AsConst<TupleType>, const_iterator_tuple_type,
+      const_iterator_deref_tuple>
+  end() const {
+    return {const_iterator_tuple_type<AsConst<TupleType>>{
+        get_end(impl::as_const(std::get<Is>(containers_)))...}};
+  }
+};
+
+template <typename TupleType, std::size_t... Is>
+iter::impl::Zipped<TupleType, Is...> iter::impl::zip_impl(
+    TupleType&& containers, std::index_sequence<Is...>) {
+  return {std::move(containers)};
+}
+
+template <typename... Containers>
+auto iter::zip(Containers&&... containers) {
+  return impl::zip_impl(
+      std::tuple<Containers...>{std::forward<Containers>(containers)...},
+      std::index_sequence_for<Containers...>{});
+}
+
+#endif
diff --git a/src/external/cppitertools-1.0/zip_longest.hpp b/src/external/cppitertools-1.0/zip_longest.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ba6ea3c529fead5818c0cb611797849f06cbf8e7
--- /dev/null
+++ b/src/external/cppitertools-1.0/zip_longest.hpp
@@ -0,0 +1,156 @@
+#ifndef ITER_ZIP_LONGEST_HPP_
+#define ITER_ZIP_LONGEST_HPP_
+
+#include "internal/iter_tuples.hpp"
+#include "internal/iterbase.hpp"
+
+#include <boost/optional.hpp>
+#include <iterator>
+#include <tuple>
+#include <utility>
+
+namespace iter {
+  namespace impl {
+    template <typename TupleType, std::size_t... Is>
+    class ZippedLongest;
+
+    template <typename TupleType, std::size_t... Is>
+    ZippedLongest<TupleType, Is...> zip_longest_impl(
+        TupleType&&, std::index_sequence<Is...>);
+  }
+
+  template <typename... Containers>
+  auto zip_longest(Containers&&... containers);
+}
+
+template <typename TupleType, std::size_t... Is>
+class iter::impl::ZippedLongest {
+ private:
+  TupleType containers_;
+  friend ZippedLongest zip_longest_impl<TupleType, Is...>(
+      TupleType&&, std::index_sequence<Is...>);
+
+  template <std::size_t I, typename TupleTypeT>
+  using OptType = boost::optional<iterator_deref<std::tuple_element_t<I,
+      std::remove_reference_t<TupleTypeT>>>>;
+
+  template <std::size_t I, typename TupleTypeT>
+  using ConstOptType =
+      boost::optional<const_iterator_type_deref<std::tuple_element_t<I,
+          std::remove_reference_t<TupleTypeT>>>>;
+
+  template <typename TupleTypeT,
+      template <std::size_t, typename> class OptTempl>
+  using ZipIterDeref = std::tuple<OptTempl<Is, TupleTypeT>...>;
+
+  ZippedLongest(TupleType&& containers) : containers_(std::move(containers)) {}
+
+ public:
+  ZippedLongest(ZippedLongest&&) = default;
+  template <typename TupleTypeT, template <typename> class IterTuple,
+      template <std::size_t, typename> class OptTempl>
+  class Iterator {
+   private:
+    template <typename, template <typename> class,
+        template <std::size_t, typename> class>
+    friend class Iterator;
+    IterTuple<TupleTypeT> iters_;
+    IterTuple<TupleTypeT> ends_;
+
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = ZipIterDeref<TupleTypeT, OptTempl>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type*;
+    using reference = value_type&;
+
+    Iterator(IterTuple<TupleTypeT>&& iters, IterTuple<TupleTypeT>&& ends)
+        : iters_(std::move(iters)), ends_(std::move(ends)) {}
+
+    Iterator& operator++() {
+      // increment every iterator that's not already at
+      // the end
+      absorb(((std::get<Is>(iters_) != std::get<Is>(ends_))
+                  ? (++std::get<Is>(iters_), 0)
+                  : 0)...);
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      auto ret = *this;
+      ++*this;
+      return ret;
+    }
+
+    template <typename T, template <typename> class TT,
+        template <std::size_t, typename> class TU>
+    bool operator!=(const Iterator<T, TT, TU>& other) const {
+      if (sizeof...(Is) == 0) return false;
+
+      bool results[] = {
+          false, (std::get<Is>(iters_) != std::get<Is>(other.iters_))...};
+      return std::any_of(
+          get_begin(results), get_end(results), [](bool b) { return b; });
+    }
+
+    template <typename T, template <typename> class TT,
+        template <std::size_t, typename> class TU>
+    bool operator==(const Iterator<T, TT, TU>& other) const {
+      return !(*this != other);
+    }
+
+    ZipIterDeref<TupleTypeT, OptTempl> operator*() {
+      return ZipIterDeref<TupleTypeT, OptTempl>{
+          ((std::get<Is>(iters_) != std::get<Is>(ends_))
+                  ? OptTempl<Is, TupleTypeT>{*std::get<Is>(iters_)}
+                  : OptTempl<Is, TupleTypeT>{})...};
+    }
+
+    auto operator-> () -> ArrowProxy<decltype(**this)> {
+      return {**this};
+    }
+  };
+
+  Iterator<TupleType, iterator_tuple_type, OptType> begin() {
+    return {
+        iterator_tuple_type<TupleType>{get_begin(std::get<Is>(containers_))...},
+        iterator_tuple_type<TupleType>{get_end(std::get<Is>(containers_))...}};
+  }
+
+  Iterator<TupleType, iterator_tuple_type, OptType> end() {
+    return {
+        iterator_tuple_type<TupleType>{get_end(std::get<Is>(containers_))...},
+        iterator_tuple_type<TupleType>{get_end(std::get<Is>(containers_))...}};
+  }
+
+  Iterator<AsConst<TupleType>, const_iterator_tuple_type, ConstOptType> begin()
+      const {
+    return {const_iterator_tuple_type<AsConst<TupleType>>{
+                get_begin(impl::as_const(std::get<Is>(containers_)))...},
+        const_iterator_tuple_type<AsConst<TupleType>>{
+            get_end(impl::as_const(std::get<Is>(containers_)))...}};
+  }
+
+  Iterator<AsConst<TupleType>, const_iterator_tuple_type, ConstOptType> end()
+      const {
+    return {const_iterator_tuple_type<AsConst<TupleType>>{
+                get_end(impl::as_const(std::get<Is>(containers_)))...},
+        const_iterator_tuple_type<AsConst<TupleType>>{
+            get_end(impl::as_const(std::get<Is>(containers_)))...}};
+  }
+};
+
+template <typename TupleType, std::size_t... Is>
+iter::impl::ZippedLongest<TupleType, Is...> iter::impl::zip_longest_impl(
+    TupleType&& containers, std::index_sequence<Is...>) {
+  return {std::move(containers)};
+}
+
+template <typename... Containers>
+auto iter::zip_longest(Containers&&... containers) {
+  return impl::zip_longest_impl(
+      std::tuple<Containers...>{std::forward<Containers>(containers)...},
+      std::index_sequence_for<Containers...>{});
+}
+
+#endif
diff --git a/src/xrt/auxiliary/CMakeLists.txt b/src/xrt/auxiliary/CMakeLists.txt
index 724977223e3247ecebbb4f95dc8ae90d40f52dfb..a672184de927eafa8323db9120fb5d6861897a49 100644
--- a/src/xrt/auxiliary/CMakeLists.txt
+++ b/src/xrt/auxiliary/CMakeLists.txt
@@ -166,6 +166,7 @@ target_link_libraries(aux_tracking PUBLIC aux-includes PRIVATE aux_math)
 target_include_directories(aux_tracking SYSTEM
 	PRIVATE
 	${EIGEN3_INCLUDE_DIR}
+	${CMAKE_CURRENT_SOURCE_DIR}/../../external/cppitertools-1.0
 	)
 # for flexkalman
 target_link_libraries(aux_tracking PRIVATE xrt-external-flexkalman)
diff --git a/src/xrt/auxiliary/tracking/t_tracker_psvr.cpp b/src/xrt/auxiliary/tracking/t_tracker_psvr.cpp
index 87e2e09628d03342e3cb5c31f55b6c608baa309c..34238f1bd9ef708fb45db0ca47f0faba3ab3dd5d 100644
--- a/src/xrt/auxiliary/tracking/t_tracker_psvr.cpp
+++ b/src/xrt/auxiliary/tracking/t_tracker_psvr.cpp
@@ -10,12 +10,13 @@
 #include "xrt/xrt_tracking.h"
 
 #include "tracking/t_tracking.h"
+#include "tracking/t_calibration_opencv.hpp"
+
 
 #include "util/u_misc.h"
 #include "util/u_debug.h"
 #include "util/u_frame.h"
 #include "util/u_format.h"
-#include "util/u_time.h"
 
 #include "math/m_api.h"
 
@@ -25,10 +26,112 @@
 #include <assert.h>
 #include <pthread.h>
 
+#include <permutations.hpp>
+#include <Eigen/Eigen>
+#include <opencv2/opencv.hpp>
+#include <inttypes.h>
+
+#define PSVR_NUM_LEDS 7
+#define PSVR_OPTICAL_SOLVE_THRESH 5
+#define PSVR_DISAMBIG_REJECT_DIST 0.3f
+#define PSVR_MODEL_CENTER_INDEX 2 // we should use the tag enum for this
+#define PSVR_SEARCH_RADIUS                                                     \
+	3.0f // how close do we need to be to the last sample to identify an LED
+
+
+
+struct View
+{
+	cv::Mat undistort_rectify_map_x;
+	cv::Mat undistort_rectify_map_y;
+
+	cv::Matx33d intrinsics;
+	cv::Mat distortion; // size may vary
+	cv::Vec4d distortion_fisheye;
+	bool use_fisheye;
+
+	std::vector<cv::KeyPoint> keypoints;
+
+	cv::Mat frame_undist_rectified;
+
+	void
+	populate_from_calib(t_camera_calibration &calib,
+	                    const RemapPair &rectification)
+	{
+		CameraCalibrationWrapper wrap(calib);
+		intrinsics = wrap.intrinsics_mat;
+		distortion = wrap.distortion_mat.clone();
+		distortion_fisheye = wrap.distortion_fisheye_mat;
+		use_fisheye = wrap.use_fisheye;
+
+		undistort_rectify_map_x = rectification.remap_x;
+		undistort_rectify_map_y = rectification.remap_y;
+	}
+};
+
+typedef enum led_tag
+{
+	TAG_NONE,
+	TAG_TL,
+	TAG_TR,
+	TAG_C,
+	TAG_BL,
+	TAG_BR,
+	TAG_SL,
+	TAG_SR
+} led_tag_t;
+
+typedef struct model_vertex
+{
+	int32_t vertex_index;
+	Eigen::Vector4f position;
+
+	led_tag_t tag;
+	bool active;
+
+	bool
+	operator<(const model_vertex &mv) const
+	{
+		return (vertex_index < mv.vertex_index);
+	}
+	bool
+	operator>(const model_vertex &mv) const
+	{
+		return (vertex_index > mv.vertex_index);
+	}
+
+} model_vertex_t;
+
+typedef struct match_data
+{
+	float angle;
+	float distance;
+	int32_t vertex_index;
+	Eigen::Vector4f position;
+} match_data_t;
+
+typedef struct match_vertex
+{
+	int32_t vertex_index;
+	std::vector<match_data_t> vertex_data;
+} match_vertex_t;
+
+
+typedef struct match_vertices
+{
+	std::vector<match_vertex_t> verts;
+} match_vertices_t;
+
+typedef struct match_model
+{
+	std::vector<match_data_t> measurements;
+} match_model_t;
+
 
 class TrackerPSVR
 {
 public:
+	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
 	struct xrt_tracked_psvr base = {};
 	struct xrt_frame_sink sink = {};
 	struct xrt_frame_node node = {};
@@ -49,16 +152,1082 @@ public:
 		struct xrt_vec3 pos = {};
 		struct xrt_quat rot = {};
 	} fusion;
+
+	struct
+	{
+		struct xrt_vec3 pos = {};
+		struct xrt_quat rot = {};
+	} optical;
+
+	Eigen::Quaternionf optical_rotation_correction;
+	Eigen::Quaternionf axis_alignment_rotation;
+	Eigen::Matrix4f corrected_imu_rotation;
+
+
+	model_vertex_t model_vertices[PSVR_NUM_LEDS];
+	std::vector<match_data_t> last_vertices;
+	cv::KalmanFilter track_filters[PSVR_NUM_LEDS];
+
+	View view[2];
+
+	bool calibrated;
+
+	cv::Mat disparity_to_depth;
+	cv::Vec3d r_cam_translation;
+	cv::Matx33d r_cam_rotation;
+
+	cv::Ptr<cv::SimpleBlobDetector> sbd;
+	std::vector<cv::KeyPoint> l_blobs, r_blobs;
+	std::vector<match_model_t> matches;
+
+	std::vector<cv::Point3f> world_points;
+
+	std::vector<Eigen::Vector4f> pruned_points;
+	std::vector<Eigen::Vector4f> merged_points;
+
+	std::vector<model_vertex_t> processed_points;
+	std::vector<match_data_t> match_vertices;
+
+	bool done_correction;
+
+
+	float model_scale_factor = 30.0f;
+	float outlier_thresh = 7.0f;
+	float merge_thresh = 0.5f;
+
+	FILE *dump_file;
+};
+
+static float
+dist_3d(Eigen::Vector4f a, Eigen::Vector4f b)
+{
+	return sqrt((a[0] - b[0]) * (a[0] - b[0]) +
+	            (a[1] - b[1]) * (a[1] - b[1]) +
+	            (a[2] - b[2]) * (a[2] - b[2]));
+}
+
+static void
+init_filter(cv::KalmanFilter &kf, float process_cov, float meas_cov, float dt)
+{
+	kf.init(6, 3);
+	kf.transitionMatrix =
+	    (cv::Mat_<float>(6, 6) << 1.0, 0.0, 0.0, dt, 0.0, 0.0, 0.0, 1.0,
+	     0.0, 0.0, dt, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, dt, 0.0, 0.0, 0.0, 1.0,
+	     0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+	     1.0);
+
+	cv::setIdentity(kf.measurementMatrix, cv::Scalar::all(1.0f));
+	cv::setIdentity(kf.errorCovPost, cv::Scalar::all(0.0f));
+
+	// our filter parameters set the process and measurement noise
+	// covariances.
+
+	cv::setIdentity(kf.processNoiseCov, cv::Scalar::all(process_cov));
+	cv::setIdentity(kf.measurementNoiseCov, cv::Scalar::all(meas_cov));
+}
+
+static void
+filter_predict(std::vector<match_data_t> *pose,
+               cv::KalmanFilter *filters,
+               float dt)
+{
+	for (uint32_t i = 0; i < PSVR_NUM_LEDS; i++) {
+		match_data_t current_led;
+		cv::KalmanFilter *current_kf = filters + i;
+
+		// set our dt components in the transition matrix
+		current_kf->transitionMatrix.at<float>(0, 3) = dt;
+		current_kf->transitionMatrix.at<float>(1, 4) = dt;
+		current_kf->transitionMatrix.at<float>(2, 5) = dt;
+
+		current_led.vertex_index = i;
+		// current_led->tag = (led_tag_t)(i + 1); // increment, as 0 is
+		// TAG_NONE
+		cv::Mat prediction = current_kf->predict();
+		current_led.position[0] = prediction.at<float>(0, 0);
+		current_led.position[1] = prediction.at<float>(1, 0);
+		current_led.position[2] = prediction.at<float>(2, 0);
+		pose->push_back(current_led);
+	}
+}
+
+static void
+filter_update(std::vector<match_data_t> *pose,
+              cv::KalmanFilter *filters,
+              float dt)
+{
+	for (uint32_t i = 0; i < PSVR_NUM_LEDS; i++) {
+		match_data_t *current_led = &pose->at(i);
+		cv::KalmanFilter *current_kf = filters + i;
+
+		// set our dt components in the transition matrix
+		current_kf->transitionMatrix.at<float>(0, 3) = dt;
+		current_kf->transitionMatrix.at<float>(1, 4) = dt;
+		current_kf->transitionMatrix.at<float>(2, 5) = dt;
+
+		current_led->vertex_index = i;
+		// current_led->tag =
+		//    (led_tag_t)(i + 1); // increment, as 0 is TAG_NONE
+		cv::Mat measurement = cv::Mat(3, 1, CV_32F);
+		measurement.at<float>(0, 0) = current_led->position[0];
+		measurement.at<float>(1, 0) = current_led->position[1];
+		measurement.at<float>(2, 0) = current_led->position[2];
+		current_kf->correct(measurement);
+	}
+}
+
+
+static bool
+match_possible(match_model_t *match)
+{
+
+	// check if this match makes sense e.g. that top is 'above' bottom, that
+	// 'left' is left of right check that unobservable combinations do not
+	// appear in the first n entries e.g. SR cannot be observed at the same
+	// time as SL
+	return true;
+}
+
+static void
+verts_to_measurement(std::vector<model_vertex_t> *meas_data,
+                     std::vector<match_data_t> *match_vertices)
+{
+	match_vertices->clear();
+	if (meas_data->size() < PSVR_OPTICAL_SOLVE_THRESH) {
+		for (uint32_t i = 0; i < meas_data->size(); i++) {
+			match_data_t md;
+			md.vertex_index = -1;
+			md.position = meas_data->at(i).position;
+			match_vertices->push_back(md);
+		}
+
+		return;
+	}
+
+	model_vertex_t ref_a = meas_data->at(0);
+	model_vertex_t ref_b = meas_data->at(1);
+	Eigen::Vector4f ref_vec = ref_b.position - ref_a.position;
+	float ref_len = dist_3d(ref_a.position, ref_b.position);
+
+	for (uint32_t i = 0; i < meas_data->size(); i++) {
+		model_vertex_t vp = meas_data->at(i);
+		Eigen::Vector4f point_vec = vp.position - ref_a.position;
+		match_data_t md;
+		md.vertex_index = -1;
+		md.position = vp.position;
+		Eigen::Vector3f ref_vec3 = ref_vec.head<3>();
+		Eigen::Vector3f point_vec3 = point_vec.head<3>();
+		Eigen::Vector3f vp_pos3 = vp.position.head<3>();
+
+		if (i != 0) {
+			Eigen::Vector3f plane_norm =
+			    ref_vec3.cross(point_vec3).normalized();
+			if (plane_norm.z() > 0) {
+				md.angle =
+				    -1 * acos(point_vec3.normalized().dot(
+				             ref_vec3.normalized()));
+			} else {
+				md.angle = acos(point_vec3.normalized().dot(
+				    ref_vec3.normalized()));
+			}
+
+			md.distance =
+			    dist_3d(vp.position, ref_a.position) / ref_len;
+		} else {
+			md.angle = 0.0f;
+			md.distance = 0.0f;
+		}
+		// fix up any NaNs
+		if (md.angle != md.angle) {
+			md.angle = 0.0f;
+		}
+		if (md.distance != md.distance) {
+			md.distance = 0.0f;
+		}
+
+		match_vertices->push_back(md);
+		printf("WAT: %f %f %d\n", md.distance, md.angle,
+		       md.vertex_index);
+	}
+}
+
+static float
+last_diff(TrackerPSVR &t,
+          std::vector<match_data_t> *meas_pose,
+          std::vector<match_data_t> *last_pose)
+{
+	float diff = 0.0f;
+	for (uint32_t i = 0; i < meas_pose->size(); i++) {
+		uint32_t last_index = meas_pose->at(i).vertex_index;
+		for (uint32_t j = 0; j < last_pose->size(); j++) {
+			if (last_pose->at(j).vertex_index == last_index) {
+				float d =
+				    fabs(dist_3d(meas_pose->at(i).position,
+				                 last_pose->at(j).position));
+				diff += d;
+			}
+		}
+	}
+	return diff / meas_pose->size();
+}
+
+
+static void
+remove_outliers(std::vector<cv::Point3f> *orig_points,
+                std::vector<Eigen::Vector4f> *pruned_points,
+                float outlier_thresh)
+{
+
+	if (orig_points->size() == 0) {
+		return;
+	}
+	// immediately prune anything that is measured as
+	// 'behind' the camera
+	std::vector<Eigen::Vector4f> temp_points;
+
+	for (uint32_t i = 0; i < orig_points->size(); i++) {
+		cv::Point3f p = orig_points->at(i);
+		if (p.z > 0) {
+			temp_points.push_back(
+			    Eigen::Vector4f(p.x, p.y, p.z, 1.0f));
+		}
+	}
+
+	if (temp_points.size() == 0) {
+		return;
+	}
+	std::vector<float> x_values;
+	std::vector<float> y_values;
+	std::vector<float> z_values;
+	for (uint32_t i = 0; i < temp_points.size(); i++) {
+		x_values.push_back(temp_points[i][0]);
+		y_values.push_back(temp_points[i][1]);
+		z_values.push_back(temp_points[i][2]);
+	}
+
+	std::nth_element(x_values.begin(),
+	                 x_values.begin() + x_values.size() / 2,
+	                 x_values.end());
+	float median_x = x_values[x_values.size() / 2];
+	std::nth_element(y_values.begin(),
+	                 y_values.begin() + y_values.size() / 2,
+	                 y_values.end());
+	float median_y = y_values[y_values.size() / 2];
+	std::nth_element(z_values.begin(),
+	                 z_values.begin() + z_values.size() / 2,
+	                 z_values.end());
+	float median_z = z_values[z_values.size() / 2];
+
+	for (uint32_t i = 0; i < temp_points.size(); i++) {
+		float error_x = temp_points[i][0] - median_x;
+		float error_y = temp_points[i][1] - median_y;
+		float error_z = temp_points[i][2] - median_z;
+
+		float rms_error =
+		    sqrt((error_x * error_x) + (error_y * error_y) +
+		         (error_z * error_z));
+
+		if (rms_error < outlier_thresh) {
+			pruned_points->push_back(temp_points[i]);
+		}
+	}
+}
+
+struct close_pair
+{
+	int index_a;
+	int index_b;
+};
+
+static void
+merge_close_points(std::vector<Eigen::Vector4f> *orig_points,
+                   std::vector<Eigen::Vector4f> *merged_points,
+                   float merge_thresh)
+{
+	std::vector<struct close_pair> pairs;
+	for (uint32_t i = 0; i < orig_points->size(); i++) {
+		for (uint32_t j = 0; j < orig_points->size(); j++) {
+			if (i != j) {
+				if (dist_3d(orig_points->at(i),
+				            orig_points->at(j)) <
+				    merge_thresh) {
+					struct close_pair p;
+					p.index_a = i;
+					p.index_b = j;
+					pairs.push_back(p);
+				}
+			}
+		}
+	}
+	std::vector<int> indices_to_remove;
+	for (uint32_t i = 0; i < pairs.size(); i++) {
+		if (pairs[i].index_a < pairs[i].index_b) {
+			indices_to_remove.push_back(pairs[i].index_a);
+		} else {
+			indices_to_remove.push_back(pairs[i].index_b);
+		}
+	}
+
+
+	for (uint32_t i = 0; i < orig_points->size(); i++) {
+		bool remove_index = false;
+		for (uint32_t j = 0; j < indices_to_remove.size(); j++) {
+			if (i == indices_to_remove[j]) {
+				remove_index = true;
+			}
+		}
+		if (!remove_index) {
+			merged_points->push_back(orig_points->at(i));
+		}
+	}
 };
 
 static void
-procces(TrackerPSVR &t, struct xrt_frame *xf)
+match_triangles(Eigen::Matrix4f *t1_mat,
+                Eigen::Matrix4f *t1_to_t2_mat,
+                Eigen::Vector4f t1_a,
+                Eigen::Vector4f t1_b,
+                Eigen::Vector4f t1_c,
+                Eigen::Vector4f t2_a,
+                Eigen::Vector4f t2_b,
+                Eigen::Vector4f t2_c)
+{
+	*t1_mat = Eigen::Matrix4f().Identity();
+	Eigen::Matrix4f t2_mat = Eigen::Matrix4f().Identity();
+
+	Eigen::Vector3f t1_x_vec = (t1_b - t1_a).head<3>().normalized();
+	Eigen::Vector3f t1_z_vec =
+	    (t1_c - t1_a).head<3>().cross((t1_b - t1_a).head<3>()).normalized();
+	Eigen::Vector3f t1_y_vec = t1_x_vec.cross(t1_z_vec).normalized();
+
+	Eigen::Vector3f t2_x_vec = (t2_b - t2_a).head<3>().normalized();
+	Eigen::Vector3f t2_z_vec =
+	    (t2_c - t2_a).head<3>().cross((t2_b - t2_a).head<3>()).normalized();
+	Eigen::Vector3f t2_y_vec = t2_x_vec.cross(t2_z_vec).normalized();
+
+	t1_mat->col(0) << t1_x_vec[0], t1_x_vec[1], t1_x_vec[2], 0.0f;
+	t1_mat->col(1) << t1_y_vec[0], t1_y_vec[1], t1_y_vec[2], 0.0f;
+	t1_mat->col(2) << t1_z_vec[0], t1_z_vec[1], t1_z_vec[2], 0.0f;
+	t1_mat->col(3) << t1_a[0], t1_a[1], t1_a[2], 1.0f;
+
+	t2_mat.col(0) << t2_x_vec[0], t2_x_vec[1], t2_x_vec[2], 0.0f;
+	t2_mat.col(1) << t2_y_vec[0], t2_y_vec[1], t2_y_vec[2], 0.0f;
+	t2_mat.col(2) << t2_z_vec[0], t2_z_vec[1], t2_z_vec[2], 0.0f;
+	t2_mat.col(3) << t2_a[0], t2_a[1], t2_a[2], 1.0f;
+
+	// std::cout << "T1_MAT: \n" << *t1_mat << "\n";
+	// std::cout << "T2_MAT: \n" << t2_mat << "\n";
+
+	*t1_to_t2_mat = t1_mat->inverse() * t2_mat;
+}
+
+static Eigen::Matrix4f
+solve_for_measurement(TrackerPSVR *t,
+                      std::vector<match_data_t> *measurement,
+                      std::vector<match_data_t> *solved)
+{
+
+	Eigen::Matrix4f tri_basis;
+	Eigen::Matrix4f model_to_measurement;
+
+	Eigen::Vector4f meas_ref_a = measurement->at(0).position;
+	Eigen::Vector4f meas_ref_b = measurement->at(1).position;
+	int meas_index_a = measurement->at(0).vertex_index;
+	int meas_index_b = measurement->at(1).vertex_index;
+
+	Eigen::Vector4f model_ref_a = t->model_vertices[meas_index_a].position;
+	Eigen::Vector4f model_ref_b = t->model_vertices[meas_index_b].position;
+
+	float highest_length = 0.0f;
+	int best_model_index = 0;
+	int most_distant_index = 0;
+	printf("MEASUREMENT SIZE: %d\n", measurement->size());
+	for (uint32_t i = 0; i < measurement->size(); i++) {
+		int model_tag_index = measurement->at(i).vertex_index;
+		Eigen::Vector4f model_vert =
+		    t->model_vertices[model_tag_index].position;
+		if (most_distant_index > 1 &&
+		    dist_3d(model_vert, model_ref_a) > highest_length) {
+			best_model_index = most_distant_index;
+		}
+		most_distant_index++;
+	}
+
+	Eigen::Vector4f meas_ref_c = measurement->at(best_model_index).position;
+	int meas_index_c = measurement->at(best_model_index).vertex_index;
+
+	Eigen::Vector4f model_ref_c = t->model_vertices[meas_index_c].position;
+
+	match_triangles(&tri_basis, &model_to_measurement, model_ref_a,
+	                model_ref_b, model_ref_c, meas_ref_a, meas_ref_b,
+	                meas_ref_c);
+	Eigen::Matrix4f model_center_transform_f =
+	    tri_basis * model_to_measurement * tri_basis.inverse();
+
+	// now reverse the order of our verts to get a more accurate estimate.
+
+	meas_ref_a = measurement->at(measurement->size() - 1).position;
+	meas_ref_b = measurement->at(measurement->size() - 2).position;
+	meas_index_a = measurement->at(measurement->size() - 1).vertex_index;
+	meas_index_b = measurement->at(measurement->size() - 2).vertex_index;
+
+	model_ref_a = t->model_vertices[meas_index_a].position;
+	model_ref_b = t->model_vertices[meas_index_b].position;
+
+	highest_length = 0.0f;
+	best_model_index = 0;
+	most_distant_index = 0;
+
+	for (uint32_t i = 0; i < measurement->size(); i++) {
+		int model_tag_index = measurement->at(i).vertex_index;
+		Eigen::Vector4f model_vert =
+		    t->model_vertices[model_tag_index].position;
+		if (most_distant_index < measurement->size() - 2 &&
+		    dist_3d(model_vert, model_ref_a) > highest_length) {
+			best_model_index = most_distant_index;
+		}
+		most_distant_index++;
+	}
+
+	meas_ref_c = measurement->at(best_model_index).position;
+	meas_index_c = measurement->at(best_model_index).vertex_index;
+
+	model_ref_c = t->model_vertices[meas_index_c].position;
+
+	match_triangles(&tri_basis, &model_to_measurement, model_ref_a,
+	                model_ref_b, model_ref_c, meas_ref_a, meas_ref_b,
+	                meas_ref_c);
+	Eigen::Matrix4f model_center_transform_r =
+	    tri_basis * model_to_measurement * tri_basis.inverse();
+
+	// decompose our transforms and slerp
+	Eigen::Matrix3f r = model_center_transform_f.block(0, 0, 3, 3);
+	Eigen::Quaternionf f_rot_part = Eigen::Quaternionf(r);
+	r = model_center_transform_r.block(0, 0, 3, 3);
+	Eigen::Quaternionf r_rot_part = Eigen::Quaternionf(r);
+	Eigen::Vector4f f_trans_part_f = model_center_transform_f.col(3);
+	Eigen::Vector4f f_trans_part_r = model_center_transform_r.col(3);
+
+	Eigen::Matrix4f trans = Eigen::Matrix4f().Identity();
+	trans.block(0, 0, 3, 3) =
+	    f_rot_part.slerp(0.5, r_rot_part).toRotationMatrix();
+	trans.col(3) = (f_trans_part_f + f_trans_part_r) / 2.0f;
+
+	printf("F TRANS: %f %f %f R_TRANS: %f %f %f\n", f_trans_part_f.x(),
+	       f_trans_part_f.y(), f_trans_part_f.z(), f_trans_part_r.x(),
+	       f_trans_part_r.y(), f_trans_part_r.z());
+
+	solved->clear();
+	for (uint32_t i = 0; i < PSVR_NUM_LEDS; i++) {
+		match_data_t md;
+		md.vertex_index = i;
+		md.position = trans * t->model_vertices[i].position;
+		solved->push_back(md);
+		printf("MEAS SOLVED VERT: %d %f %f %f\n", i, md.position.x(),
+		       md.position.y(), md.position.z());
+	}
+	Eigen::Matrix4f pose = trans;
+	// std::cout << "MEAS POSE:\n" << pose << "\n";
+
+	return pose;
+}
+
+typedef struct proximity_data
+{
+	Eigen::Vector4f position;
+	float lowest_distance;
+	int vertex_index;
+} proximity_data_t;
+
+static Eigen::Matrix4f
+solve_with_imu(TrackerPSVR &t,
+               std::vector<match_data_t> *measurements,
+               std::vector<match_data_t> *match_measurements,
+               std::vector<match_data_t> *solved,
+               float search_radius)
+{
+	std::vector<proximity_data_t> proximity_data;
+	printf("IMU measurements %d last measurements: %d\n",
+	       measurements->size(), match_measurements->size());
+	for (uint32_t i = 0; i < measurements->size(); i++) {
+		float lowest_distance = 65535.0;
+		int closest_index = 0;
+		proximity_data_t p;
+		match_data_t measurement = measurements->at(i);
+		if (measurement.vertex_index ==
+		    -1) { // we are closest-matching this vertex
+			printf("closest matching vert %d\n", i);
+			for (uint32_t j = 0; j < match_measurements->size();
+			     j++) {
+				match_data_t match_measurement =
+				    match_measurements->at(j);
+				float distance =
+				    dist_3d(measurement.position,
+				            match_measurement.position);
+				printf("dist: %f %f %f %f : %f %f %f\n",
+				       distance, measurement.position.x(),
+				       measurement.position.y(),
+				       measurement.position.z(),
+				       match_measurement.position.x(),
+				       match_measurement.position.y(),
+				       match_measurement.position.z());
+				if (distance < lowest_distance) {
+					lowest_distance = distance;
+					closest_index =
+					    match_measurement.vertex_index;
+				}
+			}
+			if (lowest_distance < search_radius) {
+				p.position = measurement.position;
+				p.vertex_index = closest_index;
+				p.lowest_distance = lowest_distance;
+				proximity_data.push_back(p);
+			}
+		} else {
+			p.position = measurement.position;
+			p.vertex_index = measurement.vertex_index;
+			p.lowest_distance = 0.0f;
+			proximity_data.push_back(p);
+		}
+	}
+
+	if (proximity_data.size() > 0) {
+
+		std::vector<match_model_t> temp_measurement_list;
+		for (uint32_t i = 0; i < proximity_data.size(); i++) {
+			proximity_data_t p = proximity_data[i];
+			Eigen::Vector4f model_vertex =
+			    t.model_vertices[p.vertex_index].position;
+			Eigen::Vector4f measurement_vertex = p.position;
+			Eigen::Vector4f measurement_offset =
+			    t.corrected_imu_rotation * model_vertex;
+			Eigen::Affine3f translation(Eigen::Translation3f(
+			    (measurement_vertex - measurement_offset)
+			        .head<3>()));
+			Eigen::Matrix4f model_to_measurement =
+			    translation.matrix() * t.corrected_imu_rotation;
+			match_model_t temp_measurement;
+			for (uint32_t j = 0; j < PSVR_NUM_LEDS; j++) {
+				match_data_t md;
+				md.position = model_to_measurement *
+				              t.model_vertices[j].position;
+				md.vertex_index = j;
+				temp_measurement.measurements.push_back(md);
+			}
+			temp_measurement_list.push_back(temp_measurement);
+		}
+		for (uint32_t i = 0; i < PSVR_NUM_LEDS; i++) {
+			match_data_t avg_data;
+			avg_data.position =
+			    Eigen::Vector4f(0.0f, 0.0f, 0.0f, 1.0f);
+			for (uint32_t j = 0; j < temp_measurement_list.size();
+			     j++) {
+				avg_data.position += temp_measurement_list[j]
+				                         .measurements[i]
+				                         .position;
+			}
+			avg_data.position /=
+			    float(temp_measurement_list.size());
+			avg_data.vertex_index = i;
+			solved->push_back(avg_data);
+			printf("IMU SOLVED VERT: %d %f %f %f\n", i,
+			       avg_data.position.x(), avg_data.position.y(),
+			       avg_data.position.z());
+		}
+		Eigen::Vector3f center_pos =
+		    (solved->at(PSVR_MODEL_CENTER_INDEX).position).head<3>();
+		Eigen::Translation3f center_trans(center_pos);
+		Eigen::Affine3f translation(center_trans);
+		Eigen::Matrix4f pose =
+		    translation.matrix() * t.corrected_imu_rotation;
+		// std::cout << "IMU POSE:\n" << pose << "\n";
+		return pose;
+	}
+	printf("RETURNING IDENTITY\n");
+	return Eigen::Matrix4f().Identity();
+}
+
+
+static Eigen::Matrix4f
+disambiguate(TrackerPSVR &t,
+             std::vector<match_data_t> *measured_points,
+             std::vector<match_data_t> *last_measurement,
+             std::vector<match_data_t> *solved,
+             uint32_t frame_no)
+{
+
+	if (measured_points->size() < PSVR_OPTICAL_SOLVE_THRESH &&
+	    last_measurement->size() > 0) {
+		printf("Under threshold (%d points): SOLVING WITH IMU\n",
+		       measured_points->size());
+		return solve_with_imu(t, measured_points, last_measurement,
+		                      solved, PSVR_SEARCH_RADIUS);
+	}
+
+	if (measured_points->size() < 3) {
+		printf("Under 3 points: SOLVING WITH IMU\n");
+		// equivalent to returning null, but better than crashing
+		return solve_with_imu(t, measured_points, last_measurement,
+		                      solved, PSVR_SEARCH_RADIUS);
+	}
+
+	float lowestError = 65535.0f;
+	int32_t bestModel = -1;
+	uint32_t matched_vertex_indices[PSVR_NUM_LEDS];
+	for (uint32_t i = 0; i < t.matches.size(); i++) {
+		match_model_t m = t.matches[i];
+
+		float squaredSum = 0.0f;
+		float signDiff = 0.0f;
+
+		// we have 2 measurements per vertex (distance and angle)
+		// and we are comparing only the 'non-basis vector' elements
+
+		// fill in our 'proposed' vertex indices from the model data
+		// (this will be overwritten once our best model is selected
+		for (uint32_t i = 0; i < measured_points->size(); i++) {
+			measured_points->at(i).vertex_index =
+			    m.measurements.at(i).vertex_index;
+		}
+
+		float ld = last_diff(t, measured_points, &t.last_vertices);
+		// printf("last diff: model %d %f\n", i, ld);
+
+		float tl_y, tr_y, bl_y, br_y = -65535.0f;
+		bool ignore = false;
+		for (uint32_t j = 1; j < measured_points->size(); j++) {
+
+
+			// reject anything where TL ends
+			// up below BL
+			// or TR ends up below BR
+			if (measured_points->at(j).vertex_index == 3) { // TL -
+				// TODO: use tags
+
+				tl_y = measured_points->at(j).position.y();
+			}
+			if (measured_points->at(j).vertex_index == 0) {
+				// BL -
+				// TODO: use tags
+				bl_y = measured_points->at(j).position.y();
+			}
+			if (measured_points->at(j).vertex_index == 4) { // TR -
+				// TODO: use tags
+				tr_y = measured_points->at(j).position.y();
+			}
+			if (measured_points->at(j).vertex_index == 1) { // BR -
+				// TODO: use tags
+				br_y = measured_points->at(j).position.y();
+			}
+
+
+			if (tl_y != -65535.0f and bl_y != -65535.0f) {
+				if (tl_y > bl_y) {
+					// ignore = true;
+				}
+			}
+			if (tr_y != -65535.0f and br_y != -65535.0f) {
+				if (tr_y > br_y) {
+					// ignore = true;
+				}
+			}
+
+			// if the distance is significantly different (~0.3
+			// difference, discard this)
+			float dist = fabs(measured_points->at(j).distance -
+			                  m.measurements.at(j).distance);
+			if (dist > PSVR_DISAMBIG_REJECT_DIST) {
+				squaredSum += 50.0f;
+			} else {
+
+
+				squaredSum += fabs(
+				    measured_points->at(j).distance -
+				    m.measurements.at(j)
+				        .distance); // * (measured_points->at(j).distance - m.data.at(j).distance);
+			}
+
+			// if the angle is significantly different (~0.3
+			// difference, discard this)
+			float angdiff = fabs(measured_points->at(j).angle -
+			                     m.measurements.at(j).angle);
+			if (angdiff > 0.3f) {
+				squaredSum += 50.0f;
+			} else {
+
+				squaredSum += fabs(
+				    measured_points->at(j).angle -
+				    m.measurements.at(j)
+				        .angle); // * (measured_points->at(j).angle
+				                 // - m.data.at(j).angle);
+			}
+			// int vi = measured_points->at(j).vertex_index;
+			// Eigen::Vector4f lastPos =
+			//    last_measurement->at(vi).position;
+			// Eigen::Vector4f thisPos =
+			//   measured_points->at(j).position;
+			// squaredSum += dist_3d(thisPos, lastPos);
+
+			// differing signs should heavily discourage use
+
+			if (copysign(1, measured_points->at(j).distance) !=
+			    copysign(1, m.measurements.at(j).distance)) {
+				// squaredSum += 50.0f;
+			}
+			if (copysign(1, measured_points->at(j).angle) !=
+			    copysign(1, m.measurements.at(j).angle)) {
+				// squaredSum += 50.0f;
+			}
+
+			// printf("%d squaredSum: %f\n",j,squaredSum);
+		}
+		float rmsError =
+		    squaredSum /
+		    measured_points->size(); //+ ld; // sqrt(squaredSum);
+		// printf("ERROR: %f\n", rmsError);
+		if (rmsError <= lowestError && !ignore) {
+			lowestError = rmsError;
+			bestModel = i;
+			for (uint32_t i = 0; i < measured_points->size(); i++) {
+				matched_vertex_indices[i] =
+				    measured_points->at(i).vertex_index;
+			}
+		}
+	}
+	printf("ERR: %f BEST: %d\n", lowestError, bestModel);
+	if (bestModel == -1) {
+		return Eigen::Matrix4f().Identity();
+	}
+
+	// printf("model %d:\t",bestModel);
+	match_model_t m = t.matches[bestModel];
+
+	for (uint32_t i = 0; i < m.measurements.size(); i++) {
+		printf("BEST MATCH INDEX: %d DIST: %f ANGLE: %f\n",
+		       matched_vertex_indices[i], m.measurements[i].distance,
+		       m.measurements[i].angle);
+	}
+	// printf("\n");
+
+	// printf("meas  ?:\t");
+	for (uint32_t i = 0; i < measured_points->size(); i++) {
+		// printf("? %f %f
+		// ",measured_points->at(i).distance,measured_points->at(i).angle);
+	}
+	// printf("\n");
+
+
+
+	for (uint32_t i = 0; i < measured_points->size(); i++) {
+		measured_points->at(i).vertex_index = matched_vertex_indices[i];
+		printf("measured point %d -> vertex_index %d  \n ", i,
+		       measured_points->at(i).vertex_index);
+	}
+	printf("SOLVING WITH OPTICAL\n");
+	return solve_for_measurement(&t, measured_points, solved);
+}
+
+static void
+create_model(TrackerPSVR &t)
+{
+	t.model_vertices[0] = {
+	    0, Eigen::Vector4f(-2.51408f, 3.77113f, 0.0f, 1.0f), TAG_BL, true};
+	t.model_vertices[1] = {
+	    1, Eigen::Vector4f(-2.51408f, -3.77113f, 0.0f, 1.0f), TAG_BR, true};
+	t.model_vertices[2] = {2, Eigen::Vector4f(0.0f, 0.0f, -1.54926f, 1.0f),
+	                       TAG_C, true};
+	t.model_vertices[3] = {
+	    3, Eigen::Vector4f(2.51408f, 3.77113f, 0.0f, 1.0f), TAG_TL, true};
+	t.model_vertices[4] = {
+	    4, Eigen::Vector4f(2.51408f, -3.77113f, 0.0f, 1.0f), TAG_TR, true};
+	t.model_vertices[5] = {
+	    5, Eigen::Vector4f(0.0, 4.52535f, 2.62887f, 1.0f), TAG_SL, true};
+	t.model_vertices[6] = {
+	    6, Eigen::Vector4f(0.0, -4.52535f, 2.62887f, 1.0f), TAG_SR, true};
+}
+
+
+
+static void
+create_match_list(TrackerPSVR &t)
+{
+	// create our permutation list
+	for (auto &&vec : iter::permutations(t.model_vertices)) {
+		match_model_t m;
+
+		model_vertex_t ref_pt_a = vec[0];
+		model_vertex_t ref_pt_b = vec[1];
+		Eigen::Vector3f ref_vec3 =
+		    (ref_pt_b.position - ref_pt_a.position).head<3>();
+
+		float normScale = dist_3d(ref_pt_a.position, ref_pt_b.position);
+
+		match_data_t md;
+		for (auto &&i : vec) {
+			Eigen::Vector3f point_vec3 =
+			    (i.position - ref_pt_a.position).head<3>();
+			md.vertex_index = i.vertex_index;
+			md.distance =
+			    dist_3d(i.position, ref_pt_a.position) / normScale;
+			if (i.position.head<3>().dot(
+			        Eigen::Vector3f(0.0, 0.0, 1.0f)) < 0) {
+				md.distance *= -1;
+			}
+
+			Eigen::Vector3f plane_norm =
+			    ref_vec3.cross(point_vec3).normalized();
+			if (ref_pt_a.position != i.position) {
+
+				if (plane_norm.normalized().z() > 0) {
+					md.angle =
+					    -1 *
+					    acos(
+					        (point_vec3)
+					            .normalized()
+					            .dot(
+					                ref_vec3.normalized()));
+				} else {
+					md.angle =
+					    acos(point_vec3.normalized().dot(
+					        ref_vec3.normalized()));
+				}
+			} else {
+				md.angle = 0.0f;
+			}
+			// fix up any NaNs
+			if (md.angle != md.angle) {
+				md.angle = 0.0f;
+			}
+			if (md.distance != md.distance) {
+				md.distance = 0.0f;
+			}
+
+			m.measurements.push_back(md);
+		}
+		if (match_possible(&m)) {
+			t.matches.push_back(m);
+		}
+	}
+}
+
+static void
+do_view(TrackerPSVR &t, View &view, cv::Mat &grey)
+{
+	// Undistort and rectify the whole image.
+	cv::remap(grey,                         // src
+	          view.frame_undist_rectified,  // dst
+	          view.undistort_rectify_map_x, // map1
+	          view.undistort_rectify_map_y, // map2
+	          cv::INTER_LINEAR,             // interpolation
+	          cv::BORDER_CONSTANT,          // borderMode
+	          cv::Scalar(0, 0, 0));         // borderValue
+
+	cv::threshold(view.frame_undist_rectified, // src
+	              view.frame_undist_rectified, // dst
+	              32.0,                        // thresh
+	              255.0,                       // maxval
+	              0);
+
+	// type
+
+	// tracker_measurement_t m = {};
+
+	// Do blob detection with our masks.
+	//! @todo Re-enable masks.
+
+	t.sbd->detect(view.frame_undist_rectified, // image
+	              view.keypoints,              // keypoints
+	              cv::noArray());              // mask
+}
+
+static void
+process(TrackerPSVR &t, struct xrt_frame *xf)
 {
 	// Only IMU data
 	if (xf == NULL) {
 		return;
 	}
+	float dt = 1.0f;
+	// get our predicted filtered led positions, if any
+	std::vector<match_data_t> predicted_pose;
+	filter_predict(&predicted_pose, t.track_filters, dt);
+
+
+	model_vertex_t measured_pose[PSVR_NUM_LEDS];
+
+	// get our raw measurements
+
+	t.view[0].keypoints.clear();
+	t.view[1].keypoints.clear();
+	t.l_blobs.clear();
+	t.r_blobs.clear();
+	t.world_points.clear();
+
+	int cols = xf->width / 2;
+	int rows = xf->height;
+	int stride = xf->stride;
+
+	cv::Mat l_grey(rows, cols, CV_8UC1, xf->data, stride);
+	cv::Mat r_grey(rows, cols, CV_8UC1, xf->data + cols, stride);
+
+
+	do_view(t, t.view[0], l_grey);
+	do_view(t, t.view[1], r_grey);
+
+	// do some basic matching to come up with likely
+	// disparity-pairs.
+
+	for (uint32_t i = 0; i < t.view[0].keypoints.size(); i++) {
+		cv::KeyPoint l_blob = t.view[0].keypoints[i];
+		int l_index = -1;
+		int r_index = -1;
+
+		for (uint32_t j = 0; j < t.view[1].keypoints.size(); j++) {
+			float lowest_dist = 128;
+			cv::KeyPoint r_blob = t.view[1].keypoints[j];
+			// find closest point on same-ish scanline
+			if ((l_blob.pt.y < r_blob.pt.y + 3) &&
+			    (l_blob.pt.y > r_blob.pt.y - 3) &&
+			    ((r_blob.pt.x - l_blob.pt.x) < lowest_dist)) {
+				lowest_dist = r_blob.pt.x - l_blob.pt.x;
+				r_index = j;
+				l_index = i;
+			}
+		}
+
+		if (l_index > -1 && r_index > -1) {
+			t.l_blobs.push_back(t.view[0].keypoints.at(l_index));
+			t.r_blobs.push_back(t.view[1].keypoints.at(r_index));
+		}
+	}
+
+	// Convert our 2d point + disparities into 3d points.
+	if (t.l_blobs.size() > 0) {
+		for (uint32_t i = 0; i < t.l_blobs.size(); i++) {
+			float disp = t.r_blobs[i].pt.x - t.l_blobs[i].pt.x;
+			cv::Vec4d xydw(t.l_blobs[i].pt.x, t.l_blobs[i].pt.y,
+			               disp, 1.0f);
+			// Transform
+			cv::Vec4d h_world =
+			    (cv::Matx44d)t.disparity_to_depth * xydw;
+
+			// Divide by scale to get 3D vector from
+			// homogeneous coordinate.
+			t.world_points.push_back(
+			    cv::Point3f(h_world[0] / h_world[3],
+			                h_world[1] / h_world[3],
+			                -1 * (h_world[2] / h_world[3])) *
+			    t.model_scale_factor);
+		}
+	}
 
+	// raw debug output for Blender algo development
+	for (int i = 0; i < t.world_points.size(); i++) {
+		cv::Point3f unscaled = t.world_points[i] / t.model_scale_factor;
+		fprintf(t.dump_file, "P,%" PRIu64 ", %f,%f,%f\n",
+		        xf->source_sequence, unscaled.x, unscaled.y,
+		        unscaled.z);
+	}
+
+	t.pruned_points.clear();
+	t.merged_points.clear();
+	t.processed_points.clear();
+
+	// remove outliers from our measurement list
+	printf("WORLD POINTS %d\n", t.world_points.size());
+	remove_outliers(&t.world_points, &t.pruned_points, t.outlier_thresh);
+	printf("PRUNED POINTS %d\n", t.pruned_points.size());
+
+	merge_close_points(&t.pruned_points, &t.merged_points, t.merge_thresh);
+	printf("MERGED POINTS %d\n", t.merged_points.size());
+
+	// try matching our leds against the predictions
+	// if error low, fit model using raw and filtered positions
+	if (t.merged_points.size() < PSVR_NUM_LEDS) {
+		for (uint32_t i = 0; i < t.merged_points.size(); i++) {
+			model_vertex_t mv;
+			mv.position = t.merged_points[i];
+			t.processed_points.push_back(mv);
+			printf("INPUT: %f %f %f\n", mv.position.x(),
+			       mv.position.y(), mv.position.z());
+		}
+	} else {
+		printf("Too many blobs to be a PSVR! %ld\n",
+		       t.processed_points.size());
+	}
+	fprintf(t.dump_file, "\n");
+
+	// convert our points to match data, and disambiguate it, this
+	// tags our match_vertices with everything we need to solve the
+	// pose.
+	verts_to_measurement(&t.processed_points, &t.match_vertices);
+
+	std::vector<match_data_t> solved;
+	Eigen::Matrix4f model_center_transform =
+	    disambiguate(t, &t.match_vertices, &predicted_pose, &solved, 0);
+
+	t.last_vertices.clear();
+	for (uint32_t i = 0; i < solved.size(); i++) {
+		t.last_vertices.push_back(solved[i]);
+		fprintf(t.dump_file, "S,%" PRIu64 ",%f,%f,%f\n",
+		        xf->source_sequence, solved[i].position.x(),
+		        solved[i].position.y(), solved[i].position.z());
+	}
+	fprintf(t.dump_file, "\n");
+
+	if (t.last_vertices.size() > 0) {
+		filter_update(&t.last_vertices, t.track_filters, dt);
+	}
+
+
+	std::vector<match_data_t> f_solved;
+	Eigen::Matrix4f f_pose =
+	    solve_with_imu(t, &t.match_vertices, &predicted_pose, &f_solved,
+	                   PSVR_SEARCH_RADIUS);
+
+	/*for (uint32_t i = 0; i < f_solved.size(); i++) {
+		fprintf(t.dump_file, "S,%" PRIu64 ",%f,%f,%f\n",
+			xf->source_sequence, f_solved[i].position.x(),
+			f_solved[i].position.y(), f_solved[i].position.z());
+	}
+	fprintf(t.dump_file, "\n");
+*/
+	Eigen::Matrix3f r = model_center_transform.block(0, 0, 3, 3);
+	Eigen::Quaternionf rot(r);
+
+	if (t.processed_points.size() == 5 && !t.done_correction) {
+		printf("ADJUSTING CORRECTION\n");
+		t.optical_rotation_correction =
+		    (t.axis_alignment_rotation *
+		     Eigen::Quaternionf(t.fusion.rot.w, t.fusion.rot.x,
+		                        t.fusion.rot.y, t.fusion.rot.z))
+		        .inverse() *
+		    rot;
+		// t.done_correction = true;
+	}
+
+	// rot = t.axis_alignment_rotation * rot;
+
+	t.optical.rot.x = rot.x();
+	t.optical.rot.y = rot.y();
+	t.optical.rot.z = rot.z();
+	t.optical.rot.w = rot.w();
+
+
+	float inv_model_scale_factor = 1.0f / t.model_scale_factor;
+
+	t.optical.pos.x =
+	    model_center_transform(0, 3) * inv_model_scale_factor; // row-first
+	t.optical.pos.y = model_center_transform(1, 3) * inv_model_scale_factor;
+	t.optical.pos.z = model_center_transform(2, 3) * inv_model_scale_factor;
+
+	printf("POS: %f %f %f\n", t.optical.pos.x, t.optical.pos.y,
+	       t.optical.pos.z);
+	printf("OPTICAL ROT: %f %f %f %f\n", t.optical.rot.x, t.optical.rot.y,
+	       t.optical.rot.z, t.optical.rot.w);
+
+
+	printf("FRAME: %ld\n", xf->source_sequence);
 	xrt_frame_reference(&xf, NULL);
 }
 
@@ -79,17 +1248,17 @@ run(TrackerPSVR &t)
 			break;
 		}
 
-		// Take a reference on the current frame, this keeps it alive
-		// if it is replaced during the consumer processing it, but
-		// we no longer need to hold onto the frame on the queue we
-		// just move the pointer.
+		// Take a reference on the current frame, this keeps it
+		// alive if it is replaced during the consumer
+		// processing it, but we no longer need to hold onto the
+		// frame on the queue we just move the pointer.
 		frame = t.frame;
 		t.frame = NULL;
 
 		// Unlock the mutex when we do the work.
 		os_thread_helper_unlock(&t.oth);
 
-		procces(t, frame);
+		process(t, frame);
 
 		// Have to lock it again.
 		os_thread_helper_lock(&t.oth);
@@ -111,10 +1280,11 @@ get_pose(TrackerPSVR &t,
 		return;
 	}
 
-	out_relation->pose.position = t.fusion.pos;
-	out_relation->pose.orientation = t.fusion.rot;
+	out_relation->pose.position = t.optical.pos;
+	out_relation->pose.orientation = t.optical.rot;
 
-	//! @todo assuming that orientation is actually currently tracked.
+	//! @todo assuming that orientation is actually currently
+	//! tracked.
 	out_relation->relation_flags = (enum xrt_space_relation_flags)(
 	    XRT_SPACE_RELATION_POSITION_VALID_BIT |
 	    XRT_SPACE_RELATION_POSITION_TRACKED_BIT |
@@ -143,6 +1313,24 @@ imu_data(TrackerPSVR &t,
 		math_quat_integrate_velocity(
 		    &t.fusion.rot, &sample->gyro_rad_secs, dt, &t.fusion.rot);
 	}
+	fprintf(t.dump_file, "I,%" PRIu64 ", %f,%f,%f,%f\n\n", timestamp_ns,
+	        t.fusion.rot.x, t.fusion.rot.y, t.fusion.rot.z, t.fusion.rot.w);
+
+	Eigen::Matrix4f corrected_rot = Eigen::Matrix4f().Identity();
+	corrected_rot.block(0, 0, 3, 3) =
+	    (t.axis_alignment_rotation *
+	     Eigen::Quaternionf(t.fusion.rot.w, t.fusion.rot.x, t.fusion.rot.y,
+	                        t.fusion.rot.z) *
+	     t.optical_rotation_correction)
+	        .toRotationMatrix();
+
+	/*fusion_rot.block(0, 0, 3, 3) =
+	    t.axis_alignment_rotation *
+	    Eigen::Quaternionf(t.fusion.rot.w, t.fusion.rot.x, t.fusion.rot.y,
+			       t.fusion.rot.z)
+		.toRotationMatrix();*/
+
+	t.corrected_imu_rotation = corrected_rot;
 	t.last_imu = timestamp_ns;
 
 	os_thread_helper_unlock(&t.oth);
@@ -251,6 +1439,7 @@ t_psvr_start(struct xrt_tracked_psvr *xtvr)
 	auto &t = *container_of(xtvr, TrackerPSVR, base);
 	int ret;
 
+
 	ret = os_thread_helper_start(&t.oth, t_psvr_run, &t);
 	if (ret != 0) {
 		return ret;
@@ -270,6 +1459,49 @@ t_psvr_create(struct xrt_frame_context *xfctx,
 	auto &t = *(new TrackerPSVR());
 	int ret;
 
+	for (uint32_t i = 0; i < PSVR_NUM_LEDS; i++) {
+		init_filter(t.track_filters[i], 0.09f, 3.0f, 1.0f);
+	}
+
+	StereoRectificationMaps rectify(data);
+	t.view[0].populate_from_calib(data->view[0], rectify.view[0].rectify);
+	t.view[1].populate_from_calib(data->view[1], rectify.view[1].rectify);
+	t.disparity_to_depth = rectify.disparity_to_depth_mat;
+	StereoCameraCalibrationWrapper wrapped(data);
+	t.r_cam_rotation = wrapped.camera_rotation_mat;
+	t.r_cam_translation = wrapped.camera_translation_mat;
+	t.calibrated = true;
+
+	// clang-format off
+    cv::SimpleBlobDetector::Params blob_params;
+    blob_params.filterByArea = false;
+    blob_params.filterByConvexity = false;
+    blob_params.filterByInertia = false;
+    blob_params.filterByColor = true;
+    blob_params.blobColor = 255; // 0 or 255 - color comes from binarized image?
+    blob_params.minArea = 1;
+    blob_params.maxArea = 1000;
+    blob_params.maxThreshold = 51; // using a wide threshold span slows things down bigtime
+    blob_params.minThreshold = 50;
+    blob_params.thresholdStep = 1;
+    blob_params.minDistBetweenBlobs = 5;
+    blob_params.minRepeatability = 1; // need this to avoid error?
+        // clang-format on
+
+        t.sbd = cv::SimpleBlobDetector::create(blob_params);
+
+	t.corrected_imu_rotation = Eigen::Matrix4f().Identity();
+	t.axis_alignment_rotation = Eigen::Quaternionf(1.0f, 0.0f, 0.0f, 0.0f);
+	/*t.axis_alignment_rotation =
+	    Eigen::AngleAxis<float>(-M_PI / 2.0,
+				    Eigen::Vector3f(0.0f, 0.0f, 1.0f)) *
+	    Eigen::AngleAxis<float>(M_PI, Eigen::Vector3f(1.0f, 0.0f, 0.0f)) *
+	    Eigen::AngleAxis<float>(0, Eigen::Vector3f(0.0f, 1.0f, 0.0f));
+*/
+
+	create_model(t);
+	create_match_list(t);
+
 	t.base.get_tracked_pose = t_psvr_get_tracked_pose;
 	t.base.push_imu = t_psvr_push_imu;
 	t.base.destroy = t_psvr_fake_destroy;
@@ -299,5 +1531,7 @@ t_psvr_create(struct xrt_frame_context *xfctx,
 	*out_sink = &t.sink;
 	*out_xtvr = &t.base;
 
+	t.dump_file = fopen("/tmp/psvr_dump.txt", "w");
+
 	return 0;
 }
diff --git a/src/xrt/auxiliary/util/u_render_timing.c b/src/xrt/auxiliary/util/u_render_timing.c
index 9e630b796ddc4a5606a29e1039d0e319741b5d92..15e164a9a7cfc29a8a02583c4bc532f3f22d7b02 100644
--- a/src/xrt/auxiliary/util/u_render_timing.c
+++ b/src/xrt/auxiliary/util/u_render_timing.c
@@ -37,10 +37,15 @@ get_last_input_plus_period_at_least_greater_then(struct u_rt_helper *urth,
 {
 	uint64_t val = urth->last_input;
 
+	if (urth->period == 0) {
+		return then_ns;
+	}
+
 	while (val <= then_ns) {
 		val += urth->period;
 	}
 
+
 	return val;
 }
 
diff --git a/src/xrt/include/xrt/xrt_tracking.h b/src/xrt/include/xrt/xrt_tracking.h
index da57ddbad0093cc73040e344bdee9ec121757f51..9257765d6ace96985ba5bcecbaf88e04ceb8948d 100644
--- a/src/xrt/include/xrt/xrt_tracking.h
+++ b/src/xrt/include/xrt/xrt_tracking.h
@@ -84,7 +84,7 @@ struct xrt_tracking_factory
 	                           struct xrt_tracked_psmv **out_psmv);
 
 	/*!
-	 * Create a tracked PSVR ball.
+	 * Create a tracked PSVR HMD.
 	 */
 	int (*create_tracked_psvr)(struct xrt_tracking_factory *,
 	                           struct xrt_device *xdev,