ericniebler / range-v3

Range library for C++14/17/20, basis for C++20's std::ranges
Other
4.06k stars 437 forks source link

Can one zip one sequence several times and then sort zipped view? #1755

Closed Fedr closed 1 year ago

Fedr commented 1 year ago

In the following program, one sequence appears twice in zip-view: ( x, y, y )

#include <array>
#include <iostream>
#include <range/v3/all.hpp>

int main() {
   auto x = std::array{ 3,   2,   4,   1 };
   auto y = std::array{'A', 'B', 'C', 'D'};
   ranges::sort( ranges::views::zip( x, y, y ) );
   for ( auto c : y )
      std::cout << c << ' ';
}

Still the sorting is correct, the program prints D B A C.

Is it accidental, or it is always guaranteed to work if any of the sequences appear several times in zip-view?

brevzin commented 1 year ago

Accidental.

We have something like this:

auto e1 = std::tie(x[0], y[0], y[0]);
auto e2 = std::tie(x[1], y[1], y[1]);
std::swap(e1, e2);

What happens when we swap these tuples? We swap x[0] and x[1], then we swap y[0] and y[1], then we... swap y[0] and y[1] again. We ended up swapping the xs but not the ys.

In this case, the arrays are short enough that an insertion sort - which doesn't use swaps. If you made it long enough to necessitate swaps:

   auto x = std::array{ 3,   2,   4,   1,   0,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  -1};
   auto y = std::array{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q'};
   ranges::sort( ranges::views::zip( x, y, y ) );
   fmt::print("x={::3}\ny={}\n", x, y);

Then this fails:

x=[ -1,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15]
y=['A', 'C', 'D', 'B', 'Q', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P']

Q should be first.