Skip to content

Commit

Permalink
Merge pull request #2898 from h-2/views_chars_strictly_to
Browse files Browse the repository at this point in the history
[FEATURE] views::char_strictly_to
  • Loading branch information
eseiler authored Dec 8, 2021
2 parents 5e3527a + 9263bf5 commit 999352e
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 0 deletions.
74 changes: 74 additions & 0 deletions include/seqan3/alphabet/views/char_strictly_to.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// -----------------------------------------------------------------------------------------------------
// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
// Copyright (c) 2016-2021, 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::char_strictly_to.
* \author Hannes Hauswedell <hannes.hauswedell AT fu-berlin.de>
*/

#pragma once

#include <seqan3/std/ranges>

#include <seqan3/alphabet/concept.hpp>
#include <seqan3/alphabet/views/char_to.hpp>
#include <seqan3/alphabet/views/validate_char_for.hpp>

namespace seqan3::views
{
/*!\brief A view over an alphabet, given a range of characters.
* \tparam urng_t The type of the range being processed. See below for requirements. [template parameter is
* omitted in pipe notation]
* \tparam alphabet_t The alphabet to convert to; must satisfy seqan3::alphabet.
* \param[in] urange The range being processed. [parameter is omitted in pipe notation]
* \returns A range of converted elements. See below for the properties of the returned range.
* \throws seqan3::invalid_char_assignment if an invalid character is encountered.
* \ingroup alphabet_views
*
* \details
*
* \header_file{seqan3/alphabet/views/char_strictly_to.hpp}
*
* This view differs from seqan3::views::chars_to in that it throws an exception if an invalid character conversion
* happens. See seqan3::char_strictly_to for more details.
*
* ### View properties
*
* This view is a **deep view**. Given a range-of-range as input (as opposed to just a range), it will apply
* the transformation on the innermost range (instead of the outermost range).
*
* | Concepts and traits | `urng_t` (underlying range type) | `rrng_t` (returned range type) |
* |----------------------------------|:-------------------------------------:|:------------------------------:|
* | std::ranges::input_range | *required* | *preserved* |
* | std::ranges::forward_range | | *preserved* |
* | std::ranges::bidirectional_range | | *preserved* |
* | std::ranges::random_access_range | | *preserved* |
* | std::ranges::contiguous_range | | *lost* |
* | | | |
* | std::ranges::viewable_range | *required* | *guaranteed* |
* | std::ranges::view | | *guaranteed* |
* | std::ranges::sized_range | | *preserved* |
* | std::ranges::common_range | | *preserved* |
* | std::ranges::output_range | | *lost* |
* | std::semiregular | | *preserved* |
* | seqan3::const_iterable_range | | *preserved* |
* | | | |
* | std::ranges::range_reference_t | seqan3::alphabet_char_t<alphabet_t> | `alphabet_t` |
*
* See the \link views views submodule documentation \endlink for detailed descriptions of the view properties.
*
* ### Example
*
* \include test/snippet/alphabet/views/char_strictly_to.cpp
* \hideinitializer
*
* \experimentalapi{Experimental since version 3.2.}
*/
template <alphabet alphabet_type>
inline auto const char_strictly_to = views::validate_char_for<alphabet_type> | views::char_to<alphabet_type>;

} // namespace seqan3::views
85 changes: 85 additions & 0 deletions include/seqan3/alphabet/views/validate_char_for.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// -----------------------------------------------------------------------------------------------------
// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
// Copyright (c) 2016-2021, 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::validate_char_for.
* \author Hannes Hauswedell <hannes.hauswedell AT fu-berlin.de>
*/

#pragma once

#include <seqan3/std/ranges>

#include <seqan3/alphabet/concept.hpp>
#include <seqan3/utility/type_traits/basic.hpp>
#include <seqan3/utility/views/deep.hpp>

namespace seqan3::views
{
/*!\brief An identity view that throws if an encountered character is not valid for the given alphabet.
* \tparam urng_t The type of the range being processed. See below for requirements. [template parameter is
* omitted in pipe notation]
* \tparam alphabet_t The alphabet to check validity for; must satisfy seqan3::alphabet.
* \param[in] urange The range being processed. [parameter is omitted in pipe notation]
* \returns The range of input values. See below for the properties of the returned range.
* \throws seqan3::invalid_char_assignment if an invalid character is encountered.
* \ingroup alphabet_views
*
* \details
*
* \header_file{seqan3/alphabet/views/validate_char_for.hpp}
*
* This view throws if it encounters a character that is not in the valid set of an alphabet. It performs
* no transformation on the elements of the view itself. However, the contiguous property is lost due to the way it
* is currently implemented.
*
* ### View properties
*
* This view is a **deep view**. Given a range-of-range as input (as opposed to just a range), it will apply
* the transformation on the innermost range (instead of the outermost range).
*
* | Concepts and traits | `urng_t` (underlying range type) | `rrng_t` (returned range type) |
* |----------------------------------|:-------------------------------------:|:--------------------------------------:|
* | std::ranges::input_range | *required* | *preserved* |
* | std::ranges::forward_range | | *preserved* |
* | std::ranges::bidirectional_range | | *preserved* |
* | std::ranges::random_access_range | | *preserved* |
* | std::ranges::contiguous_range | | *lost* |
* | | | |
* | 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* |
* | std::semiregular | | *preserved* |
* | seqan3::const_iterable_range | | *preserved* |
* | | | |
* | std::ranges::range_reference_t | seqan3::alphabet_char_t<alphabet_t> | std::ranges::range_reference_t<urng_t> |
*
* See the \link views views submodule documentation \endlink for detailed descriptions of the view properties.
*
* ### Example
*
* \include test/snippet/alphabet/views/validate_char_for.cpp
* \hideinitializer
*
* \experimentalapi{Experimental since version 3.2.}
*/
template <alphabet alphabet_type>
inline auto const validate_char_for = deep{std::views::transform([] <typename char_t> (char_t && in) -> char_t
{
static_assert(std::common_reference_with<char_t, alphabet_char_t<alphabet_type>>,
"The innermost value type must have a common reference to underlying char type of alphabet_type.");

if (!char_is_valid_for<alphabet_type>(in))
{
throw seqan3::invalid_char_assignment{"alphabet_type", in};
}
return std::forward<char_t>(in);
})};

} // namespace seqan3::views
16 changes: 16 additions & 0 deletions test/snippet/alphabet/views/char_strictly_to.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <seqan3/alphabet/nucleotide/dna4.hpp>
#include <seqan3/alphabet/views/char_strictly_to.hpp>
#include <seqan3/core/debug_stream.hpp>

int main()
{
std::string_view str{"ACTTTGATAN"};
try
{
seqan3::debug_stream << (str | seqan3::views::char_strictly_to<seqan3::dna4>); // ACTTTGATA
}
catch (seqan3::invalid_char_assignment const &)
{
seqan3::debug_stream << "\n[ERROR] Invalid char!\n"; // Will throw on parsing 'N'
}
}
2 changes: 2 additions & 0 deletions test/snippet/alphabet/views/char_strictly_to.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ACTTTGATA
[ERROR] Invalid char!
16 changes: 16 additions & 0 deletions test/snippet/alphabet/views/validate_char_for.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <seqan3/alphabet/nucleotide/dna4.hpp>
#include <seqan3/alphabet/views/validate_char_for.hpp>
#include <seqan3/core/debug_stream.hpp>

int main()
{
std::string_view str{"ACTTTGATAN"};
try
{
seqan3::debug_stream << (str | seqan3::views::validate_char_for<seqan3::dna4>); // ACTTTGATA
}
catch (seqan3::invalid_char_assignment const &)
{
seqan3::debug_stream << "\n[ERROR] Invalid char!\n"; // Will throw on parsing 'N'
}
}
2 changes: 2 additions & 0 deletions test/snippet/alphabet/views/validate_char_for.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ACTTTGATA
[ERROR] Invalid char!
2 changes: 2 additions & 0 deletions test/unit/alphabet/views/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
add_subdirectories()

seqan3_test(char_to_test.cpp)
seqan3_test(char_strictly_to_test.cpp)
seqan3_test(complement_test.cpp)
seqan3_test(rank_to_test.cpp)
seqan3_test(to_char_test.cpp)
seqan3_test(to_rank_test.cpp)
seqan3_test(translate_join_test.cpp)
seqan3_test(translate_test.cpp)
seqan3_test(trim_quality_test.cpp)
seqan3_test(validate_char_for_test.cpp)
83 changes: 83 additions & 0 deletions test/unit/alphabet/views/char_strictly_to_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// -----------------------------------------------------------------------------------------------------
// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
// Copyright (c) 2016-2021, 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 <gtest/gtest.h>

#include <seqan3/std/algorithm>
#include <iostream>
#include <seqan3/std/ranges>

#include <seqan3/alphabet/detail/debug_stream_alphabet.hpp>
#include <seqan3/alphabet/nucleotide/dna5.hpp>
#include <seqan3/alphabet/views/char_strictly_to.hpp>
#include <seqan3/test/expect_range_eq.hpp>
#include <seqan3/utility/range/concept.hpp>

using seqan3::operator""_dna5;

TEST(view_char_strictly_to, basic)
{
std::string vec{"ACTTTGATA"};
seqan3::dna5_vector cmp{"ACTTTGATA"_dna5};

// pipe notation
EXPECT_RANGE_EQ(cmp, vec | seqan3::views::char_strictly_to<seqan3::dna5>);

// function notation
EXPECT_RANGE_EQ(cmp, seqan3::views::char_strictly_to<seqan3::dna5>(vec));

// combinability
seqan3::dna5_vector cmp2{"ATAGTTTCA"_dna5};
EXPECT_RANGE_EQ(cmp2, vec | seqan3::views::char_strictly_to<seqan3::dna5> | std::views::reverse);
}

TEST(view_char_strictly_to, deep_view)
{
std::vector<std::string> foo{"ACGTA", "TGCAT"};

auto v = foo | seqan3::views::char_strictly_to<seqan3::dna5>;

ASSERT_EQ(std::ranges::size(v), 2u);
EXPECT_RANGE_EQ(v[0], "ACGTA"_dna5);
EXPECT_RANGE_EQ(v[1], "TGCAT"_dna5);
}

TEST(view_char_strictly_to, concepts)
{
std::string vec{"ACTTTGATA"};
EXPECT_TRUE(std::ranges::input_range<decltype(vec)>);
EXPECT_TRUE(std::ranges::forward_range<decltype(vec)>);
EXPECT_TRUE(std::ranges::bidirectional_range<decltype(vec)>);
EXPECT_TRUE(std::ranges::random_access_range<decltype(vec)>);
EXPECT_TRUE(std::ranges::contiguous_range<decltype(vec)>);
EXPECT_FALSE(std::ranges::view<decltype(vec)>);
EXPECT_TRUE(std::ranges::sized_range<decltype(vec)>);
EXPECT_TRUE(std::ranges::common_range<decltype(vec)>);
EXPECT_TRUE(seqan3::const_iterable_range<decltype(vec)>);
EXPECT_TRUE((std::ranges::output_range<decltype(vec), char>));

auto v1 = vec | seqan3::views::char_strictly_to<seqan3::dna5>;
EXPECT_TRUE(std::ranges::input_range<decltype(v1)>);
EXPECT_TRUE(std::ranges::forward_range<decltype(v1)>);
EXPECT_TRUE(std::ranges::bidirectional_range<decltype(v1)>);
EXPECT_TRUE(std::ranges::random_access_range<decltype(v1)>);
EXPECT_FALSE(std::ranges::contiguous_range<decltype(v1)>);
EXPECT_TRUE(std::ranges::view<decltype(v1)>);
EXPECT_TRUE(std::ranges::sized_range<decltype(v1)>);
EXPECT_TRUE(std::ranges::common_range<decltype(v1)>);
EXPECT_TRUE(seqan3::const_iterable_range<decltype(v1)>);
EXPECT_FALSE((std::ranges::output_range<decltype(v1), seqan3::dna5>));
EXPECT_FALSE((std::ranges::output_range<decltype(v1), char>));
}

TEST(view_char_strictly_to, exception)
{
std::string foo = "ACGPTA";

auto v = foo | seqan3::views::char_strictly_to<seqan3::dna5>;
EXPECT_THROW((std::ranges::equal(v, "ACGNTA"_dna5)), seqan3::invalid_char_assignment);
}
83 changes: 83 additions & 0 deletions test/unit/alphabet/views/validate_char_for_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// -----------------------------------------------------------------------------------------------------
// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
// Copyright (c) 2016-2021, 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 <gtest/gtest.h>

#include <seqan3/std/algorithm>
#include <iostream>
#include <seqan3/std/ranges>

#include <seqan3/alphabet/detail/debug_stream_alphabet.hpp>
#include <seqan3/alphabet/nucleotide/dna5.hpp>
#include <seqan3/alphabet/views/validate_char_for.hpp>
#include <seqan3/test/expect_range_eq.hpp>
#include <seqan3/utility/range/concept.hpp>

using std::literals::string_view_literals::operator""sv;

TEST(view_validate_char_for, basic)
{
std::string vec{"ACTTTGATA"};
std::string cmp{"ACTTTGATA"};

// pipe notation
EXPECT_RANGE_EQ(cmp, vec | seqan3::views::validate_char_for<seqan3::dna5>);

// function notation
EXPECT_RANGE_EQ(cmp, seqan3::views::validate_char_for<seqan3::dna5>(vec));

// combinability
std::string cmp2{"ATAGTTTCA"};
EXPECT_RANGE_EQ(cmp2, vec | seqan3::views::validate_char_for<seqan3::dna5> | std::views::reverse);
}

TEST(view_validate_char_for, deep_view)
{
std::vector<std::string> foo{"ACGTA", "TGCAT"};

auto v = foo | seqan3::views::validate_char_for<seqan3::dna5>;

ASSERT_EQ(std::ranges::size(v), 2u);
EXPECT_RANGE_EQ(v[0], "ACGTA"sv);
EXPECT_RANGE_EQ(v[1], "TGCAT"sv);
}

TEST(view_validate_char_for, concepts)
{
std::string vec{"ACTTTGATA"};
EXPECT_TRUE(std::ranges::input_range<decltype(vec)>);
EXPECT_TRUE(std::ranges::forward_range<decltype(vec)>);
EXPECT_TRUE(std::ranges::bidirectional_range<decltype(vec)>);
EXPECT_TRUE(std::ranges::random_access_range<decltype(vec)>);
EXPECT_TRUE(std::ranges::contiguous_range<decltype(vec)>);
EXPECT_FALSE(std::ranges::view<decltype(vec)>);
EXPECT_TRUE(std::ranges::sized_range<decltype(vec)>);
EXPECT_TRUE(std::ranges::common_range<decltype(vec)>);
EXPECT_TRUE(seqan3::const_iterable_range<decltype(vec)>);
EXPECT_TRUE((std::ranges::output_range<decltype(vec), char>));

auto v1 = vec | seqan3::views::validate_char_for<seqan3::dna5>;
EXPECT_TRUE(std::ranges::input_range<decltype(v1)>);
EXPECT_TRUE(std::ranges::forward_range<decltype(v1)>);
EXPECT_TRUE(std::ranges::bidirectional_range<decltype(v1)>);
EXPECT_TRUE(std::ranges::random_access_range<decltype(v1)>);
EXPECT_FALSE(std::ranges::contiguous_range<decltype(v1)>);
EXPECT_TRUE(std::ranges::view<decltype(v1)>);
EXPECT_TRUE(std::ranges::sized_range<decltype(v1)>);
EXPECT_TRUE(std::ranges::common_range<decltype(v1)>);
EXPECT_TRUE(seqan3::const_iterable_range<decltype(v1)>);
EXPECT_FALSE((std::ranges::output_range<decltype(v1), seqan3::dna5>));
EXPECT_TRUE((std::ranges::output_range<decltype(v1), char>));
}

TEST(view_validate_char_for, exception)
{
std::string foo = "ACGPTA";

auto v = foo | seqan3::views::validate_char_for<seqan3::dna5>;
EXPECT_THROW((std::ranges::equal(v, "ACGNTA"sv)), seqan3::invalid_char_assignment);
}

1 comment on commit 999352e

@vercel
Copy link

@vercel vercel bot commented on 999352e Dec 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.