-
Notifications
You must be signed in to change notification settings - Fork 185
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement zip_view for external sort. (#4930)
This PR implements a `zip_view` for zipping together a set of ranges. It is intended to implement the `std::ranges::zip_view` as defined for C++23. From https://en.cppreference.com/w/cpp/ranges/zip_view: 1. A zip_view is a range adaptor that takes one or more views, and produces a view whose ith element is a tuple-like value consisting of the ith elements of all views. The size of produced view is the minimum of sizes of all adapted views. 2. `zip()` is a customization point object that constructs a `zip_view` Currently, the `zip_view` only supports zipping together ranges that are `random_access_range`s. In addition, the `size()` and `end()` functions are only provided if all of the ranges are `sized_range`s The iterator from a `zip_view` is essentially a tuple of pointers to the beginning of each of the zipped ranges, plus an index that keeps track of the iterator's location in the zipped ranges. The size of a `zip_view` is the size of the smallest range. The end iterator of a zip view is the begin iterator plus the size of the zip_view. Unit tests have similar coverage to the tests for the var length views. Tests also include zipping a var length view and iterating through with `std::for_each` and `for`. [sc-43639] --- TYPE: IMPROVEMENT DESC: Implement zip_view for external sort.
- Loading branch information
Showing
3 changed files
with
554 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,292 @@ | ||
/** | ||
* @file unit_zip_view.cc | ||
* | ||
* @section LICENSE | ||
* | ||
* The MIT License | ||
* | ||
* @copyright Copyright (c) 2024 TileDB, Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
* | ||
* @section DESCRIPTION | ||
* | ||
* This file implements unit tests for the zip_view class. | ||
*/ | ||
|
||
#include <algorithm> | ||
#include <catch2/catch_all.hpp> | ||
#include <vector> | ||
#include "../zip_view.h" | ||
|
||
#include "../alt_var_length_view.h" | ||
|
||
TEST_CASE("zip_view: Null test", "[zip_view][null_test]") { | ||
REQUIRE(true); | ||
} | ||
|
||
TEST_CASE("zip_view: Range concepts", "[zip_view][concepts]") { | ||
using test_type = zip_view<std::vector<double>, std::vector<int>>; | ||
|
||
CHECK(std::ranges::range<test_type>); | ||
CHECK(!std::ranges::borrowed_range<test_type>); | ||
CHECK(std::ranges::sized_range<test_type>); | ||
CHECK(std::ranges::input_range<test_type>); | ||
CHECK(!std::ranges:: | ||
output_range<test_type, std::ranges::range_value_t<test_type>>); | ||
CHECK(std::ranges::forward_range<test_type>); | ||
CHECK(std::ranges::bidirectional_range<test_type>); | ||
CHECK(std::ranges::random_access_range<test_type>); | ||
CHECK(!std::ranges::contiguous_range<test_type>); | ||
CHECK(std::ranges::common_range<test_type>); | ||
|
||
// @todo Fix so that it passes on ubuntu. | ||
// CHECK(std::ranges::viewable_range<test_type>); | ||
|
||
// @todo: Should this be a view? | ||
CHECK(!std::ranges::view<test_type>); | ||
} | ||
|
||
TEST_CASE("zip_view: Iterator concepts", "[zip_view][concepts]") { | ||
using test_type = zip_view<std::vector<double>, std::vector<int>>; | ||
using test_type_iterator = std::ranges::iterator_t<test_type>; | ||
using test_type_const_iterator = std::ranges::iterator_t<const test_type>; | ||
|
||
CHECK(std::input_or_output_iterator<test_type_iterator>); | ||
CHECK(std::input_or_output_iterator<test_type_const_iterator>); | ||
CHECK(std::input_iterator<test_type_iterator>); | ||
CHECK(std::input_iterator<test_type_const_iterator>); | ||
CHECK(!std::output_iterator< | ||
test_type_iterator, | ||
std::ranges::range_value_t<test_type>>); | ||
CHECK(!std::output_iterator< | ||
test_type_const_iterator, | ||
std::ranges::range_value_t<test_type>>); | ||
CHECK(std::forward_iterator<test_type_iterator>); | ||
CHECK(std::forward_iterator<test_type_const_iterator>); | ||
CHECK(std::bidirectional_iterator<test_type_iterator>); | ||
CHECK(std::bidirectional_iterator<test_type_const_iterator>); | ||
CHECK(std::random_access_iterator<test_type_iterator>); | ||
CHECK(std::random_access_iterator<test_type_const_iterator>); | ||
} | ||
|
||
// Test that the zip_view value_type satisfies the expected concepts | ||
TEST_CASE("zip_view: value_type concepts", "[zip_view][concepts]") { | ||
using test_type = zip_view<std::vector<double>, std::vector<int>>; | ||
CHECK(std::ranges::range<test_type>); | ||
|
||
using test_iterator_type = std::ranges::iterator_t<test_type>; | ||
using test_iterator_value_type = std::iter_value_t<test_iterator_type>; | ||
using test_iterator_reference_type = | ||
std::iter_reference_t<test_iterator_type>; | ||
|
||
using range_value_type = std::ranges::range_value_t<test_type>; | ||
using range_reference_type = std::ranges::range_reference_t<test_type>; | ||
|
||
CHECK(std::is_same_v<test_iterator_value_type, range_value_type>); | ||
CHECK(std::is_same_v<test_iterator_reference_type, range_reference_type>); | ||
} | ||
|
||
TEST_CASE("zip_view: constructor", "[zip_view]") { | ||
std::vector<int> a{1, 2, 3}; | ||
std::vector<int> b{4, 5, 6}; | ||
std::vector<int> c{7, 8, 9}; | ||
|
||
SECTION("Zip one range") { | ||
auto z = zip(a); | ||
auto it = z.begin(); | ||
CHECK(*it == std::tuple{1}); | ||
++it; | ||
CHECK(*it == std::tuple{2}); | ||
++it; | ||
CHECK(*it == std::tuple{3}); | ||
it = z.begin(); | ||
std::get<0>(*it) = 99; | ||
CHECK(a[0] == 99); | ||
} | ||
|
||
SECTION("Zip three ranges") { | ||
auto z = zip(a, b, c); | ||
auto it = z.begin(); | ||
CHECK(*it == std::tuple{1, 4, 7}); | ||
++it; | ||
CHECK(*it == std::tuple{2, 5, 8}); | ||
++it; | ||
CHECK(*it == std::tuple{3, 6, 9}); | ||
it = z.begin(); | ||
std::get<0>(*it) = 41; | ||
std::get<1>(*it) = 42; | ||
std::get<2>(*it) = 43; | ||
CHECK(a[0] == 41); | ||
CHECK(b[0] == 42); | ||
CHECK(c[0] == 43); | ||
} | ||
} | ||
|
||
TEST_CASE("zip_view: size()", "[zip_view]") { | ||
std::vector<int> a{1, 2, 3}; | ||
std::vector<int> b{4, 5, 6, 7, 8, 9}; | ||
std::vector<int> c{10, 11, 12, 13}; | ||
|
||
CHECK(zip(a).size() == 3); | ||
CHECK(zip(b).size() == 6); | ||
CHECK(zip(c).size() == 4); | ||
CHECK(zip(a, b).size() == 3); | ||
CHECK(zip(a, c).size() == 3); | ||
CHECK(zip(b, c).size() == 4); | ||
CHECK(zip(a, b, c).size() == 3); | ||
} | ||
|
||
TEST_CASE("zip_view: end()", "[zip_view]") { | ||
std::vector<int> a{1, 2, 3}; | ||
std::vector<int> b{4, 5, 6, 7, 8, 9}; | ||
std::vector<int> c{10, 11, 12, 13}; | ||
|
||
[[maybe_unused]] auto x = zip(a).begin(); | ||
[[maybe_unused]] auto y = zip(a).end(); | ||
|
||
CHECK(zip(a).end() == zip(a).begin() + 3); | ||
CHECK(zip(b).end() == zip(b).begin() + 6); | ||
CHECK(zip(c).end() == zip(c).begin() + 4); | ||
CHECK(zip(a, b).end() == zip(a, b).begin() + 3); | ||
CHECK(zip(a, c).end() == zip(a, c).begin() + 3); | ||
CHECK(zip(b, c).end() == zip(b, c).begin() + 4); | ||
CHECK(zip(a, b, c).end() == zip(a, b, c).begin() + 3); | ||
|
||
CHECK(zip(a).end() - zip(a).begin() == 3); | ||
CHECK(zip(b).end() - zip(b).begin() == 6); | ||
CHECK(zip(c).end() - zip(c).begin() == 4); | ||
CHECK(zip(a, b).end() - zip(a, b).begin() == 3); | ||
CHECK(zip(a, c).end() - zip(a, c).begin() == 3); | ||
CHECK(zip(b, c).end() - zip(b, c).begin() == 4); | ||
CHECK(zip(a, b, c).end() - zip(a, b, c).begin() == 3); | ||
} | ||
|
||
TEST_CASE("zip_view: basic iterator properties", "[zip_view]") { | ||
std::vector<int> a{1, 2, 3}; | ||
std::vector<int> b{4, 5, 6, 7, 8, 9}; | ||
std::vector<int> c{10, 11, 12, 13}; | ||
|
||
auto z = zip(a, b, c); | ||
auto it = z.begin(); | ||
auto it2 = z.begin(); | ||
CHECK(it == it2); | ||
CHECK(*it == *it2); | ||
it++; | ||
CHECK(it != it2); | ||
it2++; | ||
CHECK(it == it2); | ||
CHECK(*it == *it2); | ||
auto jt = z.end(); | ||
CHECK(it != jt); | ||
CHECK(it < jt); | ||
CHECK(it <= jt); | ||
CHECK(jt > it); | ||
CHECK(jt >= it); | ||
CHECK(jt == jt); | ||
CHECK(jt >= jt); | ||
CHECK(jt <= jt); | ||
|
||
it = z.begin(); | ||
auto x = *it++; | ||
CHECK(x == std::tuple{1, 4, 10}); | ||
CHECK(it == z.begin() + 1); | ||
|
||
it = z.begin(); | ||
auto y = *++it; | ||
CHECK(y == std::tuple{2, 5, 11}); | ||
CHECK(it == z.begin() + 1); | ||
|
||
CHECK(it[0] == *it); | ||
CHECK(it[1] == *(it + 1)); | ||
CHECK(it[2] == *(it + 2)); | ||
CHECK(it[0] == std::tuple{2, 5, 11}); | ||
} | ||
|
||
TEST_CASE("zip_view: alt_var_length_view", "[zip_view]") { | ||
std::vector<double> r = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}; | ||
std::vector<size_t> o = {0, 3, 6, 10}; | ||
auto v = alt_var_length_view{r, o}; | ||
std::vector<int> a{1, 2, 3}; | ||
std::vector<int> b{4, 5, 6, 7, 8, 9}; | ||
std::vector<int> c{10, 11, 12, 13}; | ||
|
||
auto z = zip(a, b, c, v); | ||
auto it = z.begin(); | ||
CHECK(std::get<0>(*it) == 1); | ||
CHECK(std::get<1>(*it) == 4); | ||
CHECK(std::get<2>(*it) == 10); | ||
CHECK(std::ranges::equal( | ||
std::get<3>(*it++), std::vector<double>{1.0, 2.0, 3.0})); | ||
CHECK(std::ranges::equal( | ||
std::get<3>(*it++), std::vector<double>{4.0, 5.0, 6.0})); | ||
CHECK(std::ranges::equal( | ||
std::get<3>(*it++), std::vector<double>{7.0, 8.0, 9.0, 10.0})); | ||
} | ||
|
||
TEST_CASE( | ||
"zip_view: for, std::for_each with alt_var_length_view", "[zip_view]") { | ||
std::vector<double> r = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}; | ||
std::vector<size_t> o = {0, 3, 6, 10}; | ||
auto v = alt_var_length_view{r, o}; | ||
std::vector<int> a{8, 6, 7}; | ||
|
||
auto z = zip(a, v); | ||
|
||
SECTION("for_each") { | ||
size_t count = 0; | ||
std::for_each(z.begin(), z.end(), [&a, &count](auto x) { | ||
auto&& [i, j] = x; | ||
CHECK(i == a[count]); | ||
switch (count) { | ||
case 0: | ||
CHECK(std::ranges::equal(j, std::vector<double>{1.0, 2.0, 3.0})); | ||
break; | ||
case 1: | ||
CHECK(std::ranges::equal(j, std::vector<double>{4.0, 5.0, 6.0})); | ||
break; | ||
case 2: | ||
CHECK( | ||
std::ranges::equal(j, std::vector<double>{7.0, 8.0, 9.0, 10.0})); | ||
break; | ||
} | ||
++count; | ||
}); | ||
} | ||
SECTION("for") { | ||
size_t count = 0; | ||
for (auto x : z) { | ||
auto&& [i, j] = x; | ||
CHECK(i == a[count]); | ||
switch (count) { | ||
case 0: | ||
CHECK(std::ranges::equal(j, std::vector<double>{1.0, 2.0, 3.0})); | ||
break; | ||
case 1: | ||
CHECK(std::ranges::equal(j, std::vector<double>{4.0, 5.0, 6.0})); | ||
break; | ||
case 2: | ||
CHECK( | ||
std::ranges::equal(j, std::vector<double>{7.0, 8.0, 9.0, 10.0})); | ||
break; | ||
} | ||
++count; | ||
} | ||
} | ||
} |
Oops, something went wrong.