From f46b0652177aef18647647752e8b24b6671de69b Mon Sep 17 00:00:00 2001 From: Enrico Seiler Date: Mon, 2 May 2022 19:47:30 +0200 Subject: [PATCH] [WIP] views::zip --- .../matrix/detail/affine_cell_proxy.hpp | 4 +- include/seqan3/utility/views/common_tuple.hpp | 325 ++++++++++++++ include/seqan3/utility/views/zip.hpp | 417 +++++++++++++++++- test/unit/utility/views/zip_test.cpp | 155 ++++++- 4 files changed, 877 insertions(+), 24 deletions(-) create mode 100644 include/seqan3/utility/views/common_tuple.hpp diff --git a/include/seqan3/alignment/matrix/detail/affine_cell_proxy.hpp b/include/seqan3/alignment/matrix/detail/affine_cell_proxy.hpp index 39079b074b7..cf4e8e551ff 100644 --- a/include/seqan3/alignment/matrix/detail/affine_cell_proxy.hpp +++ b/include/seqan3/alignment/matrix/detail/affine_cell_proxy.hpp @@ -163,7 +163,7 @@ class affine_cell_proxy : public tuple_t //!\brief Converting move-constructor. template //!\cond - requires std::constructible_from + requires std::constructible_from //!\endcond explicit affine_cell_proxy(affine_cell_proxy && other) : tuple_t{static_cast(std::move(other))} @@ -194,7 +194,7 @@ class affine_cell_proxy : public tuple_t //!\brief Converting move-assignment. template //!\cond - requires std::assignable_from + // requires std::assignable_from Needs some std::pair/tuple magic //!\endcond affine_cell_proxy & operator=(affine_cell_proxy && other) { diff --git a/include/seqan3/utility/views/common_tuple.hpp b/include/seqan3/utility/views/common_tuple.hpp new file mode 100644 index 00000000000..aa521255e3a --- /dev/null +++ b/include/seqan3/utility/views/common_tuple.hpp @@ -0,0 +1,325 @@ +#pragma once + +#include + +namespace seqan3::detail +{ + +template +class common_tuple : public std::tuple +{ +private: + using tuple_t = std::tuple; + + template + constexpr static auto tuple_transform(fun_t && f, tuple_t && tuple) + { + return std::apply([&] (ts && ... elements) + { + return common_tuple...> + { + std::invoke(f, std::forward(elements))... + }; + }, + std::forward(tuple)); + } + +public: + using tuple_t::tuple_t; + using tuple_t::operator=; + + common_tuple() = default; //!< Defaulted. + common_tuple(common_tuple const &) = default; //!< Defaulted. + common_tuple(common_tuple &&) = default; //!< Defaulted. + ~common_tuple() = default; //!< Defaulted. + + + /*!\name cpp23 constructors + * \{ + */ + template + requires (sizeof...(Types) == sizeof...(UTypes)) && (std::is_constructible_v && ...) + constexpr common_tuple(common_tuple & other) : common_tuple(tuple_transform([] (auto & i) -> decltype(auto) { return i; }, other)) + {} + + template + requires (sizeof...(Types) == sizeof...(UTypes)) && (std::is_constructible_v && ...) + constexpr common_tuple(common_tuple const && other) : common_tuple(tuple_transform([] (I && i) { return std::forward(i); }, other)) + {} + + template + requires (sizeof...(Types) == sizeof...(UTypes)) && (std::is_constructible_v && ...) + explicit constexpr common_tuple(std::tuple & other) : common_tuple(tuple_transform([] (auto & i) -> decltype(auto) { return i; }, other)) + {} + + template + requires (sizeof...(Types) == sizeof...(UTypes)) && (std::is_constructible_v && ...) + explicit constexpr common_tuple(std::tuple const && other) : common_tuple(tuple_transform([] (I && i) { return std::forward(i); }, other)) + {} + //!\} + + /*!\name cpp23 assignment operators + * \{ + */ + constexpr common_tuple& operator=(const common_tuple& other) + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::get(other)), ...); + } (std::index_sequence_for{}); + + return *this; + } + + constexpr const common_tuple& operator=(const common_tuple& other) const + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::get(other)), ...); + } (std::index_sequence_for{}); + + return *this; + } + + constexpr common_tuple& operator=(common_tuple&& other) + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::forward(std::get(other))), ...); + } (std::index_sequence_for{}); + + return *this; + } + + constexpr const common_tuple& operator=(common_tuple&& other) const + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::forward(std::get(other))), ...); + } (std::index_sequence_for{}); + + return *this; + } + + template + requires (sizeof...(Types) == sizeof...(UTypes)) + constexpr common_tuple& operator=(const common_tuple& other) + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::get(other)), ...); + } (std::index_sequence_for{}); + + return *this; + } + + template + requires (sizeof...(Types) == sizeof...(UTypes)) + constexpr const common_tuple& operator=(const common_tuple& other) const + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::get(other)), ...); + } (std::index_sequence_for{}); + + return *this; + } + + template + requires (sizeof...(Types) == sizeof...(UTypes)) + constexpr common_tuple& operator=(common_tuple&& other) + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::forward(std::get(other))), ...); + } (std::index_sequence_for{}); + + return *this; + } + + template + requires (sizeof...(Types) == sizeof...(UTypes)) + constexpr const common_tuple& operator=(common_tuple&& other) const + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::forward(std::get(other))), ...); + } (std::index_sequence_for{}); + + return *this; + } + + constexpr common_tuple& operator=(const std::tuple& other) + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::get(other)), ...); + } (std::index_sequence_for{}); + + return *this; + } + + constexpr const common_tuple& operator=(const std::tuple& other) const + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::get(other)), ...); + } (std::index_sequence_for{}); + + return *this; + } + + constexpr common_tuple& operator=(std::tuple&& other) + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::forward(std::get(other))), ...); + } (std::index_sequence_for{}); + + return *this; + } + + constexpr const common_tuple& operator=(std::tuple&& other) const + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::forward(std::get(other))), ...); + } (std::index_sequence_for{}); + + return *this; + } + + template + requires (sizeof...(Types) == sizeof...(UTypes)) + constexpr common_tuple& operator=(const std::tuple& other) + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::get(other)), ...); + } (std::index_sequence_for{}); + + return *this; + } + + template + requires (sizeof...(Types) == sizeof...(UTypes)) + constexpr const common_tuple& operator=(const std::tuple& other) const + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::get(other)), ...); + } (std::index_sequence_for{}); + + return *this; + } + + template + requires (sizeof...(Types) == sizeof...(UTypes)) + constexpr common_tuple& operator=(std::tuple&& other) + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::forward(std::get(other))), ...); + } (std::index_sequence_for{}); + + return *this; + } + + template + requires (sizeof...(Types) == sizeof...(UTypes)) + constexpr const common_tuple& operator=(std::tuple&& other) const + { + [&] (std::integer_sequence) + { + ((std::get(*this) = std::forward(std::get(other))), ...); + } (std::index_sequence_for{}); + + return *this; + } + //!\} +}; +} + + +namespace std +{ +template +struct tuple_size> : public tuple_size> +{}; + +template +struct tuple_element> : public tuple_element> +{}; + +template + requires requires { typename seqan3::detail::common_tuple...>; } +struct common_type, seqan3::detail::common_tuple> +{ + using type = seqan3::detail::common_tuple...>; +}; + +template class TQual, template class UQual> + requires requires { typename seqan3::detail::common_tuple, UQual>...>; } +struct basic_common_reference, seqan3::detail::common_tuple, TQual, UQual> +{ + using type = seqan3::detail::common_tuple, UQual>...>; +}; + +template +constexpr std::tuple_element_t> & get(seqan3::detail::common_tuple & t) noexcept + requires (i < sizeof...(types)) +{ + return std::get(static_cast &>(t)); +} + +template +constexpr std::tuple_element_t> const & get(seqan3::detail::common_tuple const & t) noexcept + requires (i < sizeof...(types)) +{ + return std::get(static_cast const &>(t)); +} + +template +constexpr std::tuple_element_t> && get(seqan3::detail::common_tuple && t) noexcept + requires (i < sizeof...(types)) +{ + return std::get(static_cast &&>(std::move(t))); +} + +template +constexpr std::tuple_element_t> const && get(seqan3::detail::common_tuple const && t) noexcept + requires (i < sizeof...(types)) +{ + return std::get(static_cast const &&>(std::move(t))); +} + +template +constexpr type & get(seqan3::detail::common_tuple & t) noexcept + requires (seqan3::pack_traits::count == 1) +{ + return std::get(static_cast &>(t)); +} + +template +constexpr type const & get(seqan3::detail::common_tuple const & t) noexcept + requires (seqan3::pack_traits::count == 1) +{ + return std::get(static_cast const &>(t)); +} + +template +constexpr type && get(seqan3::detail::common_tuple && t) noexcept + requires (seqan3::pack_traits::count == 1) +{ + return std::get(static_cast &&>(std::move(t))); +} + +template +constexpr type const && get(seqan3::detail::common_tuple const && t) noexcept + requires (seqan3::pack_traits::count == 1) +{ + return std::get(static_cast const &&>(std::move(t))); +} +} + + diff --git a/include/seqan3/utility/views/zip.hpp b/include/seqan3/utility/views/zip.hpp index 57abb19d32a..e7e1e7c7fc4 100644 --- a/include/seqan3/utility/views/zip.hpp +++ b/include/seqan3/utility/views/zip.hpp @@ -6,26 +6,419 @@ // ----------------------------------------------------------------------------------------------------- /*!\file - * \author Hannes Hauswedell + * \author * \brief Provides seqan3::views::zip. */ #pragma once -#include - -#include +#include +#include +#include +#include #include +#include +#include "common_tuple.hpp" -namespace seqan3::views +// #ifndef __cpp_lib_ranges_zip +namespace seqan3::detail { + template + concept zip_is_common = + (sizeof...(range_ts) == 1 && (std::ranges::common_range && ...)) || + (!(std::ranges::bidirectional_range && ...) && (std::ranges::common_range && ...)) || + ((std::ranges::random_access_range && ...) && (std::ranges::sized_range && ...)); -/*!\brief A zip view - * \ingroup utility_views - * \details - * \noapi{This is currently range-v3's zip implementation.} - */ -inline constexpr auto zip = ::ranges::views::zip; + template + struct tuple_or_pair_impl; + + template + requires (sizeof...(ts) != 2) + struct tuple_or_pair_impl + { + using type = common_tuple; + }; + + template + requires (sizeof...(ts) == 2) + struct tuple_or_pair_impl + { + using type = common_tuple; // common_pair + }; + + template + constexpr t abs(t && v) noexcept + { + if constexpr (std::is_signed_v) + return v < 0 ? -v : v; + else + return v; + } + + template + using tuple_or_pair = tuple_or_pair_impl::type; + + template + constexpr auto tuple_transform(fun_t && f, tuple_t && tuple) + { + return std::apply([&] (ts && ... elements) + { + return tuple_or_pair...> + { + std::invoke(f, std::forward(elements))... + }; + }, + std::forward(tuple)); + } + + template + constexpr void tuple_for_each(fun_t && f, tuple_t && tuple) + { + std::apply([&] (ts && ... elements) + { + (std::invoke(f, std::forward(elements)), ...); + }, + std::forward(tuple)); + } + + template + concept simple_view = std::ranges::view && std::ranges::range && + std::same_as, std::ranges::iterator_t> && + std::same_as, std::ranges::sentinel_t>; + + template + using maybe_const = std::conditional_t; + + template + concept all_random_access = + (std::ranges::random_access_range> && ...); + + template + concept all_bidirectional = + (std::ranges::bidirectional_range> && ...); + + template + concept all_forward = + (std::ranges::forward_range> && ...); + + template + requires (std::ranges::view && ...) && (sizeof...(Views) > 0) + class zip_view : public std::ranges::view_interface> + { + common_tuple views_; + + template + class iterator; + + template + class sentinel; + + public: + zip_view() requires (std::is_default_constructible_v && ...) = default; + constexpr explicit zip_view(Views... views) : views_(std::move(views)...) + {} + + constexpr auto begin() + requires (!(simple_view && ...)) + { + return iterator(tuple_transform(std::ranges::begin, views_)); + } + + constexpr auto begin() const + requires (std::ranges::range && ...) + { + return iterator(tuple_transform(std::ranges::begin, views_)); + } + + constexpr auto end() + requires (!(simple_view && ...)) + { + if constexpr (!zip_is_common) + return sentinel(tuple_transform(std::ranges::end, views_)); + else if constexpr ((std::ranges::random_access_range && ...)) + return begin() + std::iter_difference_t>(size()); + else + return iterator(tuple_transform(std::ranges::end, views_)); + } + + constexpr auto end() const + requires (std::ranges::range && ...) + { + if constexpr (!zip_is_common) + return sentinel(tuple_transform(std::ranges::end, views_)); + else if constexpr ((std::ranges::random_access_range && ...)) + return begin() + std::iter_difference_t>(size()); + else + return iterator(tuple_transform(std::ranges::end, views_)); + } + + constexpr auto size() requires (std::ranges::sized_range && ...) + { + return std::apply([] (auto... sizes) + { + using common_size_t = /*make_unsigned*/std::common_type_t; + return std::ranges::min({static_cast(sizes)...}); + }, + tuple_transform(std::ranges::size, views_)); + } + + constexpr auto size() const requires (std::ranges::sized_range && ...) + { + return std::apply([] (auto... sizes) + { + using common_size_t = std::make_unsigned_t>; + return std::ranges::min({static_cast(sizes)...}); + }, + tuple_transform(std::ranges::size, views_)); + } + }; -} // namespace seqan3::views + template + zip_view(range_ts && ...) -> zip_view...>; + + template struct helper; + template <> struct helper { using iterator_category = std::input_iterator_tag; }; + template <> struct helper {}; + + template + requires (std::ranges::view && ...) && (sizeof...(Views) > 0) + template + class zip_view::iterator : public helper> + { + public: + tuple_or_pair>...> current_; + constexpr explicit iterator(tuple_or_pair>...> current) : current_(std::move(current)) + {} + + friend class zip_view; + friend class zip_view::sentinel; + friend class zip_view::sentinel; + // public: + using iterator_concept = + std::conditional_t, std::random_access_iterator_tag, + std::conditional_t, std::bidirectional_iterator_tag, + std::conditional_t, std::forward_iterator_tag, + std::input_iterator_tag>>>; + using value_type = tuple_or_pair>...>; + using difference_type = std::common_type_t>...>; + + iterator() = default; + constexpr iterator(iterator i) + requires Const && (std::convertible_to, + std::ranges::iterator_t>> && ...) : current_(std::move(i.current)) + {} + + constexpr auto operator*() const + { + return tuple_transform([] (auto & i) -> decltype(auto) { return *i; }, current_); + } + constexpr iterator& operator++() + { + tuple_for_each([] (auto & i) { ++i; }, current_); + return *this; + } + constexpr void operator++(int) + { + ++*this; + } + constexpr iterator operator++(int) requires all_forward + { + auto tmp = *this; + ++*this; + return tmp; + } + + constexpr iterator& operator--() requires all_bidirectional + { + tuple_for_each([] (auto & i) { --i; }, current_); + return *this; + } + constexpr iterator operator--(int) requires all_bidirectional + { + auto tmp = *this; + --*this; + return tmp; + } + + constexpr iterator& operator+=(difference_type x) + requires all_random_access + { + tuple_for_each([&] (I & i) { i += std::iter_difference_t(x); }, current_); + return *this; + } + constexpr iterator& operator-=(difference_type x) + requires all_random_access + { + tuple_for_each([&] (I & i) { i -= std::iter_difference_t(x); }, current_); + return *this; + } + + constexpr auto operator[](difference_type n) const + requires all_random_access + { + return tuple_transform([&] (I & i) -> decltype(auto) + { + return i[std::iter_difference_t(n)]; + }, current_); + } + + friend constexpr bool operator==(iterator const & x, iterator const & y) + requires (std::equality_comparable>> && ...) + { + if constexpr (all_bidirectional) + { + return x.current_ == y.current_; + } + else + { + return [&] (std::integer_sequence) + { + return ((std::get(x.current_) == std::get(y.current_)) || ...); + } (std::index_sequence_for{}); + } + } + + friend constexpr bool operator<(iterator const & x, iterator const & y) + requires all_random_access + { + return x.current_ < y.current_; + } + friend constexpr bool operator>(iterator const & x, iterator const & y) + requires all_random_access + { + return y < x; + } + friend constexpr bool operator<=(iterator const & x, iterator const & y) + requires all_random_access + { + return !(y < x); + } + friend constexpr bool operator>=(iterator const & x, iterator const & y) + requires all_random_access + { + return !(x < y); + } +#ifdef __cpp_lib_three_way_comparison + friend constexpr auto operator<=>(iterator const & x, iterator const & y) + requires all_random_access && + (std::three_way_comparable>> && ...) + { + return x.current_ <=> y.current_; + } +#endif + + friend constexpr iterator operator+(iterator const & i, difference_type n) + requires all_random_access + { + auto r = i; + r += n; + return r; + } + friend constexpr iterator operator+(difference_type n, iterator const & i) + requires all_random_access + { + return i + n; + } + friend constexpr iterator operator-(iterator const & i, difference_type n) + requires all_random_access + { + auto r = i; + r -= n; + return r; + } + + friend constexpr difference_type operator-(iterator const & x, iterator const & y) + requires (std::sized_sentinel_for>, + std::ranges::iterator_t>> && ...) + { + return [&] (std::integer_sequence) + { + return std::ranges::min({(abs(std::get(x.current_) - std::get(y.current_)), ...)}); + } (std::index_sequence_for{}); + } + + friend constexpr auto iter_move(iterator const & i) noexcept( + (noexcept(std::ranges::iter_move(std::declval> const &>())) && ...) && + (std::is_nothrow_move_constructible_v>> && ...)) + { + return tuple_transform(std::ranges::iter_move, i.current_); + } + + /*todo*/ + friend constexpr void iter_swap(iterator const & l, iterator const & r) + requires (std::indirectly_swappable>> && ...) + { + [&] (std::integer_sequence) + { + (std::ranges::iter_swap(std::get(l.current_), std::get(r.current)), ...); + } (std::index_sequence_for{}); + } + }; + + template + requires (std::ranges::view && ...) && (sizeof...(Views) > 0) + template + class zip_view::sentinel + { + constexpr explicit sentinel(tuple_or_pair>...> end) : end_(std::move(end)) + {} + public: + tuple_or_pair>...> end_; + + friend class zip_view; + friend class zip_view::iterator; + friend class zip_view::iterator; + + // public: + sentinel() = default; + constexpr sentinel(sentinel i) + requires Const && (std::convertible_to, std::ranges::sentinel_t>> && ...) : end_(std::move(i.end_)) + {} + + template + requires (std::sentinel_for>, std::ranges::iterator_t>> && ...) + friend constexpr bool operator==(iterator const & x, sentinel const & y) + { + return [&] (std::integer_sequence) + { + return ((std::get(x.current_) == std::get(y.end_)) || ...); + } (std::index_sequence_for{}); + + } + + template + requires (std::sized_sentinel_for>, std::ranges::iterator_t>> && ...) + friend constexpr std::common_type_t>...> operator-(iterator const & x, sentinel const & y) + { + return [&] (std::integer_sequence) + { + return std::ranges::min({(abs(std::get(x.current_) - std::get(y.end_)), ...)}); + } (std::index_sequence_for{}); + } + + template + requires (std::sized_sentinel_for>, std::ranges::iterator_t>> && ...) + friend constexpr std::common_type_t>...> operator-(sentinel const & y, iterator const & x) + { + return - (x - y); + } + }; + + struct zip_fn + { + template + constexpr auto operator()(rng_t&&... rngs) const + { + if constexpr (sizeof...(rng_t) == 0) + return std::views::empty>; + else + return zip_view{std::forward(rngs)...}; + } + }; +} + +namespace seqan3::views +{ + inline constexpr auto zip = detail::zip_fn{}; +} diff --git a/test/unit/utility/views/zip_test.cpp b/test/unit/utility/views/zip_test.cpp index 29db9af6f94..81a4cffe810 100644 --- a/test/unit/utility/views/zip_test.cpp +++ b/test/unit/utility/views/zip_test.cpp @@ -5,20 +5,155 @@ // shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md // ----------------------------------------------------------------------------------------------------- -#include -#include -#include -#include -#include -#include -#include - #include +#include +#include +#include #include -// https://github.com/ericniebler/range-v3/issues/1480 -TEST(zip_view, gcc10bug_rangev3_1480) +using seqan3::operator""_dna4; + +class zip_test : public ::testing::Test +{ +protected: + std::vector vi{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector vs{"this", "is", "a", "test"}; + std::vector const vc{"this", "is", "a", "test"}; + static constexpr auto vr = seqan3::views::repeat('L'); + + using tuple1_t = std::tuple; + using tuple2_t = std::tuple; + using result1_t = std::vector; + using result2_t = std::vector; + + using rng_not_common_t = decltype(seqan3::views::zip(vi, vs, vr)); + using rng_common_t = decltype(seqan3::views::zip(vi, vs)); + using rng_const_t = decltype(seqan3::views::zip(vi, vc)); + using view_const_t = decltype(seqan3::views::zip(vi, vc)) const; + using rng_view_const_t = decltype(seqan3::views::zip(vi, vc)) const; + + result1_t expected1{{0, "this", 'L'}, {1, "is", 'L'}, {2, "a", 'L'}, {3, "test", 'L'}}; + result2_t expected2{{0, "this"}, {1, "is"}, {2, "a"}, {3, "test"}}; + + rng_not_common_t v1; + rng_common_t v2; + + virtual void SetUp() + { + v1 = seqan3::views::zip(vi, vs, vr); + v2 = seqan3::views::zip(vi, vs); + } +}; + +TEST_F(zip_test, concepts) +{ + EXPECT_TRUE((std::ranges::range)); + EXPECT_TRUE((std::ranges::range)); + EXPECT_TRUE((std::ranges::range)); + EXPECT_TRUE((std::ranges::range)); + EXPECT_TRUE((std::ranges::range)); + + EXPECT_TRUE((std::ranges::input_range)); + EXPECT_TRUE((std::ranges::input_range)); + EXPECT_TRUE((std::ranges::input_range)); + EXPECT_TRUE((std::ranges::input_range)); + EXPECT_TRUE((std::ranges::input_range)); + + EXPECT_TRUE((std::ranges::forward_range)); + EXPECT_TRUE((std::ranges::forward_range)); + EXPECT_TRUE((std::ranges::forward_range)); + EXPECT_TRUE((std::ranges::forward_range)); + EXPECT_TRUE((std::ranges::forward_range)); + + EXPECT_TRUE((std::ranges::bidirectional_range)); + EXPECT_TRUE((std::ranges::bidirectional_range)); + EXPECT_TRUE((std::ranges::bidirectional_range)); + EXPECT_TRUE((std::ranges::bidirectional_range)); + EXPECT_TRUE((std::ranges::bidirectional_range)); + + EXPECT_TRUE((std::ranges::random_access_range)); + EXPECT_TRUE((std::ranges::random_access_range)); + EXPECT_TRUE((std::ranges::random_access_range)); + EXPECT_TRUE((std::ranges::random_access_range)); + EXPECT_TRUE((std::ranges::random_access_range)); + + EXPECT_FALSE((std::ranges::contiguous_range)); + EXPECT_FALSE((std::ranges::contiguous_range)); + EXPECT_FALSE((std::ranges::contiguous_range)); + EXPECT_FALSE((std::ranges::contiguous_range)); + EXPECT_FALSE((std::ranges::contiguous_range)); + + EXPECT_TRUE((std::ranges::view)); + EXPECT_TRUE((std::ranges::view)); + EXPECT_TRUE((std::ranges::view)); + EXPECT_FALSE((std::ranges::view)); + EXPECT_FALSE((std::ranges::view)); + + EXPECT_FALSE((std::ranges::sized_range)); + EXPECT_TRUE((std::ranges::sized_range)); + EXPECT_TRUE((std::ranges::sized_range)); + EXPECT_TRUE((std::ranges::sized_range)); + EXPECT_TRUE((std::ranges::sized_range)); + + EXPECT_FALSE((std::ranges::common_range)); + EXPECT_TRUE((std::ranges::common_range)); + EXPECT_TRUE((std::ranges::common_range)); + EXPECT_TRUE((std::ranges::common_range)); + EXPECT_TRUE((std::ranges::common_range)); + + EXPECT_TRUE((std::ranges::output_range>)); + EXPECT_TRUE((std::ranges::output_range>)); + EXPECT_TRUE((std::ranges::output_range>)); + EXPECT_TRUE((std::ranges::output_range>)); + EXPECT_TRUE((std::ranges::output_range>)); +} + +TEST_F(zip_test, access) +{ + EXPECT_TRUE(std::ranges::equal(v1, expected1)); + EXPECT_TRUE(std::ranges::equal(v2, expected2)); +} + +TEST_F(zip_test, combine) +{ + EXPECT_TRUE(std::ranges::equal(v1 | std::views::reverse, expected1 | std::views::reverse)); + EXPECT_TRUE(std::ranges::equal(v2 | std::views::reverse, expected2 | std::views::reverse)); + + EXPECT_TRUE(std::ranges::equal(v1 | std::views::take(2), expected1 | std::views::take(2))); + EXPECT_TRUE(std::ranges::equal(v2 | std::views::take(2), expected2 | std::views::take(2))); +} + +TEST_F(zip_test, alignment_usage_1) +{ + seqan3::dna4_vector sequence_1{"AAAAA"_dna4}; + seqan3::dna4_vector sequence_2{"TTTTT"_dna4}; + std::tuple sequence_pair{sequence_1, sequence_2}; + auto tuple_view = std::views::single(sequence_pair); + auto zipped_tuple = seqan3::views::zip(tuple_view, std::views::iota(0)); + auto chunked_zip = zipped_tuple | seqan3::views::chunk(1); + (void) chunked_zip; +} + +TEST_F(zip_test, alignment_usage_2) +{ + seqan3::dna4_vector sequence_1{"AAAAA"_dna4}; + seqan3::dna4_vector sequence_2{"TTTTT"_dna4}; + // std::tuple sequence_pair{sequence_1, sequence_2}; + auto tuple_view = std::views::single(std::tie(sequence_1, sequence_2)); + auto zipped_tuple = seqan3::views::zip(tuple_view, std::views::iota(0)); + auto chunked_zip = zipped_tuple | seqan3::views::chunk(1); + (void) chunked_zip; +} + +TEST_F(zip_test, assign) +{ + *v1.begin() = std::tuple{9, "moo", 'P'}; + EXPECT_TRUE(std::ranges::equal(v1, result1_t{{9, "moo", 'P'}, {1, "is", 'P'}, {2, "a", 'P'}, {3, "test", 'P'}})); +} + +// // https://github.com/ericniebler/range-v3/issues/1480 +TEST_F(zip_test, gcc10bug_rangev3_1480) { // This regression test only checks if the respective code compiles. std::vector const first_sequence{};