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,