SeqAn3  3.0.3
The Modern C++ library for sequence analysis.
alphabet_variant.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2021, 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 <utility>
21 #include <variant>
22 
27 
28 namespace seqan3::detail
29 {
30 
31 #if SEQAN3_WORKAROUND_GCC7_AND_8_CONCEPT_ISSUES
32 template <typename t>
33 SEQAN3_CONCEPT variant_guard_pseudoalphabet = requires { requires seqan3::alphabet_size<t> > 0; };
34 #endif // SEQAN3_WORKAROUND_GCC7_AND_8_CONCEPT_ISSUES
35 
37 template <typename other_t, typename ... alternative_types>
38 inline constexpr bool variant_general_guard =
39  (!std::same_as<other_t, alphabet_variant<alternative_types...>>) &&
40  (!std::is_base_of_v<alphabet_variant<alternative_types...>, other_t>) &&
41  (!(std::same_as<other_t, alternative_types> || ...)) &&
42  (!list_traits::contains<alphabet_variant<alternative_types...>, recursive_required_types_t<other_t>>)
43 #if SEQAN3_WORKAROUND_GCC7_AND_8_CONCEPT_ISSUES
44  && variant_guard_pseudoalphabet<other_t>
45 #endif // SEQAN3_WORKAROUND_GCC7_AND_8_CONCEPT_ISSUES
46  ;
47 
49 template <typename lhs_t, typename rhs_t, bool lhs_rhs_switched, typename ... alternative_types>
50 inline constexpr bool variant_comparison_guard =
51  (instantiate_if_v<lazy<weakly_equality_comparable_with_trait, rhs_t, alternative_types>,
52  (std::same_as<lhs_t, alphabet_variant<alternative_types...>>) &&
53  (variant_general_guard<rhs_t, alternative_types...>) &&
54  !(lhs_rhs_switched && is_type_specialisation_of_v<rhs_t, alphabet_variant>)
55  > || ...);
56 } // namespace seqan3::detail
57 
58 namespace seqan3
59 {
60 
120 template <typename ...alternative_types>
122  requires (detail::writable_constexpr_alphabet<alternative_types> && ...) &&
123  (std::regular<alternative_types> && ...) &&
124  (sizeof...(alternative_types) >= 2)
125  //TODO same char_type
127 class alphabet_variant : public alphabet_base<alphabet_variant<alternative_types...>,
128  (static_cast<size_t>(alphabet_size<alternative_types>) + ...),
129  char> //TODO underlying char t
130 
131 {
132 private:
134  using base_t = alphabet_base<alphabet_variant<alternative_types...>,
135  (static_cast<size_t>(alphabet_size<alternative_types>) + ...),
136  char>;
138  friend base_t;
139 
141  using alternatives = seqan3::type_list<alternative_types...>;
142 
143  static_assert(((seqan3::list_traits::count<alternative_types, alternatives> == 1) && ... && true),
144  "All types in a alphabet_variant must be distinct.");
145 
146  using typename base_t::char_type;
147  using typename base_t::rank_type;
148 public:
149  using base_t::alphabet_size;
150  using base_t::to_char;
151  using base_t::to_rank;
152  using base_t::assign_rank;
153 
159  using seqan3_required_types = type_list<alternative_types...>;
165  using seqan3_recursive_required_types =
167  detail::transformation_trait_or_t<detail::recursive_required_types<alternative_types>,
168  type_list<>>...>;
169 
177  template <typename alternative_t>
178  static constexpr bool is_alternative() noexcept
179  {
180  return seqan3::pack_traits::contains<alternative_t, alternative_types...>;
181  }
182 
186  constexpr alphabet_variant() noexcept = default;
187  constexpr alphabet_variant(alphabet_variant const &) noexcept = default;
188  constexpr alphabet_variant(alphabet_variant &&) noexcept = default;
189  constexpr alphabet_variant & operator=(alphabet_variant const &) noexcept = default;
190  constexpr alphabet_variant & operator=(alphabet_variant &&) noexcept = default;
191  ~alphabet_variant() noexcept = default;
192 
201  template <typename alternative_t>
203  requires (!std::same_as<alternative_t, alphabet_variant>) &&
204  (!std::is_base_of_v<alphabet_variant, alternative_t>) &&
205  (!list_traits::contains<alphabet_variant,
206  detail::transformation_trait_or_t<detail::recursive_required_types<alternative_t>, type_list<>>>) &&
207  (is_alternative<alternative_t>())
209  constexpr alphabet_variant(alternative_t const alternative) noexcept
210  {
211  assign_rank(rank_by_type_(alternative));
212  }
213 
232  template <typename indirect_alternative_t>
234  requires ((detail::instantiate_if_v<
235  detail::lazy<std::is_convertible, indirect_alternative_t, alternative_types>,
236  detail::variant_general_guard<indirect_alternative_t, alternative_types...>> || ...))
238  constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
239  {
240  using alternative_predicate = detail::implicitly_convertible_from<indirect_alternative_t>;
241  constexpr auto alternative_position = seqan3::list_traits::find_if<alternative_predicate::template invoke,
242  alternatives>;
244  assign_rank(rank_by_type_(alternative_t(rhs)));
245  }
246 
263  template <typename indirect_alternative_t>
265  requires ((!(detail::instantiate_if_v<
266  detail::lazy<std::is_convertible, indirect_alternative_t, alternative_types>,
267  detail::variant_general_guard<indirect_alternative_t, alternative_types...>> || ...)) &&
268  (detail::instantiate_if_v<
269  detail::lazy<std::is_constructible, alternative_types, indirect_alternative_t>,
270  detail::variant_general_guard<indirect_alternative_t, alternative_types...>> || ...))
272  constexpr explicit alphabet_variant(indirect_alternative_t const rhs) noexcept
273  {
274  using alternative_predicate = detail::constructible_from<indirect_alternative_t>;
275  constexpr auto alternative_position = seqan3::list_traits::find_if<alternative_predicate::template invoke,
276  alternatives>;
278  assign_rank(rank_by_type_(alternative_t(rhs)));
279  }
280 
292  template <typename indirect_alternative_t>
294  requires (detail::variant_general_guard<indirect_alternative_t, alternative_types...> &&
297  constexpr alphabet_variant & operator=(indirect_alternative_t const & rhs) noexcept
298  {
299  using alternative_predicate = detail::assignable_from<indirect_alternative_t>;
300  constexpr auto alternative_position = seqan3::list_traits::find_if<alternative_predicate::template invoke,
301  alternatives>;
303  alternative_t alternative{};
304  alternative = rhs;
305  assign_rank(rank_by_type_(alternative));
306  return *this;
307  }
309 
319  template <size_t index>
320  constexpr bool holds_alternative() const noexcept
321  {
322  static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
323  return (to_rank() >= partial_sum_sizes[index]) && (to_rank() < partial_sum_sizes[index + 1]);
324  }
325 
332  template <size_t index>
333  constexpr auto convert_to() const
334  {
335  return convert_impl<index, true>();
336  }
337 
343  template <size_t index>
344  constexpr auto convert_unsafely_to() const noexcept
345  {
346  return convert_impl<index, false>();
347  }
349 
358  template <typename alternative_t>
359  constexpr bool holds_alternative() const noexcept
361  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
377  requires (is_alternative<alternative_t>())
379  {
380  constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
381  return convert_impl<index, true>();
382  }
383 
389  template <typename alternative_t>
390  constexpr alternative_t convert_unsafely_to() const noexcept
392  requires (is_alternative<alternative_t>())
394  {
395  constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
396  return convert_impl<index, false>();
397  }
399 
422  template <typename alphabet_variant_t, typename indirect_alternative_type>
423  friend constexpr auto operator==(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept
424  -> std::enable_if_t<detail::variant_comparison_guard<alphabet_variant_t,
425  indirect_alternative_type,
426  false,
427  alternative_types...>,
428  bool>
429  {
430  using alternative_predicate = detail::weakly_equality_comparable_with_<indirect_alternative_type>;
431  constexpr auto alternative_position = seqan3::list_traits::find_if<alternative_predicate::template invoke,
432  alternatives>;
434  return lhs.template holds_alternative<alternative_t>() && (lhs.template convert_unsafely_to<alternative_t>() == rhs);
435  }
436 
438  template <typename alphabet_variant_t, typename indirect_alternative_type>
439  friend constexpr auto operator!=(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept
440  -> std::enable_if_t<detail::variant_comparison_guard<alphabet_variant_t,
441  indirect_alternative_type,
442  false,
443  alternative_types...>,
444  bool>
445  {
446  return !(lhs == rhs);
447  }
448 
450  template <typename alphabet_variant_t, typename indirect_alternative_type, typename = void>
451  friend constexpr auto operator==(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
452  -> std::enable_if_t<detail::variant_comparison_guard<alphabet_variant_t,
453  indirect_alternative_type,
454  true,
455  alternative_types...>,
456  bool>
457  {
458  return rhs == lhs;
459  }
460 
462  template <typename alphabet_variant_t, typename indirect_alternative_type, typename = void>
463  friend constexpr auto operator!=(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
464  -> std::enable_if_t<detail::variant_comparison_guard<alphabet_variant_t,
465  indirect_alternative_type,
466  true,
467  alternative_types...>,
468  bool>
469  {
470  return rhs != lhs;
471  }
473 
478  static constexpr bool char_is_valid(char_type const chr) noexcept
479  {
480  using index_t = std::make_unsigned_t<char_type>;
481  return first_valid_char_table[static_cast<index_t>(chr)] < sizeof...(alternative_types);
482  }
483 
484 protected:
486 
491  template <size_t index, bool throws>
492  constexpr auto convert_impl() const noexcept(!throws) -> seqan3::list_traits::at<index, alternatives>
493  {
494  static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
495  using alternative_t = seqan3::list_traits::at<index, alternatives>;
496 
497  if constexpr (throws)
498  {
499  if (!holds_alternative<index>()) // [[unlikely]]
500  {
501  throw std::bad_variant_access{};
502  }
503  }
504 
505  return seqan3::assign_rank_to(to_rank() - partial_sum_sizes[index], alternative_t{});
506  }
507 
515  static constexpr std::array<rank_type, sizeof...(alternative_types) + 1> partial_sum_sizes = []() constexpr
516  {
517  constexpr size_t N = sizeof...(alternative_types) + 1;
518 
519  std::array<rank_type, N> partial_sum{0, seqan3::alphabet_size<alternative_types>...};
520  for (size_t i = 1u; i < N; ++i)
521  partial_sum[i] += partial_sum[i-1];
522 
523  return partial_sum;
524  }();
525 
527  static constexpr std::array<char_type, alphabet_size> rank_to_char_table = []() constexpr
528  {
529  // Explicitly writing assign_rank_to_char within assign_rank_to_char
530  // causes this bug (g++-7 and g++-8):
531  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84684
532  auto assign_rank_to_char = [](auto alternative, size_t rank) constexpr
533  {
534  return seqan3::to_char(seqan3::assign_rank_to(rank, alternative));
535  };
536 
537  auto assign_value_to_char = [assign_rank_to_char] (auto alternative, auto & value_to_char, auto & value) constexpr
538  {
539  using alternative_t = std::decay_t<decltype(alternative)>;
540  for (size_t i = 0u; i < seqan3::alphabet_size<alternative_t>; ++i, ++value)
541  value_to_char[value] = assign_rank_to_char(alternative, i);
542  };
543 
544  unsigned value = 0u;
545  std::array<char_type, alphabet_size> value_to_char{};
546 
547  // initializer lists guarantee sequencing;
548  // the following expression behaves as:
549  // for(auto alternative: alternative_types)
550  // assign_rank_to_char(alternative, rank_to_char, value);
551  ((assign_value_to_char(alternative_types{}, value_to_char, value)),...);
552 
553  return value_to_char;
554  }();
555 
560  template <size_t index, typename alternative_t>
562  requires (is_alternative<alternative_t>())
564  static constexpr rank_type rank_by_index_(alternative_t const & alternative) noexcept
565  {
566  return partial_sum_sizes[index] + static_cast<rank_type>(seqan3::to_rank(alternative));
567  }
568 
573  template <typename alternative_t>
575  requires (is_alternative<alternative_t>())
577  static constexpr rank_type rank_by_type_(alternative_t const & alternative) noexcept
578  {
579  constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
580  return rank_by_index_<index>(alternative);
581  }
582 
586  static constexpr auto first_valid_char_table
587  {
588  [] () constexpr
589  {
590  constexpr size_t alternative_size = sizeof...(alternative_types);
591  constexpr size_t table_size = detail::size_in_values_v<char_type>;
592  using first_alphabet_t = detail::min_viable_uint_t<alternative_size>;
593 
595 
596  for (size_t i = 0u; i < table_size; ++i)
597  {
598  char_type chr = static_cast<char_type>(i);
599 
600  std::array<bool, alternative_size> valid_chars{char_is_valid_for<alternative_types>(chr)...};
601 
602 #if defined(__cpp_lib_constexpr_algorithms) && __cpp_lib_constexpr_algorithms >= 201806L
603  // the following lines only works beginning from c++20
604  auto found_it = std::find(valid_chars.begin(), valid_chars.end(), true);
605  lookup_table[i] = found_it - valid_chars.begin();
606 #else
607  size_t found_index = 0u;
608  for (; found_index < valid_chars.size() && !valid_chars[found_index]; ++found_index);
609  lookup_table[i] = found_index;
610 #endif // defined(__cpp_lib_constexpr_algorithms) && __cpp_lib_constexpr_algorithms >= 201806L
611  }
612 
613  return lookup_table;
614  }()
615  };
616 
618  static constexpr std::array<rank_type, detail::size_in_values_v<char_type>> char_to_rank_table = []() constexpr
619  {
620  constexpr size_t alternative_size = sizeof...(alternative_types);
621  constexpr size_t table_size = detail::size_in_values_v<char_type>;
622 
623  std::array<rank_type, table_size> char_to_rank{};
624 
625  for (size_t i = 0u; i < table_size; ++i)
626  {
627  char_type chr = static_cast<char_type>(i);
628 
629  std::array<rank_type, alternative_size> ranks{rank_by_type_(assign_char_to(chr, alternative_types{}))...};
630 
631  // if no char_is_valid_for any alternative use the rank of the first alternative
632  char_to_rank[i] = first_valid_char_table[i] < alternative_size ? ranks[first_valid_char_table[i]] : 0;
633  }
634 
635  return char_to_rank;
636  }();
637 
644  static constexpr char_type rank_to_char(rank_type const rank)
645  {
646  return rank_to_char_table[rank];
647  }
648 
655  static constexpr rank_type char_to_rank(char_type const chr)
656  {
657  using index_t = std::make_unsigned_t<char_type>;
658  return char_to_rank_table[static_cast<index_t>(chr)];
659  }
660 };
661 
662 } // namespace seqan3
Provides seqan3::alphabet_base.
A CRTP-base that makes defining a custom alphabet easier.
Definition: alphabet_base.hpp:81
constexpr char_type to_char() const noexcept
Return the letter as a character of char_type.
Definition: alphabet_base.hpp:139
constexpr rank_type to_rank() const noexcept
Return the letter's numeric value (rank in the alphabet).
Definition: alphabet_base.hpp:185
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:104
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:276
constexpr alphabet_variant< alternative_types... > & assign_rank(rank_type const c) noexcept
Assign from a numeric value.
Definition: alphabet_base.hpp:264
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:96
A combined alphabet that can hold values of either of its alternatives.
Definition: alphabet_variant.hpp:131
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:478
static constexpr bool is_alternative() noexcept
Returns true if alternative_t is one of the given alternative types.
Definition: alphabet_variant.hpp:178
constexpr bool holds_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition: alphabet_variant.hpp:320
constexpr friend 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:423
constexpr friend 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:463
constexpr friend 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:451
constexpr auto convert_to() const
Convert to the specified alphabet (throws if holds_alternative() would be false).
Definition: alphabet_variant.hpp:333
constexpr friend 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:439
constexpr alphabet_variant() noexcept=default
Defaulted.
constexpr alternative_t convert_to() const
Convert to the specified alphabet (throws if holds_alternative() would be false).
Definition: alphabet_variant.hpp:375
constexpr auto convert_unsafely_to() const noexcept
Convert to the specified alphabet (undefined behaviour if holds_alternative() would be false).
Definition: alphabet_variant.hpp:344
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:390
T find(T... args)
constexpr auto assign_char_to
Assign a character to an alphabet object.
Definition: concept.hpp:523
constexpr auto to_char
Return the char representation of an alphabet object.
Definition: concept.hpp:384
constexpr auto assign_rank_to
Assign a rank to an alphabet object.
Definition: concept.hpp:291
constexpr auto to_rank
Return the rank representation of a (semi-)alphabet object.
Definition: concept.hpp:155
decltype(detail::concat(lists_t{}...)) concat
Join two seqan3::type_list s into one.
Definition: traits.hpp:329
constexpr bool contains
Whether a type occurs in a type list or not.
Definition: traits.hpp:231
typename decltype(detail::at< idx >(list_t{}))::type at
Return the type at given index from the type list.
Definition: traits.hpp:260
constexpr ptrdiff_t find_if
Get the index of the first type in a pack that satisfies the given predicate.
Definition: traits.hpp:210
constexpr bool contains
Whether a type occurs in a pack or not.
Definition: traits.hpp:228
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.