From 0d4681ad4e2ec76255bcdf0d6d405f6a0b0662fb Mon Sep 17 00:00:00 2001 From: rrahn Date: Tue, 1 Oct 2019 16:09:14 +0200 Subject: [PATCH 1/6] [FEATURE] Adds pseudo random access range. --- .../range/views/pseudo_random_access.hpp | 393 ++++++++++++++++++ .../range/views/pseudo_random_access.cpp | 38 ++ test/unit/range/views/CMakeLists.txt | 1 + .../views/view_pseudo_random_access_test.cpp | 202 +++++++++ 4 files changed, 634 insertions(+) create mode 100644 include/seqan3/range/views/pseudo_random_access.hpp create mode 100644 test/snippet/range/views/pseudo_random_access.cpp create mode 100644 test/unit/range/views/view_pseudo_random_access_test.cpp diff --git a/include/seqan3/range/views/pseudo_random_access.hpp b/include/seqan3/range/views/pseudo_random_access.hpp new file mode 100644 index 0000000000..694eb19811 --- /dev/null +++ b/include/seqan3/range/views/pseudo_random_access.hpp @@ -0,0 +1,393 @@ +// ----------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2019, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------- + +/*!\file + * \brief Provides seqan3::views::pseudo_random_access. + * \author Rene Rahn + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace seqan3::detail +{ + +// ============================================================================ +// view_pseudo_random_access +// ============================================================================ + +/*!\brief View to force random access range iterator for seqan3::pseudo_random_access_range. + * \tparam urng_t The underlying range type; must model std::ranges::view and seqan3::pseudo_random_access_range. + * \ingroup views + * + * \details + * + * Wraps the iterator of a seqan3::pseudo_random_access_range and overwrites the iterator category to be + * std::random_access_iterator_tag. Thus, the resulting range can be used in algorithms or other contexts that require + * random access, although the time complexity still depends on the underlying range, that is it is not guaranteed to + * be constant. + */ +template +//!\cond + requires pseudo_random_access_range +//!\endcond +class view_pseudo_random_access : public ranges::view_interface> +{ +private: + + // Iterator declaration. + template + class view_pseudo_random_access_iterator; + +public: + + /*!\name Constructors, destructor and assignment + * \{ + */ + constexpr view_pseudo_random_access() = default; //!< Defaulted. + constexpr view_pseudo_random_access(view_pseudo_random_access const &) = default; //!< Defaulted. + constexpr view_pseudo_random_access(view_pseudo_random_access &&) = default; //!< Defaulted. + constexpr view_pseudo_random_access & operator=(view_pseudo_random_access const &) = default; //!< Defaulted. + constexpr view_pseudo_random_access & operator=(view_pseudo_random_access &&) = default; //!< Defaulted. + ~view_pseudo_random_access() = default; //!< Defaulted. + + //!\brief Construction from the underlying view. + explicit constexpr view_pseudo_random_access(urng_t range) : urng{std::move(range)} + {} + + //!\brief Construction from the underlying viewable range. + template + //!\cond + requires !std::same_as, view_pseudo_random_access> && + std::ranges::viewable_range && + std::constructible_from>> + //!\endcond + explicit constexpr view_pseudo_random_access(viewable_rng_t range) : + view_pseudo_random_access{std::views::all(range)} + {} + //!\} + + /*!\name Iterators + * \{ + */ + //!\brief Returns the iterator to the begin of the range. + constexpr auto begin() noexcept + { + return view_pseudo_random_access_iterator{std::ranges::begin(urng)}; + } + + //!\copydoc seqan3::detail::view_pseudo_random_access::begin + constexpr auto begin() const noexcept + //!\cond + requires const_iterable_range + //!\endcond + { + return view_pseudo_random_access_iterator{std::ranges::cbegin(urng)}; + } + + //!\copydoc seqan3::detail::view_pseudo_random_access::begin + constexpr auto cbegin() const noexcept + //!\cond + requires const_iterable_range + //!\endcond + { + return begin(); + } + + /*!\brief Returns the sentinel to the end of the range. + * + * \details + * + * If the underlying range is a common range this functions returns + * seqan3::detail::view_pseudo_random_access::view_pseudo_random_access_iterator initialised with the end of the + * underlying range. Otherwise it returns the sentinel of the underlying range. + */ + constexpr auto end() noexcept + { + if constexpr (std::ranges::common_range) + return view_pseudo_random_access_iterator{std::ranges::end(urng)}; + else + return urng.end(); + } + + //!\copydoc seqan3::detail::view_pseudo_random_access::end + constexpr auto end() const noexcept + //!\cond + requires const_iterable_range + //!\endcond + { + if constexpr (std::ranges::common_range) + return view_pseudo_random_access_iterator{std::ranges::cend(urng)}; + else + return std::ranges::cend(urng); + } + + //!\copydoc seqan3::detail::view_pseudo_random_access::end + constexpr auto cend() const noexcept + //!\cond + requires const_iterable_range + //!\endcond + { + return end(); + } + //!\} + + urng_t urng; //!< The underlying range. +}; + +/*!\brief Iterator wrapper for the underlying range iterator enforcing std::random_access_iterator_tag. + * \tparam underlying_iter_t The type of the underlying range iterator. + * + * \details + * + * This class inherits all properties of the underlying range iterator and overwrites the iterator category to be + * std::random_access_range_tag. + */ +template +//!\cond + requires pseudo_random_access_range +//!\endcond +template +class view_pseudo_random_access::view_pseudo_random_access_iterator : + public inherited_iterator_base, underlying_iter_t> +{ +private: + //!\brief The type of the base class. + using base_t = inherited_iterator_base, underlying_iter_t>; + +public: + + //!\brief The new iterator category. + using iterator_category = std::random_access_iterator_tag; + + /*!\name Constructors, destructor and assignment + * \{ + */ + // Importing base's constructors. + using base_t::base_t; + //!\brief Defaulted. + constexpr view_pseudo_random_access_iterator() = default; + //!\brief Defaulted. + constexpr view_pseudo_random_access_iterator(view_pseudo_random_access_iterator const &) = default; + //!\brief Defaulted. + constexpr view_pseudo_random_access_iterator(view_pseudo_random_access_iterator &&) = default; + //!\brief Defaulted. + constexpr view_pseudo_random_access_iterator & operator=(view_pseudo_random_access_iterator const &) = default; + //!\brief Defaulted. + constexpr view_pseudo_random_access_iterator & operator=(view_pseudo_random_access_iterator &&) = default; + //!\brief Defaulted. + ~view_pseudo_random_access_iterator() = default; + //!\} + + /*!\name Comparison operators + * \brief Comparison with sentinel of underlying range. + * \{ + */ + // Importing base's equality operators + using base_t::operator==; + using base_t::operator!=; + //!\brief Tests if iterator is at the end. + friend constexpr bool operator==(view_pseudo_random_access_iterator const & lhs, + std::ranges::sentinel_t const & rhs) + noexcept(noexcept(std::declval() == + std::declval const &>())) + { + return static_cast(lhs) == rhs; + } + + //!\brief Tests if iterator is at the end. + friend constexpr bool operator==(std::ranges::sentinel_t const & lhs, + view_pseudo_random_access_iterator const & rhs) + noexcept(noexcept(std::declval() == + std::declval const &>())) + { + return rhs == lhs; + } + + //!\brief Tests if iterator is not at the end. + friend constexpr bool operator!=(view_pseudo_random_access_iterator const & lhs, + std::ranges::sentinel_t const & rhs) + noexcept(noexcept(std::declval() != + std::declval const &>())) + { + return !(lhs == rhs); + } + + //!\brief Tests if iterator is not at the end. + friend constexpr bool operator!=(std::ranges::sentinel_t const & lhs, + view_pseudo_random_access_iterator const & rhs) + noexcept(noexcept(std::declval() != + std::declval const &>())) + { + return rhs != lhs; + } + //!\} + + /*!\name Arithmetic operators + * \{ + */ + // Import operator from base. + using base_t::operator-; + + //!\brief Computes the distance betwen this iterator and the sentinel of the underlying range. + constexpr typename base_t::difference_type operator-(std::ranges::sentinel_t const & rhs) const + noexcept(noexcept(std::declval() - + std::declval const &>())) + requires std::sized_sentinel_for, underlying_iter_t> + { + return static_cast(*this) - rhs; + } + + //!\brief Computes the distance betwen this iterator and the sentinel of the underlying range. + constexpr friend typename base_t::difference_type operator-(std::ranges::sentinel_t const & lhs, + view_pseudo_random_access_iterator const & rhs) + noexcept(noexcept(std::declval const &>() - + std::declval())) + requires std::sized_sentinel_for, underlying_iter_t> + { + return lhs - static_cast(rhs); + } + //!\} +}; + +/*!\name Type deduction guides + * \relates seqan3::detail::view_pseudo_random_access + * \{ + */ +//!\brief A deduction guide for the view class template. +template +view_pseudo_random_access(rng_t &&) -> view_pseudo_random_access>; +//!\} + +// ============================================================================ +// pseudo_random_access_fn (adaptor definition) +// ============================================================================ + +//!\brief View adaptor definition for seqan3::views::pseudo_random_access. +struct pseudo_random_access_fn : public adaptor_base +{ +private: + //!\brief Type of the CRTP-base. + using base_t = adaptor_base; + +public: + //!\brief Inherit the base class's Constructors. + using base_t::base_t; + +private: + //!\brief Befriend the base class so it can call impl(). + friend base_t; + + /*!\brief Call the view's constructor with the underlying view as argument. + * \returns An instance of the adapted range. + */ + template + static constexpr auto impl(urng_t && urange) + { + static_assert(std::ranges::random_access_range || pseudo_random_access_range, + "The adapted range must either model std::ranges::random_access_range or must be " + "a specific SeqAn range type that supports pseudo random access."); + static_assert(std::ranges::forward_range, + "The underlying range must model std::ranges::forward_range."); + + if constexpr (std::ranges::random_access_range) + { // Nothing to do, just return as ref_view or original view. + return std::views::all(std::forward(urange)); + } + else + { // Get a subrange using the random access iterators of the container. + return view_pseudo_random_access{std::forward(urange)}; + } + } +}; + +} // namespace seqan3::detail + +namespace seqan3::views +{ + +/*!\name General purpose views + * \{ + */ + +/*!\brief A view adaptor that converts a pseudo random access range to a std::random_access_range. + * \tparam urng_t The type of the range being processed. See below for requirements. [template parameter is + * omitted in pipe notation] + * \param[in] urange The range being processed. [parameter is omitted in pipe notation] + * \returns A std::ranges::random_access_range over the given range. + * \ingroup views + * + * \details + * + * **Header** + * ```cpp + * #include + * ``` + * + * A pseudo random access range is a range whose iterator typically defines all the interfaces necessary to allow + * random access, but cannot guarantee accessing an arbitrary element in constant time. + * Thus, the highest category it can support by default is std::ranges::bidirectional_range. However, for many of these + * pseudo random access ranges better algorithms and data structures with sub-linear runtime complexities can be used + * (for example logarithmic time complexity). To enforce the faster behaviour of the range in a generic + * range-based context you can use this range adaptor, which will return a range that models + * std::ranges::random_access_range. Note, that this does not mean that the complexity of accessing an arbitrary element + * of the adapted range improves to constant time, but merely all syntactical requirements are fulfilled including the + * iterator tag. + * + * ### View properties + * + * | range concepts and reference_t | `urng_t` (underlying range type) | `rrng_t` (returned range type) | + * |----------------------------------|:---------------------------------:|:------------------------------------------:| + * | std::ranges::input_range | *required* | *guaranteed* | + * | std::ranges::forward_range | *required* | *guaranteed* | + * | std::ranges::bidirectional_range | | *guaranteed* | + * | std::ranges::random_access_range | | *guaranteed* | + * | std::ranges::contiguous_range | | *preserved* | + * | | | | + * | std::ranges::viewable_range | *required* | *guaranteed* | + * | std::ranges::view | | *guaranteed* | + * | std::ranges::sized_range | | *preserved* | + * | std::ranges::common_range | | *preserved* | + * | std::ranges::output_range | | *preserved* | + * | seqan3::const_iterable_range | | *preserved* | + * | | | | + * | seqan3::reference_t | | seqan3::reference_t | + * + * See the \link views views submodule documentation \endlink for detailed descriptions of the view properties. + * + * This adaptor requires that the underlying range models either std::ranges::random_access_range or + * seqan3::pseudo_random_access_range. + * + * ### Return type + * + * | `urng_t` (underlying range type) | `rrng_t` (returned range type) | + * |:--------------------------------------:|:----------------------------------------------------:| + * | `std::ranges::random_access_range` | `std::ranges::ref_view` | + * | `seqan3::pseudo_random_access_range` | `seqan3::detail::view_pseudo_random_access` | + * + * The adaptor returns exactly the type specified above. In the second case a view is returned whose iterator wraps + * the iterator of the underlying range and adapts all of its functionality but overwrites the + * iterator category to be std::random_access_iterator_tag. + * + * ### Example + * + * \include test/snippet/range/views/pseudo_random_access.cpp + * + * ### Complexity + * + * Construction of the returned view is in \f$ O(1) \f$. + * + * \hideinitializer + */ +inline constexpr auto pseudo_random_access = detail::pseudo_random_access_fn{}; +//!\} +} // namespace seqan3::views diff --git a/test/snippet/range/views/pseudo_random_access.cpp b/test/snippet/range/views/pseudo_random_access.cpp new file mode 100644 index 0000000000..f9674f9023 --- /dev/null +++ b/test/snippet/range/views/pseudo_random_access.cpp @@ -0,0 +1,38 @@ +#include + +#include +#include +#include +#include + +int main() +{ + using seqan3::operator""_dna4; + + // A gap decorator is a pseudo random access range using logarithmic time complexity internally. + auto seq = "ACGTACGACT"_dna4; + seqan3::gap_decorator aligned_seq{seq}; + + // It does not fulfil random access semantics because it does not allow constant time access to aribtrary + // elements in the range. Thus, it is only a bidirectional range by default. + static_assert(std::ranges::bidirectional_range); + static_assert(!std::ranges::random_access_range); + + // The default interface models at most std::bidirectional_range. + auto it = std::ranges::begin(aligned_seq); // Returned iterator models std::bidirectional_iterator. + std::ranges::advance(it, 3); // Advancing the iterator takes linear time. + seqan3::debug_stream << *it << '\n'; // "T" + + // After adapting it with pseudo_random_access, the returned range models std::random_access_range. + auto aligned_seq_ra = aligned_seq | seqan3::views::pseudo_random_access; + + // The pesudo_random_access wrapper returns a view that enforces random_access. + // Note that this does not mean that the semantic requirements have been changed. Only all syntactical + // interfaces for the std::ranges::random_access_range are modeled now by the returned view. + // Access time still depends on the underlying range. + static_assert(std::ranges::random_access_range); + + auto it_ra = std::ranges::begin(aligned_seq_ra); // Returned iterator models std::random_access_iterator. + std::ranges::advance(it_ra, 3); // Advancing the iterator takes now logarithmic time instead linear. + seqan3::debug_stream << *it_ra << '\n'; // "T" +} diff --git a/test/unit/range/views/CMakeLists.txt b/test/unit/range/views/CMakeLists.txt index 8942663619..7e3fde4ea6 100644 --- a/test/unit/range/views/CMakeLists.txt +++ b/test/unit/range/views/CMakeLists.txt @@ -12,6 +12,7 @@ seqan3_test(view_pairwise_combine_test.cpp) seqan3_test(view_drop_test.cpp) seqan3_test(view_istreambuf_test.cpp) seqan3_test(view_persist_test.cpp) +seqan3_test(view_pseudo_random_access_test.cpp) seqan3_test(view_rank_to_test.cpp) seqan3_test(view_repeat_n_test.cpp) seqan3_test(view_repeat_test.cpp) diff --git a/test/unit/range/views/view_pseudo_random_access_test.cpp b/test/unit/range/views/view_pseudo_random_access_test.cpp new file mode 100644 index 0000000000..b5a4a1e25e --- /dev/null +++ b/test/unit/range/views/view_pseudo_random_access_test.cpp @@ -0,0 +1,202 @@ +// ----------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2019, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------- + +#include + +#include + +#include +#include +#include + +#include "../iterator_test_template.hpp" + +using namespace seqan3; + +class dummy_common_pseudo_random_access_range +{ +public: + using urng_t = std::vector; + + dummy_common_pseudo_random_access_range() = default; + dummy_common_pseudo_random_access_range(urng_t urng) : urng{std::move(urng)} + {} + + template + class test_iterator : public detail::inherited_iterator_base, u_iterator_t> + { + using base_t = detail::inherited_iterator_base, u_iterator_t>; + using iterator_category = std::bidirectional_iterator_tag; + + using base_t::base_t; + }; + + auto begin() noexcept + { + return test_iterator{urng.begin()}; + } + + auto begin() const + { + return test_iterator{urng.begin()}; + } + + auto end() noexcept + { + return test_iterator{urng.end()}; + } + + auto end() const + { + return test_iterator{urng.end()}; + } + + std::vector urng{}; +}; + +class dummy_non_common_pseudo_random_access_range : public dummy_common_pseudo_random_access_range +{ +public: + + template + class test_iterator : public detail::inherited_iterator_base, u_iterator_t> + { + public: + using base_t = detail::inherited_iterator_base, u_iterator_t>; + using iterator_category = std::bidirectional_iterator_tag; + + using base_t::base_t; + + test_iterator(u_iterator_t it, u_iterator_t last) : base_t{it}, last{last} + {} + + using base_t::operator==; + using base_t::operator!=; + using base_t::operator-; + bool operator==(std::ranges::default_sentinel_t const &) const + { + return static_cast(*this) == last; + } + + friend bool operator==(std::ranges::default_sentinel_t const &, test_iterator const & rhs) + { + return rhs == std::ranges::default_sentinel; + } + + bool operator!=(std::ranges::default_sentinel_t const &) const + { + return !(*this == std::ranges::default_sentinel); + } + + friend bool operator!=(std::ranges::default_sentinel_t const &, test_iterator const & rhs) + { + return rhs != std::ranges::default_sentinel; + } + + typename base_t::difference_type operator-(std::ranges::default_sentinel_t const &) const + { + return static_cast(*this) - this->last; + } + + friend typename base_t::difference_type operator-(std::ranges::default_sentinel_t const &, + test_iterator const & rhs) + { + return rhs.last - static_cast(rhs); + } + + u_iterator_t last{}; + }; + + auto begin() noexcept + { + return test_iterator{urng.begin(), urng.end()}; + } + + auto begin() const + { + return test_iterator{urng.begin(), urng.end()}; + } + + auto end() noexcept + { + return std::ranges::default_sentinel; + } + + auto end() const + { + return std::ranges::default_sentinel; + } +}; + +template +class pseudo_random_access_test : public ::testing::Test +{}; + +using testing_types = ::testing::Types, + dummy_common_pseudo_random_access_range, + dummy_non_common_pseudo_random_access_range>; + +TYPED_TEST_CASE(pseudo_random_access_test, testing_types); + +TYPED_TEST(pseudo_random_access_test, concepts) +{ + using test_type = decltype(std::declval() | views::pseudo_random_access); + + // guaranteed concepts + EXPECT_TRUE(std::ranges::random_access_range); + EXPECT_TRUE(std::ranges::view); + EXPECT_TRUE(std::ranges::viewable_range); + + // preserved concepts + EXPECT_EQ(std::ranges::sized_range, std::ranges::sized_range); + EXPECT_EQ(std::ranges::common_range, std::ranges::common_range); + EXPECT_EQ(std::ranges::contiguous_range, std::ranges::contiguous_range); + EXPECT_EQ(const_iterable_range, const_iterable_range); + EXPECT_EQ((std::ranges::output_range), (std::ranges::output_range)); +} + +TYPED_TEST(pseudo_random_access_test, adaptor) +{ + + std::vector source{0, 1, 2, 3}; + TypeParam test_range{source}; + + // pipe notation + auto v = test_range | views::pseudo_random_access; + EXPECT_TRUE(std::ranges::equal(v, source)); + + // function notation + auto v2 = views::pseudo_random_access(test_range); + EXPECT_TRUE(std::ranges::equal(v2, source)); + + // combinability + auto v3 = test_range | views::pseudo_random_access | std::views::drop(1); + EXPECT_TRUE(std::ranges::equal(v3, std::vector{1, 2, 3})); +} + +// ---------------------------------------------------------------------------- +// iterator test +// ---------------------------------------------------------------------------- + +template +class iterator_fixture> : public ::testing::Test +{ + static_assert(!std::ranges::random_access_range); + + using iterator_tag = std::random_access_iterator_tag; + static constexpr bool const_iterable = true; + + std::vector expected_range{0, 1, 2, 3, 4, 5, 6, 7}; + + rng_t urng{expected_range}; + decltype(urng | views::pseudo_random_access) test_range = urng | views::pseudo_random_access; +}; + +using test_type = ::testing::Types, + std::type_identity>; + +INSTANTIATE_TYPED_TEST_CASE_P(pseudo_random_access_view_iterator, iterator_fixture, test_type); From a34077c266f11147559bf7fb60c191bdcdde3682 Mon Sep 17 00:00:00 2001 From: rrahn Date: Fri, 27 Sep 2019 11:42:50 +0200 Subject: [PATCH 2/6] [MISC] Let banded trace iterator return actual matrix coordinates. The banded trace iterator should return the actual coordinates within the global alignment matrix and not the coordiantes of the underlying storage. This corresponding mapping is only known by the banded trace matrix. --- .../alignment_trace_matrix_full_banded.hpp | 3 +- .../matrix/detail/trace_iterator.hpp | 8 ---- .../matrix/detail/trace_iterator_banded.hpp | 26 ++++++---- .../matrix/detail/trace_iterator_base.hpp | 2 +- .../detail/trace_iterator_banded_test.cpp | 47 +++++++++++++------ .../matrix/detail/trace_iterator_test.cpp | 6 +++ 6 files changed, 58 insertions(+), 34 deletions(-) diff --git a/include/seqan3/alignment/matrix/detail/alignment_trace_matrix_full_banded.hpp b/include/seqan3/alignment/matrix/detail/alignment_trace_matrix_full_banded.hpp index 0c247cf47d..9ebdafaa2a 100644 --- a/include/seqan3/alignment/matrix/detail/alignment_trace_matrix_full_banded.hpp +++ b/include/seqan3/alignment/matrix/detail/alignment_trace_matrix_full_banded.hpp @@ -159,7 +159,8 @@ class alignment_trace_matrix_full_banded : if (trace_begin.row >= static_cast(band_size) || trace_begin.col >= matrix_base_t::num_cols) throw std::invalid_argument{"The given coordinate exceeds the trace matrix size."}; - return path_t{trace_iterator_t{matrix_base_t::data.begin() + matrix_offset{trace_begin}}, + return path_t{trace_iterator_t{matrix_base_t::data.begin() + matrix_offset{trace_begin}, + column_index_type{band_col_index}}, std::ranges::default_sentinel}; } diff --git a/include/seqan3/alignment/matrix/detail/trace_iterator.hpp b/include/seqan3/alignment/matrix/detail/trace_iterator.hpp index 0e58b46dd8..5f6f6dee32 100644 --- a/include/seqan3/alignment/matrix/detail/trace_iterator.hpp +++ b/include/seqan3/alignment/matrix/detail/trace_iterator.hpp @@ -73,12 +73,4 @@ class trace_iterator : public trace_iterator_base, //!\} }; -/*!\name Type deduction guides - * \{ - */ -//!\brief Deduces the template argument from the passed iterator. -template -trace_iterator(matrix_iter_t const) -> trace_iterator; -//!\} - } // namespace seqan3::detail diff --git a/include/seqan3/alignment/matrix/detail/trace_iterator_banded.hpp b/include/seqan3/alignment/matrix/detail/trace_iterator_banded.hpp index c9e8b21b5f..d021adc71a 100644 --- a/include/seqan3/alignment/matrix/detail/trace_iterator_banded.hpp +++ b/include/seqan3/alignment/matrix/detail/trace_iterator_banded.hpp @@ -54,8 +54,14 @@ class trace_iterator_banded : public trace_iterator_base + constexpr trace_iterator_banded(matrix_iter_t const matrix_iter, column_index_type const & pivot_column) + noexcept : + base_t{matrix_iter}, + pivot_column{static_cast(pivot_column.get())} {} /*!\brief Constructs from the underlying trace matrix iterator indicating the start of the trace path. @@ -75,6 +81,14 @@ class trace_iterator_banded : public trace_iterator_base(coord.col - pivot_column); + return coord; + } + private: //!\copydoc seqan3::detail::trace_iterator_base::go_left constexpr void go_left(matrix_iter_t & iter) const noexcept @@ -91,14 +105,8 @@ class trace_iterator_banded : public trace_iterator_base -trace_iterator_banded(matrix_iter_t const) -> trace_iterator_banded; -//!\} + size_t pivot_column{}; //!< The largest column index which is inside of the band in the first row of the matrix. +}; } // namespace seqan3::detail diff --git a/include/seqan3/alignment/matrix/detail/trace_iterator_base.hpp b/include/seqan3/alignment/matrix/detail/trace_iterator_base.hpp index 0307db0014..61e4696bad 100644 --- a/include/seqan3/alignment/matrix/detail/trace_iterator_base.hpp +++ b/include/seqan3/alignment/matrix/detail/trace_iterator_base.hpp @@ -140,7 +140,7 @@ class trace_iterator_base } //!\brief Returns the current coordinate in two-dimensional space. - constexpr matrix_coordinate coordinate() const noexcept + [[nodiscard]] constexpr matrix_coordinate coordinate() const noexcept { return matrix_iter.coordinate(); } diff --git a/test/unit/alignment/matrix/detail/trace_iterator_banded_test.cpp b/test/unit/alignment/matrix/detail/trace_iterator_banded_test.cpp index 6e1f9bc630..6f3837a1f5 100644 --- a/test/unit/alignment/matrix/detail/trace_iterator_banded_test.cpp +++ b/test/unit/alignment/matrix/detail/trace_iterator_banded_test.cpp @@ -31,20 +31,29 @@ struct trace_iterator_banded_test : public ::testing::Test static constexpr trace_directions LO = trace_directions::left_open; two_dimensional_matrix matrix{number_rows{5}, number_cols{6}, std::vector - { + { // emulating band {row:2, col:2} N, N, L, D, D, L, N, LO, D, D, UO, UO, N, D, D, LO, D, U, UO, D, D, D, D, N, U, D, D, D, N, N }}; - - using trace_iterator_type = decltype(trace_iterator_banded{matrix.begin()}); + // Band view + // 0 1 2 3 4 5 + //0 N LO L + //1 UO D D D + //2 U D D D D + //3 D D LO UO L + //4 D D D UO + //5 D D U + + using trace_iterator_type = decltype(trace_iterator_banded{matrix.begin(), column_index_type{0}}); using path_type = std::ranges::subrange; path_type path(matrix_offset const & offset) { - return path_type{trace_iterator_type{matrix.begin() + offset}, std::ranges::default_sentinel}; + return path_type{trace_iterator_type{matrix.begin() + offset, column_index_type{2}}, + std::ranges::default_sentinel}; } }; @@ -56,6 +65,12 @@ TEST_F(trace_iterator_banded_test, concepts) EXPECT_FALSE(std::ranges::bidirectional_range); } +TEST_F(trace_iterator_banded_test, type_deduction) +{ + trace_iterator_banded it{matrix.begin(), column_index_type{0u}}; + EXPECT_TRUE((std::is_same_v>)); +} + TEST_F(trace_iterator_banded_test, trace_path_2_5) { std::vector vec = path(matrix_offset{row_index_type{2}, column_index_type{5}}) | views::to; @@ -69,31 +84,31 @@ TEST_F(trace_iterator_banded_test, coordinate) auto p = path(matrix_offset{row_index_type{2}, column_index_type{5}}); auto it = p.begin(); - EXPECT_EQ(it.coordinate().row, 2u); + EXPECT_EQ(it.coordinate().row, 5u); EXPECT_EQ(it.coordinate().col, 5u); ++it; - EXPECT_EQ(it.coordinate().row, 1u); + EXPECT_EQ(it.coordinate().row, 4u); EXPECT_EQ(it.coordinate().col, 5u); ++it; - EXPECT_EQ(it.coordinate().row, 0u); + EXPECT_EQ(it.coordinate().row, 3u); EXPECT_EQ(it.coordinate().col, 5u); ++it; - EXPECT_EQ(it.coordinate().row, 1u); + EXPECT_EQ(it.coordinate().row, 3u); EXPECT_EQ(it.coordinate().col, 4u); ++it; - EXPECT_EQ(it.coordinate().row, 2u); + EXPECT_EQ(it.coordinate().row, 3u); EXPECT_EQ(it.coordinate().col, 3u); ++it; EXPECT_EQ(it.coordinate().row, 3u); EXPECT_EQ(it.coordinate().col, 2u); ++it; - EXPECT_EQ(it.coordinate().row, 3u); + EXPECT_EQ(it.coordinate().row, 2u); EXPECT_EQ(it.coordinate().col, 1u); ++it; - EXPECT_EQ(it.coordinate().row, 3u); + EXPECT_EQ(it.coordinate().row, 1u); EXPECT_EQ(it.coordinate().col, 0u); ++it; - EXPECT_EQ(it.coordinate().row, 2u); + EXPECT_EQ(it.coordinate().row, 0u); EXPECT_EQ(it.coordinate().col, 0u); } @@ -107,7 +122,7 @@ struct iterator_fixture : public trace_iterator_band using base_t = trace_iterator_banded_test; using iterator_type = typename base_t::trace_iterator_type; - using const_iterator_type = decltype(trace_iterator_banded{base_t::matrix.cbegin()}); + using const_iterator_type = decltype(trace_iterator_banded{base_t::matrix.cbegin(), column_index_type{0}}); // Test forward iterator concept. using iterator_tag = std::forward_iterator_tag; @@ -117,12 +132,14 @@ struct iterator_fixture : public trace_iterator_band { auto begin() { - return iterator_type{matrix.begin() + matrix_offset{row_index_type{2}, column_index_type{5}}}; + return iterator_type{matrix.begin() + matrix_offset{row_index_type{2}, column_index_type{5}}, + column_index_type{2}}; } auto begin() const { - return const_iterator_type{matrix.cbegin() + matrix_offset{row_index_type{2}, column_index_type{5}}}; + return const_iterator_type{matrix.cbegin() + matrix_offset{row_index_type{2}, column_index_type{5}}, + column_index_type{2}}; } auto end() { return std::ranges::default_sentinel; } diff --git a/test/unit/alignment/matrix/detail/trace_iterator_test.cpp b/test/unit/alignment/matrix/detail/trace_iterator_test.cpp index 51acf269fb..6b1b82df06 100644 --- a/test/unit/alignment/matrix/detail/trace_iterator_test.cpp +++ b/test/unit/alignment/matrix/detail/trace_iterator_test.cpp @@ -54,6 +54,12 @@ TEST_F(trace_iterator_fixture, concepts) EXPECT_FALSE(std::ranges::bidirectional_range); } +TEST_F(trace_iterator_fixture, type_deduction) +{ + trace_iterator it{matrix.begin()}; + EXPECT_TRUE((std::is_same_v>)); +} + TEST_F(trace_iterator_fixture, trace_path_2_3) { std::vector vec = path(matrix_offset{row_index_type{2}, column_index_type{3}}) | views::to; From 9f9cda4032e57281dbce0b45adb3f19ddb4448b4 Mon Sep 17 00:00:00 2001 From: rrahn Date: Tue, 17 Sep 2019 13:42:37 +0200 Subject: [PATCH 3/6] [FEATURE] Add is_instantiable_with unary type trait. --- include/seqan3/core/type_traits/lazy.hpp | 43 ++++++++++++++++++- .../is_class_template_declarable_with.cpp | 43 +++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 test/snippet/core/type_traits/is_class_template_declarable_with.cpp diff --git a/include/seqan3/core/type_traits/lazy.hpp b/include/seqan3/core/type_traits/lazy.hpp index d2501680b1..23604d8ade 100644 --- a/include/seqan3/core/type_traits/lazy.hpp +++ b/include/seqan3/core/type_traits/lazy.hpp @@ -63,7 +63,7 @@ struct instantiate> /*!\brief A transformation trait that instantiates seqan3::lazy types. Transformation trait shortcut. * \tparam t The type to operate on. - * \relates seqan3::instantiate + * \relates seqan3::detail::instantiate */ template //!\cond @@ -95,7 +95,7 @@ struct lazy_conditional : instantiate //!\cond @@ -103,5 +103,44 @@ template //!\endcond using lazy_conditional_t = instantiate_t>; +/*!\brief An unary type trait that tests whether a template class can be declared with the given template type + * parameters. + * \implements seqan3::unary_type_trait + * \tparam query_t The type of the template class to test. + * \tparam args_t The template parameter pack to instantiate the template class with. + * + * \details + * + * Note, this unary type trait can be used in a seqan3::detail::lazy_conditional expression to check if instantiating + * a template class with specific template arguments would result in a valid template declaration. Thus, the template + * parameters of the checked class must be constrained accordingly. It is, however, not possible to test if the + * the resulting type is incomplete or not, such that it can not be tested if an instance of the class template with + * the given template arguments can be actually created. + * + * ### Example + * + * \include test/snippet/core/type_traits/is_class_template_declarable_with.cpp + */ +template