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// SPDX-FileCopyrightText: 2006-2024 Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2024 Knut Reinert & MPI für molekulare Genetik
3// SPDX-License-Identifier: BSD-3-Clause
4
12#pragma once
13
14#include <algorithm>
15#include <array>
16#include <cassert>
17#include <span>
18#include <utility>
19#include <variant>
20
25
26namespace seqan3::detail
27{
28
30template <typename t, size_t extent = std::dynamic_extent>
31struct is_span : std::false_type
32{};
33
35template <typename t, size_t extent>
36struct is_span<std::span<t, extent>> : std::true_type
37{};
39
41template <typename other_t, typename... alternative_types>
42inline constexpr bool variant_general_guard =
43 (!std::same_as<other_t, alphabet_variant<alternative_types...>>)&&(
44 !std::is_base_of_v<alphabet_variant<alternative_types...>,
45 other_t>)&&(!(std::same_as<other_t, alternative_types> || ...))
46 && (!list_traits::contains<alphabet_variant<alternative_types...>, recursive_required_types_t<other_t>>);
47
49template <typename lhs_t, typename rhs_t, bool lhs_rhs_switched, typename... alternative_types>
50inline constexpr bool variant_comparison_guard =
51 (instantiate_if_v<
52 lazy<weakly_equality_comparable_with_trait, rhs_t, alternative_types>,
53 (std::same_as<lhs_t, alphabet_variant<alternative_types...>>)&&(
54 variant_general_guard<rhs_t, alternative_types...>)&&!(lhs_rhs_switched
55 && is_type_specialisation_of_v<rhs_t,
56 alphabet_variant>)>
57 || ...);
58} // namespace seqan3::detail
59
60namespace seqan3
61{
62
122template <typename... alternative_types>
123 requires (detail::writable_constexpr_alphabet<alternative_types> && ...) && (std::regular<alternative_types> && ...)
124 && (sizeof...(alternative_types) >= 2)
126 public alphabet_base<alphabet_variant<alternative_types...>,
127 (static_cast<size_t>(alphabet_size<alternative_types>) + ...),
128 char>
129{
130private:
132 using base_t = alphabet_base<alphabet_variant<alternative_types...>,
133 (static_cast<size_t>(alphabet_size<alternative_types>) + ...),
134 char>;
135
136 static_assert((std::is_same_v<alphabet_char_t<alternative_types>, char> && ...),
137 "The alphabet_variant is currently only tested for alphabets with char_type char. "
138 "Contact us on GitHub if you have a different use case: https://github.com/seqan/seqan3 .");
139
141 friend base_t;
142
144 using alternatives = seqan3::type_list<alternative_types...>;
145
146 static_assert(((seqan3::list_traits::count<alternative_types, alternatives> == 1) && ... && true),
147 "All types in a alphabet_variant must be distinct.");
148
149 using typename base_t::char_type;
150 using typename base_t::rank_type;
151
152public:
155 using base_t::to_char;
156 using base_t::to_rank;
157
163 using seqan3_required_types = type_list<alternative_types...>;
169 using seqan3_recursive_required_types = list_traits::concat<
171 detail::transformation_trait_or_t<detail::recursive_required_types<alternative_types>, type_list<>>...>;
172
180 template <typename alternative_t>
181 static constexpr bool is_alternative() noexcept
182 {
183 return seqan3::pack_traits::contains<alternative_t, alternative_types...>;
184 }
185
189 constexpr alphabet_variant() noexcept = default;
190 constexpr alphabet_variant(alphabet_variant const &) noexcept = default;
191 constexpr alphabet_variant(alphabet_variant &&) noexcept = default;
192 constexpr alphabet_variant & operator=(alphabet_variant const &) noexcept = default;
193 constexpr alphabet_variant & operator=(alphabet_variant &&) noexcept = default;
194 ~alphabet_variant() noexcept = default;
195
204 template <typename alternative_t>
205 requires (!std::same_as<alternative_t, alphabet_variant>) && (!detail::is_span<alternative_t>::value)
206 && (!std::is_base_of_v<alphabet_variant, alternative_t>)
207 && (!list_traits::contains<
209 detail::transformation_trait_or_t<detail::recursive_required_types<alternative_t>, type_list<>>>)
210 && (is_alternative<alternative_t>())
211 constexpr alphabet_variant(alternative_t const alternative) noexcept
212 {
213 assign_rank(rank_by_type_(alternative));
214 }
215
234 template <typename indirect_alternative_t>
235 requires (!detail::is_span<indirect_alternative_t>::value)
236 && ((
237 detail::instantiate_if_v<detail::lazy<std::is_convertible, indirect_alternative_t, alternative_types>,
238 detail::variant_general_guard<indirect_alternative_t, alternative_types...>>
239 || ...))
240 constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
241 {
242 using alternative_predicate = detail::implicitly_convertible_from<indirect_alternative_t>;
243 constexpr auto alternative_position =
244 seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
246 assign_rank(rank_by_type_(alternative_t(rhs)));
247 }
248
265 template <typename indirect_alternative_t>
266 requires (
267 (!(detail::instantiate_if_v<detail::lazy<std::is_convertible, indirect_alternative_t, alternative_types>,
268 detail::variant_general_guard<indirect_alternative_t, alternative_types...>>
269 || ...))
270 && (detail::instantiate_if_v<detail::lazy<std::is_constructible, alternative_types, indirect_alternative_t>,
271 detail::variant_general_guard<indirect_alternative_t, alternative_types...>>
272 || ...))
273 constexpr explicit alphabet_variant(indirect_alternative_t const rhs) noexcept
274 {
275 using alternative_predicate = detail::constructible_from<indirect_alternative_t>;
276 constexpr auto alternative_position =
277 seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
279 assign_rank(rank_by_type_(alternative_t(rhs)));
280 }
281
293 template <typename indirect_alternative_t>
294 requires (detail::variant_general_guard<indirect_alternative_t, alternative_types...>
296 constexpr alphabet_variant & operator=(indirect_alternative_t const & rhs) noexcept
297 {
298 using alternative_predicate = detail::assignable_from<indirect_alternative_t>;
299 constexpr auto alternative_position =
300 seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
302 alternative_t alternative{};
303 alternative = rhs;
304 assign_rank(rank_by_type_(alternative));
305 return *this;
306 }
308
318 template <size_t index>
319 constexpr bool holds_alternative() const noexcept
320 {
321 static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
322 return (to_rank() >= partial_sum_sizes[index]) && (to_rank() < partial_sum_sizes[index + 1]);
323 }
324
331 template <size_t index>
332 constexpr auto convert_to() const
333 {
334 return convert_impl<index, true>();
335 }
336
342 template <size_t index>
343 constexpr auto convert_unsafely_to() const noexcept
344 {
345 return convert_impl<index, false>();
346 }
348
357 template <typename alternative_t>
358 constexpr bool holds_alternative() const noexcept
359 requires (is_alternative<alternative_t>())
360 {
361 constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
362 return holds_alternative<index>();
363 }
364
371 template <typename alternative_t>
372 constexpr alternative_t convert_to() const
373 requires (is_alternative<alternative_t>())
374 {
375 constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
376 return convert_impl<index, true>();
377 }
378
384 template <typename alternative_t>
385 constexpr alternative_t convert_unsafely_to() const noexcept
386 requires (is_alternative<alternative_t>())
387 {
388 constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
389 return convert_impl<index, false>();
390 }
392
415 template <typename alphabet_variant_t, typename indirect_alternative_type>
416 requires (!detail::is_span<alphabet_variant_t>::value)
417 && (detail::variant_comparison_guard<alphabet_variant_t,
418 indirect_alternative_type,
419 false,
420 alternative_types...>)
421 friend constexpr bool operator==(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept
422 {
423 using alternative_predicate = detail::weakly_equality_comparable_with_<indirect_alternative_type>;
424 constexpr auto alternative_position =
425 seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
427 return lhs.template holds_alternative<alternative_t>()
428 && (lhs.template convert_unsafely_to<alternative_t>() == rhs);
429 }
430
432 template <typename alphabet_variant_t, typename indirect_alternative_type>
433 friend constexpr auto
434 operator!=(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept -> std::enable_if_t<
435 detail::variant_comparison_guard<alphabet_variant_t, indirect_alternative_type, false, alternative_types...>,
436 bool>
437 {
438 return !(lhs == rhs);
439 }
440
442 template <typename alphabet_variant_t, typename indirect_alternative_type, typename = void>
443 requires (!detail::is_span<alphabet_variant_t>::value)
444 && (detail::variant_comparison_guard<alphabet_variant_t,
445 indirect_alternative_type,
446 true,
447 alternative_types...>)
448 friend constexpr bool operator==(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
449 {
450 return rhs == lhs;
451 }
452
454 template <typename alphabet_variant_t, typename indirect_alternative_type, typename = void>
455 friend constexpr auto operator!=(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
457 detail::variant_comparison_guard<alphabet_variant_t, indirect_alternative_type, true, alternative_types...>,
458 bool>
459 {
460 return rhs != lhs;
461 }
463
468 static constexpr bool char_is_valid(char_type const chr) noexcept
469 {
470 using index_t = std::make_unsigned_t<char_type>;
471 return first_valid_char_table[static_cast<index_t>(chr)] < sizeof...(alternative_types);
472 }
473
474protected:
476
481 template <size_t index, bool throws>
482 constexpr auto convert_impl() const noexcept(!throws) -> seqan3::list_traits::at<index, alternatives>
483 {
484 static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
486
487 if constexpr (throws)
488 {
489 if (!holds_alternative<index>()) // [[unlikely]]
490 {
492 }
493 }
494
495 return seqan3::assign_rank_to(to_rank() - partial_sum_sizes[index], alternative_t{});
496 }
497
503 template <size_t index, typename alternative_t>
504 requires (is_alternative<alternative_t>())
505 static constexpr rank_type rank_by_index_(alternative_t const & alternative) noexcept
506 {
507 return partial_sum_sizes[index] + static_cast<rank_type>(seqan3::to_rank(alternative));
508 }
509
515 template <typename alternative_t>
516 requires (is_alternative<alternative_t>())
517 static constexpr rank_type rank_by_type_(alternative_t const & alternative) noexcept
518 {
519 constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
520 return rank_by_index_<index>(alternative);
521 }
522
529 static constexpr char_type rank_to_char(rank_type const rank)
530 {
531 return rank_to_char_table[rank];
532 }
533
540 static constexpr rank_type char_to_rank(char_type const chr)
541 {
542 using index_t = std::make_unsigned_t<char_type>;
543 return char_to_rank_table[static_cast<index_t>(chr)];
544 }
545
546 // clang-format off
554 static constexpr std::array<rank_type, sizeof...(alternative_types) + 1> partial_sum_sizes
555 {
556 []() constexpr {
557 constexpr size_t N = sizeof...(alternative_types) + 1;
558
559 std::array<rank_type, N> partial_sum{0, seqan3::alphabet_size<alternative_types>...};
560
561 for (size_t i = 1u; i < N; ++i)
562 partial_sum[i] += partial_sum[i - 1];
563
564 return partial_sum;
565 }()
566 };
567
569 static constexpr std::array<char_type, alphabet_size> rank_to_char_table
570 {
571 []() constexpr {
572 auto assign_value_to_char = [](auto alternative, auto & value_to_char, auto & value) constexpr
573 {
574 using alternative_t = std::decay_t<decltype(alternative)>;
575 for (size_t i = 0u; i < seqan3::alphabet_size<alternative_t>; ++i, ++value)
576 value_to_char[value] = seqan3::to_char(seqan3::assign_rank_to(i, alternative));
577 };
578
579 size_t value{};
581
582 // initializer lists guarantee sequencing;
583 // the following expression behaves as:
584 // for(auto alternative: alternative_types)
585 // assign_rank_to_char(alternative, rank_to_char, value);
586 ((assign_value_to_char(alternative_types{}, value_to_char, value)), ...);
587
588 return value_to_char;
589 }()
590 };
591
595 static constexpr auto first_valid_char_table
596 {
597 []() constexpr {
598 constexpr size_t alternative_size = sizeof...(alternative_types);
599 constexpr size_t table_size = detail::size_in_values_v<char_type>;
600 using first_alphabet_t = detail::min_viable_uint_t<alternative_size>;
601
603
604 for (size_t i = 0u; i < table_size; ++i)
605 {
606 char_type chr = static_cast<char_type>(i);
607
608 std::array<bool, alternative_size> valid_chars{char_is_valid_for<alternative_types>(chr)...};
609
610 auto found_it = std::find(valid_chars.begin(), valid_chars.end(), true);
611 lookup_table[i] = found_it - valid_chars.begin();
612 }
613
614 return lookup_table;
615 }()
616 };
617
619 static constexpr std::array<rank_type, detail::size_in_values_v<char_type>> char_to_rank_table
620 {
621 []() constexpr {
622 constexpr size_t alternative_size = sizeof...(alternative_types);
623 constexpr size_t table_size = detail::size_in_values_v<char_type>;
624
626
627 for (size_t i = 0u; i < table_size; ++i)
628 {
629 char_type chr = static_cast<char_type>(i);
630
631 std::array<rank_type, alternative_size> ranks{rank_by_type_(assign_char_to(chr, alternative_types{}))...};
632
633 // if no char_is_valid_for any alternative, use the rank of the first alternative
634 char_to_rank[i] = first_valid_char_table[i] < alternative_size ? ranks[first_valid_char_table[i]] : 0;
635 }
636
637 return char_to_rank;
638 }()
639 };
640};
641// clang-format on
642
643} // 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:54
constexpr rank_type to_rank() const noexcept
Return the letter's numeric value (rank in the alphabet).
Definition alphabet_base.hpp:134
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:77
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:196
constexpr char_type to_char() const noexcept
Return the letter as a character of char_type.
Definition alphabet_base.hpp:112
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:69
constexpr alphabet_variant< alternative_types... > & assign_rank(rank_type const c) noexcept
Assign from a numeric value.
Definition alphabet_base.hpp:184
A combined alphabet that can hold values of either of its alternatives..
Definition alphabet_variant.hpp:129
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:468
static constexpr bool is_alternative() noexcept
Returns true if alternative_t is one of the given alternative types.
Definition alphabet_variant.hpp:181
constexpr alternative_t convert_to() const
Convert to the specified alphabet (throws if holds_alternative() would be false).
Definition alphabet_variant.hpp:372
constexpr bool holds_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition alphabet_variant.hpp:319
constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
Constructor for arguments implicitly convertible to an alternative.
Definition alphabet_variant.hpp:240
constexpr bool holds_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition alphabet_variant.hpp:358
constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
Constructor for arguments explicitly (but not implicitly) convertible to an alternative.
Definition alphabet_variant.hpp:273
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:385
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:434
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:448
constexpr auto convert_to() const
Convert to the specified alphabet (throws if holds_alternative() would be false).
Definition alphabet_variant.hpp:332
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:421
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:343
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:455
T find(T... args)
constexpr auto assign_char_to
Assign a character to an alphabet object.
Definition alphabet/concept.hpp:521
constexpr auto to_char
Return the char representation of an alphabet object.
Definition alphabet/concept.hpp:383
constexpr auto assign_rank_to
Assign a rank to an alphabet object.
Definition alphabet/concept.hpp:290
constexpr auto to_rank
Return the rank representation of a (semi-)alphabet object.
Definition alphabet/concept.hpp:152
decltype(detail::concat(lists_t{}...)) concat
Join two seqan3::type_list s into one.
Definition type_list/traits.hpp:339
constexpr bool contains
Whether a type occurs in a type list or not.
Definition type_list/traits.hpp:249
typename decltype(detail::at< idx >(list_t{}))::type at
Return the type at given index from the type list.
Definition type_list/traits.hpp:276
constexpr bool contains
Whether a type occurs in a pack or not.
Definition type_pack/traits.hpp:220
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:26
SeqAn specific customisations in the standard namespace.
T partial_sum(T... args)
Type that contains multiple types.
Definition type_list.hpp:26
Provides traits for seqan3::type_list.
Hide me