tuple_operators.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. #include "simple/support/range.hpp"
  2. #include "simple/support/vuple.hpp"
  3. #include "simple/support/algorithm/vmismatch.hpp"
  4. #include "simple/support/algorithm/find.hpp"
  5. #include <vector>
  6. #include <list>
  7. #include <deque>
  8. #include <cassert>
  9. #include <algorithm>
  10. #include <functional>
  11. template <typename F>
  12. auto flatten_args(F f) { return [f](auto&& t){ return std::apply(f,t); }; }
  13. using simple::support::vuple;
  14. using simple::support::vbegin;
  15. using simple::support::vend;
  16. int main()
  17. {
  18. static_assert(simple::support::vuple_depth<int>{} == 0);
  19. static_assert(simple::support::vuple_depth<vuple<int>>{} == 1);
  20. static_assert(simple::support::vuple_depth<vuple<vuple<int>>>{} == 2);
  21. static_assert(simple::support::vuple_depth<vuple<vuple<vuple<int>>>>{} == 3);
  22. // basic binary stuff
  23. assert(( vuple{1,2,3} == vuple{1,2,3} )); // equality works we hope
  24. assert(( vuple{vuple{1,2},vuple{3,4}} == vuple{vuple{1,2},vuple{3,4}} ));
  25. assert(( vuple{vuple{1,2},vuple{3,4}} != vuple{1,2} ));
  26. assert(( vuple{1,2,3} != vuple{1,2,4} )); // inequality also, not so obvious
  27. assert(( vuple{1,2,3} < vuple{2,3,4} )); // then comes blasphemy
  28. assert(( not(vuple{1,2,3} < vuple{2,0,4}) )); // oh no this is not lexicographic
  29. assert(( vuple{1,2,3} <= vuple{1,3,4} ));
  30. assert(( vuple{1,2,3} > vuple{0,1,2} ));
  31. assert(( vuple{1,2,3} >= vuple{0,1,3} ));
  32. assert(( vuple{1,2,3} + vuple{3,2,1} == vuple{4,4,4} )); // now we can check some arithmetic
  33. assert(( vuple{1,2,3} - vuple{3,2,1} == vuple{-2,0,2} ));
  34. assert(( vuple{true,false} + vuple{true,true} == vuple{2,1} )); // promotion
  35. assert(( vuple{1,2,3} + vuple{true,true,false} == vuple{2,3,3} ));
  36. assert(( 1 + vuple{1,2,3} == vuple{2,3,4} )); // mingle with element type
  37. assert(( vuple{1,2,3} + 2 == vuple{3,4,5} ));
  38. // implicit conversion of reduction
  39. {
  40. assert(( vuple{1,2,3} - (vuple{1,2,3} > 2) == vuple{1,2,2} ));
  41. vuple a{1,2,3};
  42. a -= a <= 2;
  43. assert(( a == vuple{0,1,3} ));
  44. }
  45. // basic unary
  46. {
  47. assert(( -vuple{1,2,3} == vuple{-1,-2,-3} ));
  48. auto a = -vuple(1,2,3);
  49. assert(( a == vuple{-1,-2,-3} ));
  50. auto&& b = -vuple(1,2,3);
  51. assert(( b == vuple{-1,-2,-3} ));
  52. }
  53. // fmod
  54. {
  55. assert(( vuple{1.f, 2.f, 3.f} % vuple{2.f, 2.f ,2.f} == vuple{1.f, 0.f, 1.f} ));
  56. auto a = vuple{1.f, 2.f, 3.f};
  57. a %= vuple{2.f, 2.f, 2.f};
  58. assert(( a == vuple{1.f, 0.f, 1.f} ));
  59. }
  60. // in place left element shenanigans
  61. {
  62. auto a = 1;
  63. a += vuple{1,2,3}; // reduction is like narrow conversion... with += you are pretty much asking for it, it's hardly implicit
  64. assert(( a == 7));
  65. // comparison also return a reduction, so need to disambiguate how to reduce it
  66. bool any_odd = false;
  67. auto oddness = vuple{1,2,3,4,5,6,7,9,11} % 2 == 1; // this is a conjunction
  68. // any_odd |= oddness; // this won't compile
  69. any_odd |= oddness.value; // unwrap the conjunction and reduce the vector by the operator
  70. assert(( any_odd ));
  71. bool all_odd = false;
  72. all_odd |= bool(oddness); // reduce the conjunction
  73. assert(( not all_odd ));
  74. auto count_odd = 0;
  75. count_odd += oddness.value;
  76. assert(( count_odd == 6 ));
  77. }
  78. // basic iterators and find
  79. {
  80. std::vector a {1,2,3,4,5};
  81. std::deque b {5,4,3,2,1};
  82. std::list c {1,1,1,1,1};
  83. // TODO: libstdc++ goes full clever mode and switches on difference_type here
  84. // technically it does need to be a "signed integer type" so nothing we can do about it,
  85. // maybe discourage it by downgrading the iterator category, but that's still rather unfortunate
  86. // sad sad world...
  87. // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1522r1.pdf
  88. // maybe this will allow us to have custom difference type but I have a
  89. // feeling that it will not be for a random access iterator
  90. // the funny thing is that the dreaded switch is probably the only hole
  91. // I can't patch up, there is right shift, comparison with literal, it's
  92. // all ok, but nah gotta be extra clever, especially in generic code
  93. // that deals with user defined types, we gotta use a thing that can't
  94. // ever accept anything user defined... yeah, there is being standard
  95. // compliant, but then there is also just basic human decency...
  96. // I hope I'll live to see iterators with range constrained difference
  97. // types in the standard (like boost safe numerics) and then full of
  98. // joy come back and enable these examples... or no let's instead have
  99. // ranges v666 or something
  100. // assert(( std::find(vbegin(a, b), vend(a, b), vuple{3,3}) == vbegin(a, b) + 2 ));
  101. // assert(( std::find(vbegin(a, b), vend(a, b), vuple{4,2}) == vbegin(a, b) + 3 ));
  102. // assert(( std::find_if(vbegin(a, b), vend(a, b), flatten_args(std::equal_to<>{})) == vbegin(a, b) + 2 ));
  103. // assert(( std::find_if(vbegin(a, b), vend(a, b), flatten_args(std::less<>{})) == vbegin(a, b) ));
  104. // assert(( std::find_if(vbegin(a, b), vend(a, b), flatten_args(std::greater<>{})) == vbegin(a, b) + 3 ));
  105. // SOLVED!
  106. assert(( simple::support::find(vbegin(a, b), vend(a, b), vuple{3,3}) == vbegin(a, b) + 2 ));
  107. assert(( simple::support::find(vbegin(a, b), vend(a, b), vuple{4,2}) == vbegin(a, b) + 3 ));
  108. assert(( simple::support::find_if(vbegin(a, b), vend(a, b), flatten_args(std::equal_to<>{})) == vbegin(a, b) + 2 ));
  109. assert(( simple::support::find_if(vbegin(a, b), vend(a, b), flatten_args(std::less<>{})) == vbegin(a, b) ));
  110. assert(( simple::support::find_if(vbegin(a, b), vend(a, b), flatten_args(std::greater<>{})) == vbegin(a, b) + 3 ));
  111. for(auto&& [x,y,z] : simple::support::range{ vbegin(a, b, c), vend(a, b, c) })
  112. {
  113. assert(x + y + z == 7);
  114. x += 10;
  115. y += 20;
  116. z += 30;
  117. }
  118. assert(( a == std::vector{11,12,13,14,15} ));
  119. assert(( b == std::deque{25,24,23,22,21} ));
  120. assert(( c == std::list{31,31,31,31,31} ));
  121. {
  122. auto bg = vbegin(a,b,c);
  123. auto cg = bg++;
  124. assert(( cg == vbegin(a,b,c) ));
  125. assert(( bg == vuple{a.begin() + 1,b.begin() + 1,std::next(c.begin())} ));
  126. }
  127. assert(( vend(a, b) - vbegin(a, b) == vuple{5,5} ));
  128. assert(( vend(a, b) - vuple{5,5} == vbegin(a, b) ));
  129. assert(( vbegin(a, b) + vuple{5,5} == vend(a, b) ));
  130. }
  131. // iterators transform
  132. {
  133. std::vector a {1,2,3,4,5};
  134. std::deque b {5,4,3,2,1};
  135. std::list c {1,1,1,1,1};
  136. std::array d {0,0,0,0,0};
  137. // no extra cleverness so far in transform from either libstdc++ or libc++
  138. std::transform(vbegin(a,b,c), vend(a,b,c), d.begin(), flatten_args([](auto&... xs) { return (xs + ...); }));
  139. assert(( d == std::array{7,7,7,7,7} ));
  140. std::transform(vbegin(a,b), vend(a,b), vbegin(c,d), [](const auto& x) { return x + x; });
  141. assert(( c == std::list{2,4,6,8,10} ));
  142. assert(( d == std::array{10,8,6,4,2} ));
  143. std::transform(vbegin(a,b), vend(a,b), vbegin(c,d), flatten_args([](auto& x, auto& y) { return vuple{x+y, x-y}; }));
  144. assert(( c == std::list{6,6,6,6,6} ));
  145. assert(( d == std::array{-4,-2,0,2,4} ));
  146. std::transform(vbegin(a,b), vend(a,b), vbegin(a,b), [](auto&& x) { return x += x; });
  147. assert(( a == std::vector{2,4,6,8,10} ));
  148. assert(( b == std::deque{10,8,6,4,2} ));
  149. }
  150. // TODO: sort needs relational operators with difference type and scalars,
  151. // in context of algorithms we're assuming our ranges are of the same size
  152. // and since all operations are applied uniformly that should be an
  153. // invariant, so a usual conjunctive comparison of each element should
  154. // suffice (it's an overkill actually, but gotta stay generic and also
  155. // assuming simd, however unlikely that is with pointers)
  156. // TODO: another reason for sort to not work is swap, but it will be fixed in c++23
  157. {
  158. using std::swap;
  159. int a = 1, b = 2;
  160. int c = 3, d = 4;
  161. // this works
  162. auto x = vuple<int&,int&>(a,b);
  163. auto y = vuple<int&,int&>(c,d);
  164. swap(x,y);
  165. // and this will work in c++23
  166. // swap(vuple<int&,int&>(a,b), vuple<int&,int&>(c,d));
  167. assert(( a == 3 && b == 4 && c == 1 && d == 2 ));
  168. }
  169. // iterators mismatch
  170. {
  171. std::array a{0,1,2,3,4,5,6,7,8,9};
  172. std::list b{0,1,2,3,4,5,6,7,8,9};
  173. std::vector c{0,1,2,3,4,0,6,7};
  174. std::deque d{0,1,2,3};
  175. assert(( vend(a,c,d) - vbegin(a,c,d) >= 0 ));
  176. assert(( simple::support::mismatch(vbegin(a,b), vend(a,b)) == vend(a,b) ));
  177. assert(( simple::support::mismatch(vbegin(a,c), vend(a,c)) == vbegin(a,c) + 5 ));
  178. assert(( simple::support::mismatch(vbegin(c,d), vend(c,d)) == vbegin(c,d) + 4 ));
  179. assert(( simple::support::mismatch(vbegin(a,b,c,d), vend(a,b,c,d)) ==
  180. vuple{simple::support::transform([](auto x){return std::next(x,4);},vbegin(a,b,c,d))} ));
  181. // 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??
  182. }
  183. // shaped iterators
  184. {
  185. std::array row1 { 0,1,2,0,4,5, 0,0, 8,9 };
  186. std::array row2 { 0,1,2,3,4,5, 0,0, 8,9 };
  187. auto begin = vbegin(row1,row2);
  188. auto hole = simple::support::find(vuple{begin, begin+1}, vend(row1,row2), 0);
  189. assert(( hole == vuple{begin+6, begin+7} ));
  190. assert(( *hole == 0 ));
  191. // *hole = -1 // nope, type safety
  192. *hole = *hole - 1; // this is algebra, it's ok
  193. // *hole -= 1; // TODO: does not work on a technicality... *hole is not an lvalue... it's a tuple of lvalues...
  194. assert(( *hole == -1 ));
  195. assert(( row1 == std::array{0,1,2,0,4,5, -1,-1, 8,9} ));
  196. assert(( row2 == std::array{0,1,2,3,4,5, -1,-1, 8,9} ));
  197. *hole = *hole + 14;
  198. assert(( *hole == 13 ));
  199. assert(( row1 == std::array{0,1,2,0,4,5, 13,13, 8,9} ));
  200. assert(( row2 == std::array{0,1,2,3,4,5, 13,13, 8,9} ));
  201. *hole = vuple{
  202. vuple{21,23},
  203. vuple{22,24}
  204. };
  205. assert(( row1 == std::array{0,1,2,0,4,5, 21,22, 8,9} ));
  206. assert(( row2 == std::array{0,1,2,3,4,5, 23,24, 8,9} ));
  207. }
  208. return 0;
  209. }