SeqAn3 3.4.0-rc.1
The Modern C++ library for sequence analysis.
Loading...
Searching...
No Matches
alphabet_variant.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2006-2023, Knut Reinert & Freie Universität Berlin
3// Copyright (c) 2016-2023, Knut Reinert & MPI für molekulare Genetik
4// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
5// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
6// -----------------------------------------------------------------------------------------------------
7
15#pragma once
16
17#include <algorithm>
18#include <array>
19#include <cassert>
20#include <span>
21#include <utility>
22#include <variant>
23
28
29namespace seqan3::detail
30{
31
33template <typename t, size_t extent = std::dynamic_extent>
34struct is_span : std::false_type
35{};
36
38template <typename t, size_t extent>
39struct is_span<std::span<t, extent>> : std::true_type
40{};
42
44template <typename other_t, typename... alternative_types>
45inline constexpr bool variant_general_guard =
46 (!std::same_as<other_t, alphabet_variant<alternative_types...>>)&&(
47 !std::is_base_of_v<alphabet_variant<alternative_types...>,
48 other_t>)&&(!(std::same_as<other_t, alternative_types> || ...))
49 && (!list_traits::contains<alphabet_variant<alternative_types...>, recursive_required_types_t<other_t>>);
50
52template <typename lhs_t, typename rhs_t, bool lhs_rhs_switched, typename... alternative_types>
53inline constexpr bool variant_comparison_guard =
54 (instantiate_if_v<
55 lazy<weakly_equality_comparable_with_trait, rhs_t, alternative_types>,
56 (std::same_as<lhs_t, alphabet_variant<alternative_types...>>)&&(
57 variant_general_guard<rhs_t, alternative_types...>)&&!(lhs_rhs_switched
58 && is_type_specialisation_of_v<rhs_t,
59 alphabet_variant>)>
60 || ...);
61} // namespace seqan3::detail
62
63namespace seqan3
64{
65
125template <typename... alternative_types>
126 requires (detail::writable_constexpr_alphabet<alternative_types> && ...) && (std::regular<alternative_types> && ...)
127 && (sizeof...(alternative_types) >= 2)
129 public alphabet_base<alphabet_variant<alternative_types...>,
130 (static_cast<size_t>(alphabet_size<alternative_types>) + ...),
131 char>
132{
133private:
135 using base_t = alphabet_base<alphabet_variant<alternative_types...>,
136 (static_cast<size_t>(alphabet_size<alternative_types>) + ...),
137 char>;
138
139 static_assert((std::is_same_v<alphabet_char_t<alternative_types>, char> && ...),
140 "The alphabet_variant is currently only tested for alphabets with char_type char. "
141 "Contact us on GitHub if you have a different use case: https://github.com/seqan/seqan3 .");
142
144 friend base_t;
145
147 using alternatives = seqan3::type_list<alternative_types...>;
148
149 static_assert(((seqan3::list_traits::count<alternative_types, alternatives> == 1) && ... && true),
150 "All types in a alphabet_variant must be distinct.");
151
152 using typename base_t::char_type;
153 using typename base_t::rank_type;
154
155public:
158 using base_t::to_char;
159 using base_t::to_rank;
160
166 using seqan3_required_types = type_list<alternative_types...>;
172 using seqan3_recursive_required_types = list_traits::concat<
174 detail::transformation_trait_or_t<detail::recursive_required_types<alternative_types>, type_list<>>...>;
175
183 template <typename alternative_t>
184 static constexpr bool is_alternative() noexcept
185 {
186 return seqan3::pack_traits::contains<alternative_t, alternative_types...>;
187 }
188
192 constexpr alphabet_variant() noexcept = default;
193 constexpr alphabet_variant(alphabet_variant const &) noexcept = default;
194 constexpr alphabet_variant(alphabet_variant &&) noexcept = default;
195 constexpr alphabet_variant & operator=(alphabet_variant const &) noexcept = default;
196 constexpr alphabet_variant & operator=(alphabet_variant &&) noexcept = default;
197 ~alphabet_variant() noexcept = default;
198
207 template <typename alternative_t>
208 requires (!std::same_as<alternative_t, alphabet_variant>) && (!detail::is_span<alternative_t>::value)
209 && (!std::is_base_of_v<alphabet_variant, alternative_t>)
210 && (!list_traits::contains<
212 detail::transformation_trait_or_t<detail::recursive_required_types<alternative_t>, type_list<>>>)
213 && (is_alternative<alternative_t>())
214 constexpr alphabet_variant(alternative_t const alternative) noexcept
215 {
216 assign_rank(rank_by_type_(alternative));
217 }
218
237 template <typename indirect_alternative_t>
238 requires (!detail::is_span<indirect_alternative_t>::value)
239 && ((
240 detail::instantiate_if_v<detail::lazy<std::is_convertible, indirect_alternative_t, alternative_types>,
241 detail::variant_general_guard<indirect_alternative_t, alternative_types...>>
242 || ...))
243 constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
244 {
245 using alternative_predicate = detail::implicitly_convertible_from<indirect_alternative_t>;
246 constexpr auto alternative_position =
247 seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
249 assign_rank(rank_by_type_(alternative_t(rhs)));
250 }
251
268 template <typename indirect_alternative_t>
269 requires (
270 (!(detail::instantiate_if_v<detail::lazy<std::is_convertible, indirect_alternative_t, alternative_types>,
271 detail::variant_general_guard<indirect_alternative_t, alternative_types...>>
272 || ...))
273 && (detail::instantiate_if_v<detail::lazy<std::is_constructible, alternative_types, indirect_alternative_t>,
274 detail::variant_general_guard<indirect_alternative_t, alternative_types...>>
275 || ...))
276 constexpr explicit alphabet_variant(indirect_alternative_t const rhs) noexcept
277 {
278 using alternative_predicate = detail::constructible_from<indirect_alternative_t>;
279 constexpr auto alternative_position =
280 seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
282 assign_rank(rank_by_type_(alternative_t(rhs)));
283 }
284
296 template <typename indirect_alternative_t>
297 requires (detail::variant_general_guard<indirect_alternative_t, alternative_types...>
299 constexpr alphabet_variant & operator=(indirect_alternative_t const & rhs) noexcept
300 {
301 using alternative_predicate = detail::assignable_from<indirect_alternative_t>;
302 constexpr auto alternative_position =
303 seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
305 alternative_t alternative{};
306 alternative = rhs;
307 assign_rank(rank_by_type_(alternative));
308 return *this;
309 }
311
321 template <size_t index>
322 constexpr bool holds_alternative() const noexcept
323 {
324 static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
325 return (to_rank() >= partial_sum_sizes[index]) && (to_rank() < partial_sum_sizes[index + 1]);
326 }
327
334 template <size_t index>
335 constexpr auto convert_to() const
336 {
337 return convert_impl<index, true>();
338 }
339
345 template <size_t index>
346 constexpr auto convert_unsafely_to() const noexcept
347 {
348 return convert_impl<index, false>();
349 }
351
360 template <typename alternative_t>
361 constexpr bool holds_alternative() const noexcept
362 requires (is_alternative<alternative_t>())
363 {
364 constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
365 return holds_alternative<index>();
366 }
367
374 template <typename alternative_t>
375 constexpr alternative_t convert_to() const
376 requires (is_alternative<alternative_t>())
377 {
378 constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
379 return convert_impl<index, true>();
380 }
381
387 template <typename alternative_t>
388 constexpr alternative_t convert_unsafely_to() const noexcept
389 requires (is_alternative<alternative_t>())
390 {
391 constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
392 return convert_impl<index, false>();
393 }
395
418 template <typename alphabet_variant_t, typename indirect_alternative_type>
419 requires (!detail::is_span<alphabet_variant_t>::value)
420 && (detail::variant_comparison_guard<alphabet_variant_t,
421 indirect_alternative_type,
422 false,
423 alternative_types...>)
424 friend constexpr bool operator==(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept
425 {
426 using alternative_predicate = detail::weakly_equality_comparable_with_<indirect_alternative_type>;
427 constexpr auto alternative_position =
428 seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
430 return lhs.template holds_alternative<alternative_t>()
431 && (lhs.template convert_unsafely_to<alternative_t>() == rhs);
432 }
433
435 template <typename alphabet_variant_t, typename indirect_alternative_type>
436 friend constexpr auto
437 operator!=(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept -> std::enable_if_t<
438 detail::variant_comparison_guard<alphabet_variant_t, indirect_alternative_type, false, alternative_types...>,
439 bool>
440 {
441 return !(lhs == rhs);
442 }
443
445 template <typename alphabet_variant_t, typename indirect_alternative_type, typename = void>
446 requires (!detail::is_span<alphabet_variant_t>::value)
447 && (detail::variant_comparison_guard<alphabet_variant_t,
448 indirect_alternative_type,
449 true,
450 alternative_types...>)
451 friend constexpr bool operator==(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
452 {
453 return rhs == lhs;
454 }
455
457 template <typename alphabet_variant_t, typename indirect_alternative_type, typename = void>
458 friend constexpr auto operator!=(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
460 detail::variant_comparison_guard<alphabet_variant_t, indirect_alternative_type, true, alternative_types...>,
461 bool>
462 {
463 return rhs != lhs;
464 }
466
471 static constexpr bool char_is_valid(char_type const chr) noexcept
472 {
473 using index_t = std::make_unsigned_t<char_type>;
474 return first_valid_char_table[static_cast<index_t>(chr)] < sizeof...(alternative_types);
475 }
476
477protected:
479
484 template <size_t index, bool throws>
485 constexpr auto convert_impl() const noexcept(!throws) -> seqan3::list_traits::at<index, alternatives>
486 {
487 static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
489
490 if constexpr (throws)
491 {
492 if (!holds_alternative<index>()) // [[unlikely]]
493 {
495 }
496 }
497
498 return seqan3::assign_rank_to(to_rank() - partial_sum_sizes[index], alternative_t{});
499 }
500
506 template <size_t index, typename alternative_t>
507 requires (is_alternative<alternative_t>())
508 static constexpr rank_type rank_by_index_(alternative_t const & alternative) noexcept
509 {
510 return partial_sum_sizes[index] + static_cast<rank_type>(seqan3::to_rank(alternative));
511 }
512
518 template <typename alternative_t>
519 requires (is_alternative<alternative_t>())
520 static constexpr rank_type rank_by_type_(alternative_t const & alternative) noexcept
521 {
522 constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
523 return rank_by_index_<index>(alternative);
524 }
525
532 static constexpr char_type rank_to_char(rank_type const rank)
533 {
534 return rank_to_char_table[rank];
535 }
536
543 static constexpr rank_type char_to_rank(char_type const chr)
544 {
545 using index_t = std::make_unsigned_t<char_type>;
546 return char_to_rank_table[static_cast<index_t>(chr)];
547 }
548
549 // clang-format off
557 static constexpr std::array<rank_type, sizeof...(alternative_types) + 1> partial_sum_sizes
558 {
559 []() constexpr {
560 constexpr size_t N = sizeof...(alternative_types) + 1;
561
562 std::array<rank_type, N> partial_sum{0, seqan3::alphabet_size<alternative_types>...};
563
564 for (size_t i = 1u; i < N; ++i)
565 partial_sum[i] += partial_sum[i - 1];
566
567 return partial_sum;
568 }()
569 };
570
572 static constexpr std::array<char_type, alphabet_size> rank_to_char_table
573 {
574 []() constexpr {
575 auto assign_value_to_char = [](auto alternative, auto & value_to_char, auto & value) constexpr
576 {
577 using alternative_t = std::decay_t<decltype(alternative)>;
578 for (size_t i = 0u; i < seqan3::alphabet_size<alternative_t>; ++i, ++value)
579 value_to_char[value] = seqan3::to_char(seqan3::assign_rank_to(i, alternative));
580 };
581
582 size_t value{};
584
585 // initializer lists guarantee sequencing;
586 // the following expression behaves as:
587 // for(auto alternative: alternative_types)
588 // assign_rank_to_char(alternative, rank_to_char, value);
589 ((assign_value_to_char(alternative_types{}, value_to_char, value)), ...);
590
591 return value_to_char;
592 }()
593 };
594
598 static constexpr auto first_valid_char_table
599 {
600 []() constexpr {
601 constexpr size_t alternative_size = sizeof...(alternative_types);
602 constexpr size_t table_size = detail::size_in_values_v<char_type>;
603 using first_alphabet_t = detail::min_viable_uint_t<alternative_size>;
604
606
607 for (size_t i = 0u; i < table_size; ++i)
608 {
609 char_type chr = static_cast<char_type>(i);
610
611 std::array<bool, alternative_size> valid_chars{char_is_valid_for<alternative_types>(chr)...};
612
613 auto found_it = std::find(valid_chars.begin(), valid_chars.end(), true);
614 lookup_table[i] = found_it - valid_chars.begin();
615 }
616
617 return lookup_table;
618 }()
619 };
620
622 static constexpr std::array<rank_type, detail::size_in_values_v<char_type>> char_to_rank_table
623 {
624 []() constexpr {
625 constexpr size_t alternative_size = sizeof...(alternative_types);
626 constexpr size_t table_size = detail::size_in_values_v<char_type>;
627
629
630 for (size_t i = 0u; i < table_size; ++i)
631 {
632 char_type chr = static_cast<char_type>(i);
633
634 std::array<rank_type, alternative_size> ranks{rank_by_type_(assign_char_to(chr, alternative_types{}))...};
635
636 // if no char_is_valid_for any alternative, use the rank of the first alternative
637 char_to_rank[i] = first_valid_char_table[i] < alternative_size ? ranks[first_valid_char_table[i]] : 0;
638 }
639
640 return char_to_rank;
641 }()
642 };
643};
644// clang-format on
645
646} // namespace seqan3
Provides implementation detail for seqan3::alphabet_variant and seqan3::alphabet_tuple_base.
Provides seqan3::alphabet_base.
A CRTP-base that makes defining a custom alphabet easier.
Definition alphabet_base.hpp:57
constexpr rank_type to_rank() const noexcept
Return the letter's numeric value (rank in the alphabet).
Definition alphabet_base.hpp:137
detail::min_viable_uint_t< size - 1 > rank_type
The type of the alphabet when represented as a number (e.g. via to_rank()).
Definition alphabet_base.hpp:80
static constexpr detail::min_viable_uint_t< size > alphabet_size
The size of the alphabet, i.e. the number of different values it can take.
Definition alphabet_base.hpp:199
constexpr char_type to_char() const noexcept
Return the letter as a character of char_type.
Definition alphabet_base.hpp:115
std::conditional_t< std::same_as< char, void >, char, char > char_type
The char representation; conditional needed to make semi alphabet definitions legal.
Definition alphabet_base.hpp:72
constexpr alphabet_variant< alternative_types... > & assign_rank(rank_type const c) noexcept
Assign from a numeric value.
Definition alphabet_base.hpp:187
A combined alphabet that can hold values of either of its alternatives..
Definition alphabet_variant.hpp:132
static constexpr bool char_is_valid(char_type const chr) noexcept
Validate whether a character is valid in the combined alphabet.
Definition alphabet_variant.hpp:471
static constexpr bool is_alternative() noexcept
Returns true if alternative_t is one of the given alternative types.
Definition alphabet_variant.hpp:184
constexpr alternative_t convert_to() const
Convert to the specified alphabet (throws if holds_alternative() would be false).
Definition alphabet_variant.hpp:375
constexpr bool holds_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition alphabet_variant.hpp:322
constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
Constructor for arguments implicitly convertible to an alternative.
Definition alphabet_variant.hpp:243
constexpr bool holds_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition alphabet_variant.hpp:361
constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
Constructor for arguments explicitly (but not implicitly) convertible to an alternative.
Definition alphabet_variant.hpp:276
constexpr alternative_t convert_unsafely_to() const noexcept
Convert to the specified alphabet (undefined behaviour if holds_alternative() would be false).
Definition alphabet_variant.hpp:388
friend constexpr auto operator!=(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept -> std::enable_if_t< detail::variant_comparison_guard< alphabet_variant_t, indirect_alternative_type, false, alternative_types... >, bool >
(In-)Equality comparison against types comparable with alternatives but not convertible to the varian...
Definition alphabet_variant.hpp:437
friend constexpr bool operator==(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
(In-)Equality comparison against types comparable with alternatives but not convertible to the varian...
Definition alphabet_variant.hpp:451
constexpr auto convert_to() const
Convert to the specified alphabet (throws if holds_alternative() would be false).
Definition alphabet_variant.hpp:335
friend constexpr bool operator==(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept
(In-)Equality comparison against types comparable with alternatives but not convertible to the varian...
Definition alphabet_variant.hpp:424
constexpr alphabet_variant() noexcept=default
Defaulted.
constexpr auto convert_unsafely_to() const noexcept
Convert to the specified alphabet (undefined behaviour if holds_alternative() would be false).
Definition alphabet_variant.hpp:346
friend constexpr auto operator!=(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept -> std::enable_if_t< detail::variant_comparison_guard< alphabet_variant_t, indirect_alternative_type, true, alternative_types... >, bool >
(In-)Equality comparison against types comparable with alternatives but not convertible to the varian...
Definition alphabet_variant.hpp:458
T find(T... args)
constexpr auto assign_char_to
Assign a character to an alphabet object.
Definition alphabet/concept.hpp:524
constexpr auto to_char
Return the char representation of an alphabet object.
Definition alphabet/concept.hpp:386
constexpr auto assign_rank_to
Assign a rank to an alphabet object.
Definition alphabet/concept.hpp:293
constexpr auto to_rank
Return the rank representation of a (semi-)alphabet object.
Definition alphabet/concept.hpp:155
decltype(detail::concat(lists_t{}...)) concat
Join two seqan3::type_list s into one.
Definition type_list/traits.hpp:342
constexpr bool contains
Whether a type occurs in a type list or not.
Definition type_list/traits.hpp:252
typename decltype(detail::at< idx >(list_t{}))::type at
Return the type at given index from the type list.
Definition type_list/traits.hpp:279
constexpr bool contains
Whether a type occurs in a pack or not.
Definition type_pack/traits.hpp:223
Resolves to std::is_assignable_v<t>.
T is_base_of_v
Provides lazy template instantiation traits.
The main SeqAn3 namespace.
Definition aligned_sequence_concept.hpp:29
SeqAn specific customisations in the standard namespace.
T partial_sum(T... args)
Type that contains multiple types.
Definition type_list.hpp:29
Provides traits for seqan3::type_list.