SeqAn3  3.0.2
The Modern C++ library for sequence analysis.
alphabet_variant.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2020, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2020, 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 <utility>
20 #include <cassert>
21 #include <variant>
22 
23 #include <meta/meta.hpp>
24 
31 
32 namespace seqan3::detail
33 {
34 
35 #if SEQAN3_WORKAROUND_GCC7_AND_8_CONCEPT_ISSUES
36 template <typename t>
37 SEQAN3_CONCEPT variant_guard_pseudoalphabet = requires { requires seqan3::alphabet_size<t> > 0; };
38 #endif // SEQAN3_WORKAROUND_GCC7_AND_8_CONCEPT_ISSUES
39 
41 template <typename other_t, typename ... alternative_types>
42 inline 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...>, other_t>) &&
45  (!(std::same_as<other_t, alternative_types> || ...)) &&
46  (!list_traits::contains<alphabet_variant<alternative_types...>, recursive_required_types_t<other_t>>)
47 #if SEQAN3_WORKAROUND_GCC7_AND_8_CONCEPT_ISSUES
48  && variant_guard_pseudoalphabet<other_t>
49 #endif // SEQAN3_WORKAROUND_GCC7_AND_8_CONCEPT_ISSUES
50  ;
51 
53 template <typename lhs_t, typename rhs_t, bool lhs_rhs_switched, typename ... alternative_types>
54 inline constexpr bool variant_comparison_guard =
55  (instantiate_if_v<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...>) &&
58  !(lhs_rhs_switched && is_type_specialisation_of_v<rhs_t, alphabet_variant>)
59  > || ...);
60 } // namespace seqan3::detail
61 
62 namespace seqan3
63 {
64 
122 template <typename ...alternative_types>
124  requires (detail::writable_constexpr_alphabet<alternative_types> && ...) &&
125  (std::regular<alternative_types> && ...) &&
126  (sizeof...(alternative_types) >= 2)
127  //TODO same char_type
129 class alphabet_variant : public alphabet_base<alphabet_variant<alternative_types...>,
130  (static_cast<size_t>(alphabet_size<alternative_types>) + ...),
131  char> //TODO underlying char t
132 
133 {
134 private:
136  using base_t = alphabet_base<alphabet_variant<alternative_types...>,
137  (static_cast<size_t>(alphabet_size<alternative_types>) + ...),
138  char>;
140  friend base_t;
141 
143  using alternatives = meta::list<alternative_types...>;
144 
145  static_assert(std::same_as<alternatives, meta::unique<alternatives>>,
146  "All types in a alphabet_variant must be distinct.");
147 
148  using typename base_t::char_type;
149  using typename base_t::rank_type;
150 public:
151  using base_t::alphabet_size;
152  using base_t::to_char;
153  using base_t::to_rank;
154  using base_t::assign_rank;
155 
158  using seqan3_required_types = type_list<alternative_types...>;
161  using seqan3_recursive_required_types =
162  list_traits::concat<seqan3_required_types,
163  detail::transformation_trait_or_t<detail::recursive_required_types<alternative_types>,
164  type_list<>>...>;
165 
171  template <typename alternative_t>
172  static constexpr bool holds_alternative() noexcept
173  {
174  return detail::type_in_pack_v<alternative_t, alternative_types...>;
175  }
176 
180  constexpr alphabet_variant() noexcept = default;
181  constexpr alphabet_variant(alphabet_variant const &) noexcept = default;
182  constexpr alphabet_variant(alphabet_variant &&) noexcept = default;
183  constexpr alphabet_variant & operator=(alphabet_variant const &) noexcept = default;
184  constexpr alphabet_variant & operator=(alphabet_variant &&) noexcept = default;
185  ~alphabet_variant() noexcept = default;
186 
193  template <typename alternative_t>
195  requires (!std::same_as<alternative_t, alphabet_variant>) &&
196  (!std::is_base_of_v<alphabet_variant, alternative_t>) &&
197  (!list_traits::contains<alphabet_variant,
198  detail::transformation_trait_or_t<detail::recursive_required_types<alternative_t>, type_list<>>>) &&
199  (holds_alternative<alternative_t>())
201  constexpr alphabet_variant(alternative_t const alternative) noexcept
202  {
203  assign_rank(rank_by_type_(alternative));
204  }
205 
222  template <typename indirect_alternative_t>
224  requires ((detail::instantiate_if_v<
225  detail::lazy<std::is_convertible, indirect_alternative_t, alternative_types>,
226  detail::variant_general_guard<indirect_alternative_t, alternative_types...>> || ...))
228  constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
229  {
230  assign_rank(
231  rank_by_type_(
232  meta::front<meta::find_if<alternatives,
233  detail::implicitly_convertible_from<indirect_alternative_t>>>(rhs)));
234  }
235 
250  template <typename indirect_alternative_t>
252  requires ((!(detail::instantiate_if_v<
253  detail::lazy<std::is_convertible, indirect_alternative_t, alternative_types>,
254  detail::variant_general_guard<indirect_alternative_t, alternative_types...>> || ...)) &&
255  (detail::instantiate_if_v<
256  detail::lazy<std::is_constructible, alternative_types, indirect_alternative_t>,
257  detail::variant_general_guard<indirect_alternative_t, alternative_types...>> || ...))
259  constexpr explicit alphabet_variant(indirect_alternative_t const rhs) noexcept
260  {
261  assign_rank(rank_by_type_(meta::front<meta::find_if<alternatives,
262  detail::constructible_from<indirect_alternative_t>>>(rhs)));
263  }
264 
265 
274  template <typename indirect_alternative_t>
276  requires (detail::variant_general_guard<indirect_alternative_t, alternative_types...> &&
279  constexpr alphabet_variant & operator=(indirect_alternative_t const & rhs) noexcept
280  {
281  using alternative_t = meta::front<meta::find_if<alternatives, detail::assignable_from<indirect_alternative_t>>>;
282  alternative_t alternative{};
283  alternative = rhs;
284  assign_rank(rank_by_type_(alternative));
285  return *this;
286  }
288 
292  template <size_t index>
295  constexpr bool is_alternative() const noexcept
296  {
297  static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
298  return (to_rank() >= partial_sum_sizes[index]) && (to_rank() < partial_sum_sizes[index + 1]);
299  }
300 
305  template <size_t index>
306  constexpr auto convert_to() const
307  {
308  return convert_impl<index, true>();
309  }
310 
314  template <size_t index>
315  constexpr auto convert_unsafely_to() const noexcept
316  {
317  return convert_impl<index, false>();
318  }
320 
327  template <typename alternative_t>
328  constexpr bool is_alternative() const noexcept
330  requires (holds_alternative<alternative_t>())
332  {
333  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
334  return is_alternative<index>();
335  }
336 
341  template <typename alternative_t>
342  constexpr alternative_t convert_to() const
344  requires (holds_alternative<alternative_t>())
346  {
347  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
348  return convert_impl<index, true>();
349  }
350 
354  template <typename alternative_t>
355  constexpr alternative_t convert_unsafely_to() const noexcept
357  requires (holds_alternative<alternative_t>())
359  {
360  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
361  return convert_impl<index, false>();
362  }
364 
385  template <typename alphabet_variant_t, typename indirect_alternative_type>
386  friend constexpr auto operator==(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept
387  -> std::enable_if_t<detail::variant_comparison_guard<alphabet_variant_t,
388  indirect_alternative_type,
389  false,
390  alternative_types...>,
391  bool>
392  {
393  using alternative_t =
394  meta::front<meta::find_if<alternatives,
395  detail::weakly_equality_comparable_with_<indirect_alternative_type>>>;
396  return lhs.template is_alternative<alternative_t>() && (lhs.template convert_unsafely_to<alternative_t>() == rhs);
397  }
398 
400  template <typename alphabet_variant_t, typename indirect_alternative_type>
401  friend constexpr auto operator!=(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept
402  -> std::enable_if_t<detail::variant_comparison_guard<alphabet_variant_t,
403  indirect_alternative_type,
404  false,
405  alternative_types...>,
406  bool>
407  {
408  return !(lhs == rhs);
409  }
410 
412  template <typename alphabet_variant_t, typename indirect_alternative_type, typename = void>
413  friend constexpr auto operator==(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
414  -> std::enable_if_t<detail::variant_comparison_guard<alphabet_variant_t,
415  indirect_alternative_type,
416  true,
417  alternative_types...>,
418  bool>
419  {
420  return rhs == lhs;
421  }
422 
424  template <typename alphabet_variant_t, typename indirect_alternative_type, typename = void>
425  friend constexpr auto operator!=(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
426  -> std::enable_if_t<detail::variant_comparison_guard<alphabet_variant_t,
427  indirect_alternative_type,
428  true,
429  alternative_types...>,
430  bool>
431  {
432  return rhs != lhs;
433  }
435 
437  static constexpr bool char_is_valid(char_type const chr) noexcept
438  {
439  bool is_valid{false};
440 
441  meta::for_each(alternatives{}, [&] (auto && alt)
442  {
443  if (char_is_valid_for<std::remove_cvref_t<decltype(alt)>>(chr))
444  is_valid = true;
445  });
446 
447  return is_valid;
448  }
449 
450 protected:
452 
457  template <size_t index, bool throws>
458  constexpr auto convert_impl() const noexcept(!throws) -> meta::at_c<alternatives, index>
459  {
460  static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
461  using alternative_t = meta::at_c<alternatives, index>;
462 
463  if constexpr (throws)
464  {
465  if (!is_alternative<index>()) // [[unlikely]]
466  {
467  throw std::bad_variant_access{};
468  }
469  }
470 
471  return seqan3::assign_rank_to(to_rank() - partial_sum_sizes[index], alternative_t{});
472  }
473 
481  static constexpr std::array<rank_type, sizeof...(alternative_types) + 1> partial_sum_sizes = []() constexpr
482  {
483  constexpr size_t N = sizeof...(alternative_types) + 1;
484 
485  std::array<rank_type, N> partial_sum{0, seqan3::alphabet_size<alternative_types>...};
486  for (size_t i = 1u; i < N; ++i)
487  partial_sum[i] += partial_sum[i-1];
488 
489  return partial_sum;
490  }();
491 
499  static constexpr std::array<char_type, alphabet_size> rank_to_char = []() constexpr
500  {
501  // Explicitly writing assign_rank_to_char within assign_rank_to_char
502  // causes this bug (g++-7 and g++-8):
503  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84684
504  auto assign_rank_to_char = [](auto alternative, size_t rank) constexpr
505  {
506  return seqan3::to_char(seqan3::assign_rank_to(rank, alternative));
507  };
508 
509  auto assign_value_to_char = [assign_rank_to_char] (auto alternative, auto & value_to_char, auto & value) constexpr
510  {
511  using alternative_t = std::decay_t<decltype(alternative)>;
512  for (size_t i = 0u; i < seqan3::alphabet_size<alternative_t>; ++i, ++value)
513  value_to_char[value] = assign_rank_to_char(alternative, i);
514  };
515 
516  unsigned value = 0u;
517  std::array<char_type, alphabet_size> value_to_char{};
518 
519  // initializer lists guarantee sequencing;
520  // the following expression behaves as:
521  // for(auto alternative: alternative_types)
522  // assign_rank_to_char(alternative, rank_to_char, value);
523  ((assign_value_to_char(alternative_types{}, value_to_char, value)),...);
524 
525  return value_to_char;
526  }();
527 
532  template <size_t index, typename alternative_t>
534  requires (holds_alternative<alternative_t>())
536  static constexpr rank_type rank_by_index_(alternative_t const & alternative) noexcept
537  {
538  return partial_sum_sizes[index] + static_cast<rank_type>(seqan3::to_rank(alternative));
539  }
540 
545  template <typename alternative_t>
547  requires (holds_alternative<alternative_t>())
549  static constexpr rank_type rank_by_type_(alternative_t const & alternative) noexcept
550  {
551  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
552  return rank_by_index_<index>(alternative);
553  }
554 
562  static constexpr std::array<rank_type, detail::size_in_values_v<char_type>> char_to_rank = []() constexpr
563  {
564  constexpr size_t table_size = detail::size_in_values_v<char_type>;
565 
566  std::array<rank_type, table_size> char_to_rank{};
567 
568  for (size_t i = 0u; i < table_size; ++i)
569  {
570  char_type chr = static_cast<char_type>(i);
571  bool there_was_no_valid_representation{true};
572 
573  meta::for_each(alternatives{}, [&] (auto && alt)
574  {
575  using alt_type = std::remove_cvref_t<decltype(alt)>;
576 
577  if (there_was_no_valid_representation && char_is_valid_for<alt_type>(chr))
578  {
579  there_was_no_valid_representation = false;
580  char_to_rank[i] = rank_by_type_(assign_char_to(chr, alt_type{}));
581  }
582  });
583 
584  if (there_was_no_valid_representation)
585  char_to_rank[i] = rank_by_type_(assign_char_to(chr, meta::front<alternatives>{}));
586  }
587 
588  return char_to_rank;
589  }();
590 };
591 
592 } // namespace seqan3
alphabet_base.hpp
Provides seqan3::alphabet_base.
seqan3::assign_rank_to
constexpr auto assign_rank_to
Assign a rank to an alphabet object.
Definition: concept.hpp:239
utility
seqan3::type_list
meta::list< types... > type_list
Type that contains multiple types, an alias for meta::list.
Definition: type_list.hpp:31
seqan3::alphabet_base< alphabet_variant< alternative_types... >,(static_cast< size_t >(alphabet_size< alternative_types >)+...), char >::alphabet_size
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:176
detail.hpp
Provides implementation detail for seqan3::alphabet_variant and seqan3::alphabet_tuple_base.
seqan3::to_char
constexpr auto to_char
Return the char representation of an alphabet object.
Definition: concept.hpp:321
std::bad_variant_access
seqan3::alphabet_base< alphabet_variant< alternative_types... >,(static_cast< size_t >(alphabet_size< alternative_types >)+...), char >::to_char
constexpr char_type to_char() const noexcept
Return the letter as a character of char_type.
Definition: alphabet_base.hpp:96
seqan3::alphabet_variant::convert_to
constexpr alternative_t convert_to() const
Convert to the specified alphabet (throws if is_alternative() would be false).
Definition: alphabet_variant.hpp:342
seqan3::alphabet_base
A CRTP-base that makes defining a custom alphabet easier.
Definition: alphabet_base.hpp:54
seqan3::to_rank
constexpr auto to_rank
Return the rank representation of a (semi-)alphabet object.
Definition: concept.hpp:143
seqan3::pack_traits::contains
constexpr bool contains
Whether a type occurs in a pack or not.
Definition: traits.hpp:193
seqan3::alphabet_variant
A combined alphabet that can hold values of either of its alternatives.
Definition: alphabet_variant.hpp:133
seqan3::alphabet_variant::holds_alternative
static constexpr bool holds_alternative() noexcept
Returns true if alternative_t is one of the given alternative types.
Definition: alphabet_variant.hpp:172
seqan3::alphabet_variant::operator!=
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:425
tuple_utility.hpp
Provides utility functions for tuple like interfaces.
std::enable_if_t
seqan3::alphabet_variant::convert_unsafely_to
constexpr alternative_t convert_unsafely_to() const noexcept
Convert to the specified alphabet (undefined behaviour if is_alternative() would be false).
Definition: alphabet_variant.hpp:355
seqan3::alphabet_base< alphabet_variant< alternative_types... >,(static_cast< size_t >(alphabet_size< alternative_types >)+...), char >::rank_type
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:64
seqan3::alphabet_variant::char_is_valid
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:437
array
seqan3
The main SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:29
seqan3::pack_traits::find_if
constexpr ptrdiff_t find_if
Get the index of the first type in a pack that satisfies the given predicate.
Definition: traits.hpp:175
seqan3::alphabet_variant::is_alternative
constexpr bool is_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition: alphabet_variant.hpp:295
seqan3::alphabet_variant::convert_unsafely_to
constexpr auto convert_unsafely_to() const noexcept
Convert to the specified alphabet (undefined behaviour if is_alternative() would be false).
Definition: alphabet_variant.hpp:315
pack.hpp
Provides unary type traits on a set of types, usually provided as template argument pack.
seqan3::alphabet_variant::operator==
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:413
seqan3::pack_traits::front
typename decltype(detail::front< pack_t... >())::type front
Return the first type from the type pack.
Definition: traits.hpp:240
std::decay_t
seqan3::alphabet_variant::operator!=
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:401
seqan3::alphabet_base< alphabet_variant< alternative_types... >,(static_cast< size_t >(alphabet_size< alternative_types >)+...), char >::char_type
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:62
seqan3::alphabet_variant::operator==
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:386
std
SeqAn specific customisations in the standard namespace.
cassert
weakly_assignable_from
Resolves to std::is_assignable_v<t>.
seqan3::list_traits::contains
constexpr bool contains
Whether a type occurs in a type list or not.
Definition: traits.hpp:495
seqan3::list_traits::concat
decltype(detail::concat(lists_t{}...)) concat
Join two seqan3::type_list s into one.
Definition: traits.hpp:592
std::remove_cvref_t
seqan3::assign_char_to
constexpr auto assign_char_to
Assign a character to an alphabet object.
Definition: concept.hpp:417
seqan3::alphabet_variant::convert_to
constexpr auto convert_to() const
Convert to the specified alphabet (throws if is_alternative() would be false).
Definition: alphabet_variant.hpp:306
seqan3::alphabet_base< alphabet_variant< alternative_types... >,(static_cast< size_t >(alphabet_size< alternative_types >)+...), char >::assign_rank
constexpr alphabet_variant< alternative_types... > & assign_rank(rank_type const c) noexcept
Assign from a numeric value.
Definition: alphabet_base.hpp:167
seqan3::alphabet_variant::alphabet_variant
constexpr alphabet_variant() noexcept=default
Defaulted.
std::conditional_t
traits.hpp
Provides traits for seqan3::type_list.
std::partial_sum
T partial_sum(T... args)
seqan3::alphabet_base< alphabet_variant< alternative_types... >,(static_cast< size_t >(alphabet_size< alternative_types >)+...), char >::to_rank
constexpr rank_type to_rank() const noexcept
Return the letter's numeric value (rank in the alphabet).
Definition: alphabet_base.hpp:118
lazy.hpp
Provides lazy template instantiation traits.
std::is_base_of_v
T is_base_of_v
variant
seqan3::char_is_valid_for
constexpr auto char_is_valid_for
Returns whether a character is in the valid set of a seqan3::alphabet (usually implies a bijective ma...
Definition: concept.hpp:522