SeqAn3  3.0.0
The Modern C++ library for sequence analysis.
alphabet_variant.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2019, 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 
14 #pragma once
15 
16 #include <algorithm>
17 #include <array>
18 #include <utility>
19 #include <cassert>
20 #include <variant>
21 
22 #include <meta/meta.hpp>
23 
33 #include <seqan3/std/concepts>
34 #include <seqan3/std/type_traits>
35 
36 namespace seqan3::detail
37 {
38 
52 // default is false
53 template <typename variant_t,
54  template <typename> typename fun_t,
55  typename target_t>
56 inline bool constexpr one_alternative_is = false;
57 
59 
60 // actual implementation
61 template <typename ... alternatives,
62  template <typename> typename fun_t,
63  typename target_t>
64 inline bool constexpr one_alternative_is<alphabet_variant<alternatives...>,
65  fun_t,
66  target_t>
67  = !meta::empty<meta::find_if<meta::list<alternatives...>, fun_t<target_t>>>::value;
68 
69 // guard against self
70 template <typename ... alternatives,
71  template <typename> typename fun_t>
72 inline bool constexpr one_alternative_is<alphabet_variant<alternatives...>,
73  fun_t,
74  alphabet_variant<alternatives...>> = false;
75 
76 // guard against types convertible to self without needing self's constructors
77 template <typename ... alternatives,
78  template <typename> typename fun_t,
79  typename target_t>
80  requires ConvertibleToByMember<target_t, alphabet_variant<alternatives...>>
81 inline bool constexpr one_alternative_is<alphabet_variant<alternatives...>,
82  fun_t,
83  target_t> = false;
84 
85 // guard against tuple composites that contain the variant somewhere (they can implicitly convert at source)
86 template <typename ... alternatives,
87  template <typename> typename fun_t,
88  typename target_t>
89  requires AlphabetTupleBase<target_t> &&
90  meta::in<detail::transformation_trait_or_t<recursive_tuple_components<target_t>, meta::list<>>,
91  alphabet_variant<alternatives...>>::value
92 inline bool constexpr one_alternative_is<alphabet_variant<alternatives...>,
93  fun_t,
94  target_t> = false;
95 
96 // guard against alternatives
97 template <typename ... alternatives,
98  template <typename> typename fun_t,
99  typename target_t>
100  requires type_in_pack_v<target_t, alternatives...>
101 inline bool constexpr one_alternative_is<alphabet_variant<alternatives...>,
102  fun_t,
103  target_t> = false;
104 
105 // guard against alternatives (LHS and RHS switched)
106 template <typename ... alternatives,
107  template <typename> typename fun_t,
108  typename target_t>
109  requires type_in_pack_v<target_t, alternatives...>
110 inline bool constexpr one_alternative_is<target_t,
111  fun_t,
112  alphabet_variant<alternatives...>> = false;
113 
114 // guard against ranges and iterators over self to prevent recursive instantiation
115 template <typename ... alternatives,
116  template <typename> typename fun_t,
117  typename target_t>
118  //NO, it's not possible to use the value_type type trait here
119  requires requires { std::Same<typename target_t::value_type, alphabet_variant<alternatives...>>; }
120 inline bool constexpr one_alternative_is<alphabet_variant<alternatives...>,
121  fun_t,
122  target_t> = false;
123 
124 // guard against pairs/tuples that *might* contain self to prevent recursive instantiation
125 // (applying TupleLike unfortunately does not work because it itself starts recursive instantiation)
126 template <typename ... alternatives,
127  template <typename> typename fun_t,
128  typename target_t>
129  requires TupleSize<target_t> && !AlphabetTupleBase<target_t>
130 inline bool constexpr one_alternative_is<alphabet_variant<alternatives...>,
131  fun_t,
132  target_t> = false;
133 
135 
136 } // namespace seqan3::detail
137 
138 namespace seqan3
139 {
140 
198 template <typename ...alternative_types>
200  requires (detail::WritableConstexprAlphabet<alternative_types> && ...) &&
201  (!std::is_reference_v<alternative_types> && ...) &&
202  (sizeof...(alternative_types) >= 2)
203  //TODO same char_type
205 class alphabet_variant : public alphabet_base<alphabet_variant<alternative_types...>,
206  (static_cast<size_t>(alphabet_size<alternative_types>) + ...),
207  char> //TODO underlying char t
208 
209 {
210 private:
212  using base_t = alphabet_base<alphabet_variant<alternative_types...>,
213  (static_cast<size_t>(alphabet_size<alternative_types>) + ...),
214  char>;
216  friend base_t;
217 
219  using alternatives = meta::list<alternative_types...>;
220 
221  static_assert(std::Same<alternatives, meta::unique<alternatives>>,
222  "All types in a alphabet_variant must be distinct.");
223 
224  using typename base_t::char_type;
225  using typename base_t::rank_type;
226 public:
227  using base_t::alphabet_size;
228  using base_t::to_char;
229  using base_t::to_rank;
230  using base_t::assign_rank;
231 
237  template <typename alternative_t>
238  static constexpr bool holds_alternative() noexcept
239  {
240  return detail::type_in_pack_v<alternative_t, alternative_types...>;
241  }
242 
246  constexpr alphabet_variant() noexcept = default;
247  constexpr alphabet_variant(alphabet_variant const &) noexcept = default;
248  constexpr alphabet_variant(alphabet_variant &&) noexcept = default;
249  constexpr alphabet_variant & operator=(alphabet_variant const &) noexcept = default;
250  constexpr alphabet_variant & operator=(alphabet_variant &&) noexcept = default;
251  ~alphabet_variant() noexcept = default;
252 
259  template <typename alternative_t>
261  requires holds_alternative<alternative_t>()
263  constexpr alphabet_variant(alternative_t const & alternative) noexcept
264  {
265  assign_rank(rank_by_type_(alternative));
266  }
267 
276  template <typename indirect_alternative_t>
278  requires !detail::one_alternative_is<alphabet_variant,
279  detail::implicitly_convertible_from,
280  indirect_alternative_t> &&
281  detail::one_alternative_is<alphabet_variant,
282  detail::constructible_from,
283  indirect_alternative_t>
285  constexpr alphabet_variant(indirect_alternative_t const & rhs) noexcept
286  {
287  assign_rank(rank_by_type_(meta::front<meta::find_if<alternatives,
288  detail::constructible_from<indirect_alternative_t>>>(rhs)));
289  }
290 
292  template <typename indirect_alternative_t>
293  requires detail::one_alternative_is<alphabet_variant,
294  detail::implicitly_convertible_from,
295  indirect_alternative_t>
296  constexpr alphabet_variant(indirect_alternative_t const & rhs) noexcept
297  {
298  assign_rank(
299  rank_by_type_(
300  meta::front<meta::find_if<alternatives,
301  detail::implicitly_convertible_from<indirect_alternative_t>>>(rhs)));
302  }
304 
311  template <typename indirect_alternative_t>
313  requires !detail::one_alternative_is<alphabet_variant,
314  detail::implicitly_convertible_from,
315  indirect_alternative_t> && // constructor takes care
316  !detail::one_alternative_is<alphabet_variant,
317  detail::constructible_from,
318  indirect_alternative_t> && // constructor takes care
319  detail::one_alternative_is<alphabet_variant,
320  detail::assignable_from,
321  indirect_alternative_t>
323  constexpr alphabet_variant & operator=(indirect_alternative_t const & rhs) noexcept
324  {
325  using alternative_t = meta::front<meta::find_if<alternatives, detail::assignable_from<indirect_alternative_t>>>;
326  alternative_t alternative{};
327  alternative = rhs;
328  assign_rank(rank_by_type_(alternative));
329  return *this;
330  }
332 
336  template <size_t index>
339  constexpr bool is_alternative() const noexcept
340  {
341  static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
342  return (to_rank() >= partial_sum_sizes[index]) && (to_rank() < partial_sum_sizes[index + 1]);
343  }
344 
349  template <size_t index>
350  constexpr auto convert_to() const
351  {
352  return convert_impl<index, true>();
353  }
354 
358  template <size_t index>
359  constexpr auto convert_unsafely_to() const noexcept
360  {
361  return convert_impl<index, false>();
362  }
364 
371  template <typename alternative_t>
372  constexpr bool is_alternative() const noexcept
373  requires holds_alternative<alternative_t>()
374  {
375  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
376  return is_alternative<index>();
377  }
378 
383  template <typename alternative_t>
384  constexpr alternative_t convert_to() const
385  requires holds_alternative<alternative_t>()
386  {
387  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
388  return convert_impl<index, true>();
389  }
390 
394  template <typename alternative_t>
395  constexpr alternative_t convert_unsafely_to() const noexcept
396  requires holds_alternative<alternative_t>()
397  {
398  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
399  return convert_impl<index, false>();
400  }
402 
409  template <typename alternative_t>
410  constexpr bool operator==(alternative_t const rhs) const noexcept
411  requires holds_alternative<alternative_t>()
412  {
413  return is_alternative<alternative_t>() && (convert_unsafely_to<alternative_t>() == rhs);
414  }
415 
416  template <typename alternative_t>
417  constexpr bool operator!=(alternative_t const rhs) const noexcept
418  requires holds_alternative<alternative_t>()
419  {
420  return !operator==(rhs);
421  }
423 
431  template <typename indirect_alternative_type>
432  constexpr bool operator==(indirect_alternative_type const rhs) const noexcept
434  requires detail::one_alternative_is<alphabet_variant,
435  detail::weakly_equality_comparable_with,
436  indirect_alternative_type>
438  {
439  using alternative_t =
440  meta::front<meta::find_if<alternatives,
441  detail::weakly_equality_comparable_with<indirect_alternative_type>>>;
442  return is_alternative<alternative_t>() && (convert_unsafely_to<alternative_t>() == rhs);
443  }
444 
445  template <typename indirect_alternative_type>
446  constexpr bool operator!=(indirect_alternative_type const rhs) const noexcept
448  requires detail::one_alternative_is<alphabet_variant,
449  detail::weakly_equality_comparable_with,
450  indirect_alternative_type>
452  {
453  return !operator==(rhs);
454  }
456 
457 protected:
459 
464  template <size_t index, bool throws>
465  constexpr auto convert_impl() const noexcept(!throws) -> meta::at_c<alternatives, index>
466  {
467  static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
468  using alternative_t = meta::at_c<alternatives, index>;
469 
470  if constexpr (throws)
471  {
472  if (!is_alternative<index>()) // [[unlikely]]
473  {
474  throw std::bad_variant_access{};
475  }
476  }
477 
479  return assign_rank_to(to_rank() - partial_sum_sizes[index], alternative_t{});
480  }
481 
489  static constexpr std::array partial_sum_sizes = []() constexpr
490  {
491  constexpr size_t N = sizeof...(alternative_types) + 1;
492 
493  std::array<rank_type, N> partial_sum{0, seqan3::alphabet_size<alternative_types>...};
494  for (size_t i = 1u; i < N; ++i)
495  partial_sum[i] += partial_sum[i-1];
496 
497  return partial_sum;
498  }();
499 
507  static constexpr std::array<char_type, alphabet_size> rank_to_char = []() constexpr
508  {
509  // Explicitly writing assign_rank_to_char within assign_rank_to_char
510  // causes this bug (g++-7 and g++-8):
511  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84684
512  auto assign_rank_to_char = [](auto alternative, size_t rank) constexpr
513  {
514  return seqan3::to_char(seqan3::assign_rank_to(rank, alternative));
515  };
516 
517  auto assign_value_to_char = [assign_rank_to_char] (auto alternative, auto & value_to_char, auto & value) constexpr
518  {
519  using alternative_t = std::decay_t<decltype(alternative)>;
520  for (size_t i = 0u; i < seqan3::alphabet_size<alternative_t>; ++i, ++value)
521  value_to_char[value] = assign_rank_to_char(alternative, i);
522  };
523 
524  unsigned value = 0u;
525  std::array<char_type, alphabet_size> value_to_char{};
526 
527  // initializer lists guarantee sequencing;
528  // the following expression behaves as:
529  // for(auto alternative: alternative_types)
530  // assign_rank_to_char(alternative, rank_to_char, value);
531  ((assign_value_to_char(alternative_types{}, value_to_char, value)),...);
532 
533  return value_to_char;
534  }();
535 
540  template <size_t index, typename alternative_t>
542  requires holds_alternative<alternative_t>()
544  static constexpr rank_type rank_by_index_(alternative_t const & alternative) noexcept
545  {
546  return partial_sum_sizes[index] + static_cast<rank_type>(seqan3::to_rank(alternative));
547  }
548 
553  template <typename alternative_t>
555  requires holds_alternative<alternative_t>()
557  static constexpr rank_type rank_by_type_(alternative_t const & alternative) noexcept
558  {
559  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
560  return rank_by_index_<index>(alternative);
561  }
562 
570  static constexpr std::array char_to_rank = []() constexpr
571  {
572  constexpr size_t table_size = 1 << (sizeof(char_type) * 8);
573 
574  std::array<rank_type, table_size> char_to_rank{};
575 
576  for (size_t i = 0u; i < table_size; ++i)
577  {
578  char_type chr = static_cast<char_type>(i);
579  bool there_was_no_valid_representation{true};
580 
581  meta::for_each(alternatives{}, [&] (auto && alt)
582  {
583  using alt_type = remove_cvref_t<decltype(alt)>;
584 
585  if (there_was_no_valid_representation && char_is_valid_for<alt_type>(chr))
586  {
587  there_was_no_valid_representation = false;
588  char_to_rank[i] = rank_by_type_(assign_char_to(chr, alt_type{}));
589  }
590  });
591 
592  if (there_was_no_valid_representation)
593  char_to_rank[i] = rank_by_type_(assign_char_to(chr, meta::front<alternatives>{}));
594  }
595 
596  return char_to_rank;
597  }();
598 
600  static constexpr bool char_is_valid(char_type const chr) noexcept
601  {
602  bool is_valid{false};
603 
604  meta::for_each(alternatives{}, [&] (auto && alt)
605  {
606  if (char_is_valid_for<remove_cvref_t<decltype(alt)>>(chr))
607  is_valid = true;
608  });
609 
610  return is_valid;
611  }
612 };
613 
619 template <typename lhs_t, typename ...alternative_types>
620 constexpr bool operator==(lhs_t const lhs, alphabet_variant<alternative_types...> const rhs) noexcept
622  requires detail::WeaklyEqualityComparableByMembersWith<alphabet_variant<alternative_types...>, lhs_t> &&
623  !detail::WeaklyEqualityComparableByMembersWith<lhs_t, alphabet_variant<alternative_types...>>
625 {
626  return rhs == lhs;
627 }
628 
629 template <typename lhs_t, typename ...alternative_types>
630 constexpr bool operator!=(lhs_t const lhs, alphabet_variant<alternative_types...> const rhs) noexcept
632  requires detail::WeaklyEqualityComparableByMembersWith<alphabet_variant<alternative_types...>, lhs_t> &&
633  !detail::WeaklyEqualityComparableByMembersWith<lhs_t, alphabet_variant<alternative_types...>>
635 {
636  return rhs != lhs;
637 }
639 
640 } // namespace seqan3
A combined alphabet that can hold values of either of its alternatives.
Definition: alphabet_variant.hpp:205
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:63
Provides concepts for core language types and relations that don&#39;t have concepts in C++20 (yet)...
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:501
constexpr auto assign_rank_to
Assign a rank to an alphabet object.
Definition: concept.hpp:207
Provides metaprogramming utilities for integer types.
T operator!=(T... args)
constexpr auto to_char
Return the char representation of an alphabet object.
Definition: concept.hpp:285
Provides seqan3::detail::transformation_trait_or.
Provides unary type traits on a set of types, usually provided as template argument pack...
constexpr auto to_rank
Return the rank representation of a (semi-)alphabet object.
Definition: concept.hpp:103
std::remove_cv_t< std::remove_reference_t< t > > remove_cvref_t
Return the input type with const, volatile and references removed (type trait).
Definition: basic.hpp:35
T partial_sum(T... args)
The main SeqAn3 namespace.
constexpr alphabet_variant() noexcept=default
Defaulted.
Provides implementation detail for seqan3::alphabet_variant and seqan3::alphabet_tuple_base.
constexpr alphabet_variant< alternative_types... > & assign_rank(rank_type const c) noexcept
Assign from a numeric value.
Definition: alphabet_base.hpp:166
constexpr bool is_alternative() const noexcept requires holds_alternative< alternative_t >()
Whether the variant alphabet currently holds a value of the given alternative.
Definition: alphabet_variant.hpp:372
The Concepts library.
Provides utility functions for tuple like interfaces.
constexpr auto convert_to() const
Convert to the specified alphabet (throws if is_alternative() would be false).
Definition: alphabet_variant.hpp:350
constexpr bool is_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition: alphabet_variant.hpp:339
constexpr alphabet_variant(indirect_alternative_t const &rhs) noexcept
Construction via the value of a type that an alternative type is constructible from.
Definition: alphabet_variant.hpp:285
constexpr auto convert_unsafely_to() const noexcept
Convert to the specified alphabet (undefined behaviour if is_alternative() would be false)...
Definition: alphabet_variant.hpp:359
Provides seqan3::alphabet_base.
constexpr char_type to_char() const noexcept
Return the letter as a character of char_type.
Definition: alphabet_base.hpp:95
static detail::min_viable_uint_t< size > constexpr alphabet_size
The size of the alphabet, i.e. the number of different values it can take.
Definition: alphabet_base.hpp:175
Definition: aligned_sequence_concept.hpp:35
constexpr alphabet_variant & operator=(indirect_alternative_t const &rhs) noexcept
Assignment via a value that one of the alternative types is assignable from.
Definition: alphabet_variant.hpp:323
Provides C++20 additions to the type_traits header.
constexpr rank_type to_rank() const noexcept
Return the letter&#39;s numeric value (rank in the alphabet).
Definition: alphabet_base.hpp:117
constexpr alternative_t convert_to() const requires holds_alternative< alternative_t >()
Convert to the specified alphabet (throws if is_alternative() would be false).
Definition: alphabet_variant.hpp:384
::ranges::empty empty
Alias for ranges::empty. Checks whether a range is empty.
Definition: ranges:194
Core alphabet concept and free function/type trait wrappers.
A CRTP-base that makes defining a custom alphabet easier.
Definition: alphabet_base.hpp:52
static constexpr bool holds_alternative() noexcept
Returns true if alternative_t is one of the given alternative types.
Definition: alphabet_variant.hpp:238
Provides various transformation traits used by the range module.
constexpr auto assign_char_to
Assign a character to an alphabet object.
Definition: concept.hpp:395
The concept std::Same<T, U> is satisfied if and only if T and U denote the same type.
std::conditional_t< std::Same< char, void >, char, char > char_type
The char representation; conditional needed to make semi alphabet definitions legal.
Definition: alphabet_base.hpp:61
constexpr alternative_t convert_unsafely_to() const noexcept requires holds_alternative< alternative_t >()
Convert to the specified alphabet (undefined behaviour if is_alternative() would be false)...
Definition: alphabet_variant.hpp:395