123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- #include "simple/support/range.hpp"
- #include "simple/support/vuple.hpp"
- #include "simple/support/algorithm/vmismatch.hpp"
- #include "simple/support/algorithm/find.hpp"
- #include <vector>
- #include <list>
- #include <deque>
- #include <cassert>
- #include <algorithm>
- #include <functional>
- template <typename F>
- auto flatten_args(F f) { return [f](auto&& t){ return std::apply(f,t); }; }
- using simple::support::vuple;
- using simple::support::vbegin;
- using simple::support::vend;
- int main()
- {
- static_assert(simple::support::vuple_depth<int>{} == 0);
- static_assert(simple::support::vuple_depth<vuple<int>>{} == 1);
- static_assert(simple::support::vuple_depth<vuple<vuple<int>>>{} == 2);
- static_assert(simple::support::vuple_depth<vuple<vuple<vuple<int>>>>{} == 3);
- // basic binary stuff
- assert(( vuple{1,2,3} == vuple{1,2,3} )); // equality works we hope
- assert(( vuple{vuple{1,2},vuple{3,4}} == vuple{vuple{1,2},vuple{3,4}} ));
- assert(( vuple{vuple{1,2},vuple{3,4}} != vuple{1,2} ));
- assert(( vuple{1,2,3} != vuple{1,2,4} )); // inequality also, not so obvious
- assert(( vuple{1,2,3} < vuple{2,3,4} )); // then comes blasphemy
- assert(( not(vuple{1,2,3} < vuple{2,0,4}) )); // oh no this is not lexicographic
- assert(( vuple{1,2,3} <= vuple{1,3,4} ));
- assert(( vuple{1,2,3} > vuple{0,1,2} ));
- assert(( vuple{1,2,3} >= vuple{0,1,3} ));
- assert(( vuple{1,2,3} + vuple{3,2,1} == vuple{4,4,4} )); // now we can check some arithmetic
- assert(( vuple{1,2,3} - vuple{3,2,1} == vuple{-2,0,2} ));
- assert(( vuple{true,false} + vuple{true,true} == vuple{2,1} )); // promotion
- assert(( vuple{1,2,3} + vuple{true,true,false} == vuple{2,3,3} ));
- assert(( 1 + vuple{1,2,3} == vuple{2,3,4} )); // mingle with element type
- assert(( vuple{1,2,3} + 2 == vuple{3,4,5} ));
- // implicit conversion of reduction
- {
- assert(( vuple{1,2,3} - (vuple{1,2,3} > 2) == vuple{1,2,2} ));
- vuple a{1,2,3};
- a -= a <= 2;
- assert(( a == vuple{0,1,3} ));
- }
- // basic unary
- {
- assert(( -vuple{1,2,3} == vuple{-1,-2,-3} ));
- auto a = -vuple(1,2,3);
- assert(( a == vuple{-1,-2,-3} ));
- auto&& b = -vuple(1,2,3);
- assert(( b == vuple{-1,-2,-3} ));
- }
- // fmod
- {
- assert(( vuple{1.f, 2.f, 3.f} % vuple{2.f, 2.f ,2.f} == vuple{1.f, 0.f, 1.f} ));
- auto a = vuple{1.f, 2.f, 3.f};
- a %= vuple{2.f, 2.f, 2.f};
- assert(( a == vuple{1.f, 0.f, 1.f} ));
- }
- // in place left element shenanigans
- {
- auto a = 1;
- a += vuple{1,2,3}; // reduction is like narrow conversion... with += you are pretty much asking for it, it's hardly implicit
- assert(( a == 7));
- // comparison also return a reduction, so need to disambiguate how to reduce it
- bool any_odd = false;
- auto oddness = vuple{1,2,3,4,5,6,7,9,11} % 2 == 1; // this is a conjunction
- // any_odd |= oddness; // this won't compile
- any_odd |= oddness.value; // unwrap the conjunction and reduce the vector by the operator
- assert(( any_odd ));
- bool all_odd = false;
- all_odd |= bool(oddness); // reduce the conjunction
- assert(( not all_odd ));
- auto count_odd = 0;
- count_odd += oddness.value;
- assert(( count_odd == 6 ));
- }
- // basic iterators and find
- {
- std::vector a {1,2,3,4,5};
- std::deque b {5,4,3,2,1};
- std::list c {1,1,1,1,1};
- // TODO: libstdc++ goes full clever mode and switches on difference_type here
- // technically it does need to be a "signed integer type" so nothing we can do about it,
- // maybe discourage it by downgrading the iterator category, but that's still rather unfortunate
- // sad sad world...
- // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1522r1.pdf
- // maybe this will allow us to have custom difference type but I have a
- // feeling that it will not be for a random access iterator
- // the funny thing is that the dreaded switch is probably the only hole
- // I can't patch up, there is right shift, comparison with literal, it's
- // all ok, but nah gotta be extra clever, especially in generic code
- // that deals with user defined types, we gotta use a thing that can't
- // ever accept anything user defined... yeah, there is being standard
- // compliant, but then there is also just basic human decency...
- // I hope I'll live to see iterators with range constrained difference
- // types in the standard (like boost safe numerics) and then full of
- // joy come back and enable these examples... or no let's instead have
- // ranges v666 or something
- // assert(( std::find(vbegin(a, b), vend(a, b), vuple{3,3}) == vbegin(a, b) + 2 ));
- // assert(( std::find(vbegin(a, b), vend(a, b), vuple{4,2}) == vbegin(a, b) + 3 ));
- // assert(( std::find_if(vbegin(a, b), vend(a, b), flatten_args(std::equal_to<>{})) == vbegin(a, b) + 2 ));
- // assert(( std::find_if(vbegin(a, b), vend(a, b), flatten_args(std::less<>{})) == vbegin(a, b) ));
- // assert(( std::find_if(vbegin(a, b), vend(a, b), flatten_args(std::greater<>{})) == vbegin(a, b) + 3 ));
- // SOLVED!
- assert(( simple::support::find(vbegin(a, b), vend(a, b), vuple{3,3}) == vbegin(a, b) + 2 ));
- assert(( simple::support::find(vbegin(a, b), vend(a, b), vuple{4,2}) == vbegin(a, b) + 3 ));
- assert(( simple::support::find_if(vbegin(a, b), vend(a, b), flatten_args(std::equal_to<>{})) == vbegin(a, b) + 2 ));
- assert(( simple::support::find_if(vbegin(a, b), vend(a, b), flatten_args(std::less<>{})) == vbegin(a, b) ));
- assert(( simple::support::find_if(vbegin(a, b), vend(a, b), flatten_args(std::greater<>{})) == vbegin(a, b) + 3 ));
- for(auto&& [x,y,z] : simple::support::range{ vbegin(a, b, c), vend(a, b, c) })
- {
- assert(x + y + z == 7);
- x += 10;
- y += 20;
- z += 30;
- }
- assert(( a == std::vector{11,12,13,14,15} ));
- assert(( b == std::deque{25,24,23,22,21} ));
- assert(( c == std::list{31,31,31,31,31} ));
- {
- auto bg = vbegin(a,b,c);
- auto cg = bg++;
- assert(( cg == vbegin(a,b,c) ));
- assert(( bg == vuple{a.begin() + 1,b.begin() + 1,std::next(c.begin())} ));
- }
- assert(( vend(a, b) - vbegin(a, b) == vuple{5,5} ));
- assert(( vend(a, b) - vuple{5,5} == vbegin(a, b) ));
- assert(( vbegin(a, b) + vuple{5,5} == vend(a, b) ));
- }
- // iterators transform
- {
- std::vector a {1,2,3,4,5};
- std::deque b {5,4,3,2,1};
- std::list c {1,1,1,1,1};
- std::array d {0,0,0,0,0};
- // no extra cleverness so far in transform from either libstdc++ or libc++
- std::transform(vbegin(a,b,c), vend(a,b,c), d.begin(), flatten_args([](auto&... xs) { return (xs + ...); }));
- assert(( d == std::array{7,7,7,7,7} ));
- std::transform(vbegin(a,b), vend(a,b), vbegin(c,d), [](const auto& x) { return x + x; });
- assert(( c == std::list{2,4,6,8,10} ));
- assert(( d == std::array{10,8,6,4,2} ));
- std::transform(vbegin(a,b), vend(a,b), vbegin(c,d), flatten_args([](auto& x, auto& y) { return vuple{x+y, x-y}; }));
- assert(( c == std::list{6,6,6,6,6} ));
- assert(( d == std::array{-4,-2,0,2,4} ));
- std::transform(vbegin(a,b), vend(a,b), vbegin(a,b), [](auto&& x) { return x += x; });
- assert(( a == std::vector{2,4,6,8,10} ));
- assert(( b == std::deque{10,8,6,4,2} ));
- }
- // TODO: sort needs relational operators with difference type and scalars,
- // in context of algorithms we're assuming our ranges are of the same size
- // and since all operations are applied uniformly that should be an
- // invariant, so a usual conjunctive comparison of each element should
- // suffice (it's an overkill actually, but gotta stay generic and also
- // assuming simd, however unlikely that is with pointers)
- // TODO: another reason for sort to not work is swap, but it will be fixed in c++23
- {
- using std::swap;
- int a = 1, b = 2;
- int c = 3, d = 4;
- // this works
- auto x = vuple<int&,int&>(a,b);
- auto y = vuple<int&,int&>(c,d);
- swap(x,y);
- // and this will work in c++23
- // swap(vuple<int&,int&>(a,b), vuple<int&,int&>(c,d));
- assert(( a == 3 && b == 4 && c == 1 && d == 2 ));
- }
- // iterators mismatch
- {
- std::array a{0,1,2,3,4,5,6,7,8,9};
- std::list b{0,1,2,3,4,5,6,7,8,9};
- std::vector c{0,1,2,3,4,0,6,7};
- std::deque d{0,1,2,3};
- assert(( vend(a,c,d) - vbegin(a,c,d) >= 0 ));
- assert(( simple::support::mismatch(vbegin(a,b), vend(a,b)) == vend(a,b) ));
- assert(( simple::support::mismatch(vbegin(a,c), vend(a,c)) == vbegin(a,c) + 5 ));
- assert(( simple::support::mismatch(vbegin(c,d), vend(c,d)) == vbegin(c,d) + 4 ));
- assert(( simple::support::mismatch(vbegin(a,b,c,d), vend(a,b,c,d)) ==
- vuple{simple::support::transform([](auto x){return std::next(x,4);},vbegin(a,b,c,d))} ));
- // std::next(vbegin(a,b,c,d),4) )); // i'm asking you to increment this thing 4 times, it's a bidir, libc++, can you freaking increment a bidir 4 times without going all over the place? why are you messing around with the difference type? why do stdlib writers hate generic code so much??
- }
- // shaped iterators
- {
- std::array row1 { 0,1,2,0,4,5, 0,0, 8,9 };
- std::array row2 { 0,1,2,3,4,5, 0,0, 8,9 };
- auto begin = vbegin(row1,row2);
- auto hole = simple::support::find(vuple{begin, begin+1}, vend(row1,row2), 0);
- assert(( hole == vuple{begin+6, begin+7} ));
- assert(( *hole == 0 ));
- // *hole = -1 // nope, type safety
- *hole = *hole - 1; // this is algebra, it's ok
- // *hole -= 1; // TODO: does not work on a technicality... *hole is not an lvalue... it's a tuple of lvalues...
- assert(( *hole == -1 ));
- assert(( row1 == std::array{0,1,2,0,4,5, -1,-1, 8,9} ));
- assert(( row2 == std::array{0,1,2,3,4,5, -1,-1, 8,9} ));
- *hole = *hole + 14;
- assert(( *hole == 13 ));
- assert(( row1 == std::array{0,1,2,0,4,5, 13,13, 8,9} ));
- assert(( row2 == std::array{0,1,2,3,4,5, 13,13, 8,9} ));
- *hole = vuple{
- vuple{21,23},
- vuple{22,24}
- };
- assert(( row1 == std::array{0,1,2,0,4,5, 21,22, 8,9} ));
- assert(( row2 == std::array{0,1,2,3,4,5, 23,24, 8,9} ));
- }
- return 0;
- }
|