SeqAn3
union_composition.hpp
Go to the documentation of this file.
1 // ==========================================================================
2 // SeqAn - The Library for Sequence Analysis
3 // ==========================================================================
4 //
5 // Copyright (c) 2006-2018, Knut Reinert, FU Berlin
6 // Copyright (c) 2016-2018, Knut Reinert & MPI Molekulare Genetik
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // * Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
14 // * Redistributions in binary form must reproduce the above copyright
15 // notice, this list of conditions and the following disclaimer in the
16 // documentation and/or other materials provided with the distribution.
17 // * Neither the name of Knut Reinert or the FU Berlin nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
25 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 // DAMAGE.
32 //
33 // ==========================================================================
34 
41 #pragma once
42 
43 #include <algorithm>
44 #include <array>
45 #include <utility>
46 #include <cassert>
47 #include <variant>
48 
49 #include <meta/meta.hpp>
50 
60 #include <seqan3/std/concepts>
61 #include <seqan3/std/type_traits>
62 
63 namespace seqan3::detail
64 {
65 
79 // default is false
80 template <typename union_t,
81  template <typename> typename fun_t,
82  typename target_t>
83 inline bool constexpr one_alternative_is = false;
84 
86 
87 // actual implementation
88 template <typename ... alternatives,
89  template <typename> typename fun_t,
90  typename target_t>
91 inline bool constexpr one_alternative_is<union_composition<alternatives...>,
92  fun_t,
93  target_t>
94  = !meta::empty<meta::find_if<meta::list<alternatives...>, fun_t<target_t>>>::value;
95 
96 // guard against self
97 template <typename ... alternatives,
98  template <typename> typename fun_t>
99 inline bool constexpr one_alternative_is<union_composition<alternatives...>,
100  fun_t,
101  union_composition<alternatives...>> = false;
102 
103 // guard against types convertible to self without needing self's constructors
104 template <typename ... alternatives,
105  template <typename> typename fun_t,
106  typename target_t>
107  requires convertible_to_by_member_concept<target_t, union_composition<alternatives...>>
108 inline bool constexpr one_alternative_is<union_composition<alternatives...>,
109  fun_t,
110  target_t> = false;
111 
112 // guard against cartesian compositions that contain the union somewhere (they can implicitly convert at source)
113 template <typename ... alternatives,
114  template <typename> typename fun_t,
115  typename target_t>
116  requires cartesian_composition_concept<target_t> &&
117  meta::in<detail::transformation_trait_or_t<recursive_cartesian_components<target_t>, meta::list<>>,
118  union_composition<alternatives...>>::value
119 inline bool constexpr one_alternative_is<union_composition<alternatives...>,
120  fun_t,
121  target_t> = false;
122 
123 // guard against alternatives
124 template <typename ... alternatives,
125  template <typename> typename fun_t,
126  typename target_t>
127  requires type_in_pack_v<target_t, alternatives...>
128 inline bool constexpr one_alternative_is<union_composition<alternatives...>,
129  fun_t,
130  target_t> = false;
131 
132 // guard against alternatives (LHS and RHS switched)
133 template <typename ... alternatives,
134  template <typename> typename fun_t,
135  typename target_t>
136  requires type_in_pack_v<target_t, alternatives...>
137 inline bool constexpr one_alternative_is<target_t,
138  fun_t,
139  union_composition<alternatives...>> = false;
140 
141 // guard against ranges and iterators over self to prevent recursive instantiation
142 template <typename ... alternatives,
143  template <typename> typename fun_t,
144  typename target_t>
145  //NO, it's not possible to use the value_type metafunction here
146  requires requires { std::Same<typename target_t::value_type, union_composition<alternatives...>>; }
147 inline bool constexpr one_alternative_is<union_composition<alternatives...>,
148  fun_t,
149  target_t> = false;
150 
151 // guard against pairs/tuples that *might* contain self to prevent recursive instantiation
152 // (applying tuple_like_concept unfortunately does not work because it itself starts recursive instantiation)
153 template <typename ... alternatives,
154  template <typename> typename fun_t,
155  typename target_t>
156  requires tuple_size_concept<target_t> && !cartesian_composition_concept<target_t>
157 inline bool constexpr one_alternative_is<union_composition<alternatives...>,
158  fun_t,
159  target_t> = false;
160 
162 
163 } // namespace seqan3::detail
164 
165 namespace seqan3
166 {
167 
197 template <typename ...alternative_types>
199  requires (detail::constexpr_alphabet_concept<alternative_types> && ...) &&
200  (sizeof...(alternative_types) >= 2)
201  //TODO same char_type
203 class union_composition : public alphabet_base<union_composition<alternative_types...>,
204  (static_cast<size_t>(alphabet_size_v<alternative_types>) + ...),
205  char> //TODO underlying char t
206 
207 {
208 private:
210  using base_t = alphabet_base<union_composition<alternative_types...>,
211  (static_cast<size_t>(alphabet_size_v<alternative_types>) + ...),
212  char>;
214  friend base_t;
215 
217  using alternatives = meta::list<alternative_types...>;
218 
219  static_assert(std::Same<alternatives, meta::unique<alternatives>>,
220  "All types in a union_composition must be distinct.");
221 
222 public:
223  using base_t::value_size;
224  using base_t::to_char;
225  using base_t::to_rank;
226  using base_t::assign_rank;
227  using typename base_t::char_type;
228  using typename base_t::rank_type;
229 
235  template <typename alternative_t>
236  static constexpr bool holds_alternative() noexcept
237  {
238  return detail::type_in_pack_v<alternative_t, alternative_types...>;
239  }
240 
244  constexpr union_composition() = default;
245  constexpr union_composition(union_composition const &) = default;
246  constexpr union_composition(union_composition &&) = default;
247  constexpr union_composition & operator=(union_composition const &) = default;
248  constexpr union_composition & operator=(union_composition &&) = default;
249  ~union_composition() = default;
250 
257  template <typename alternative_t>
259  requires holds_alternative<alternative_t>()
261  constexpr union_composition(alternative_t const & alternative) noexcept
262  {
263  assign_rank(rank_by_type_(alternative));
264  }
265 
274  template <typename indirect_alternative_t>
276  requires !detail::one_alternative_is<union_composition,
277  detail::implicitly_convertible_from,
278  indirect_alternative_t> &&
279  detail::one_alternative_is<union_composition,
280  detail::constructible_from,
281  indirect_alternative_t>
283  constexpr union_composition(indirect_alternative_t const & rhs) noexcept
284  {
285  assign_rank(rank_by_type_(meta::front<meta::find_if<alternatives,
286  detail::constructible_from<indirect_alternative_t>>>(rhs)));
287  }
288 
290  template <typename indirect_alternative_t>
291  requires detail::one_alternative_is<union_composition,
292  detail::implicitly_convertible_from,
293  indirect_alternative_t>
294  constexpr union_composition(indirect_alternative_t const & rhs) noexcept
295  {
296  assign_rank(
297  rank_by_type_(
298  meta::front<meta::find_if<alternatives,
299  detail::implicitly_convertible_from<indirect_alternative_t>>>(rhs)));
300  }
302 
309  template <typename indirect_alternative_t>
311  requires !detail::one_alternative_is<union_composition,
312  detail::implicitly_convertible_from,
313  indirect_alternative_t> && // constructor takes care
314  !detail::one_alternative_is<union_composition,
315  detail::constructible_from,
316  indirect_alternative_t> && // constructor takes care
317  detail::one_alternative_is<union_composition,
318  detail::assignable_from,
319  indirect_alternative_t>
321  constexpr union_composition & operator=(indirect_alternative_t const & rhs) noexcept
322  {
323  using alternative_t = meta::front<meta::find_if<alternatives, detail::assignable_from<indirect_alternative_t>>>;
324  alternative_t alternative{};
325  alternative = rhs;
326  assign_rank(rank_by_type_(alternative));
327  return *this;
328  }
330 
334  template <size_t index>
337  constexpr bool is_alternative() const noexcept
338  {
339  static_assert(index < value_size, "The union_composition contains less alternatives than you are checking.");
340  return (to_rank() >= partial_sum_sizes[index]) && (to_rank() < partial_sum_sizes[index + 1]);
341  }
342 
347  template <size_t index>
348  constexpr auto convert_to() const
349  {
350  return convert_impl<index, true>();
351  }
352 
356  template <size_t index>
357  constexpr auto convert_unsafely_to() const noexcept
358  {
359  return convert_impl<index, false>();
360  }
362 
369  template <typename alternative_t>
370  constexpr bool is_alternative() const noexcept
371  requires holds_alternative<alternative_t>()
372  {
373  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
374  return is_alternative<index>();
375  }
376 
381  template <typename alternative_t>
382  constexpr alternative_t convert_to() const
383  requires holds_alternative<alternative_t>()
384  {
385  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
386  return convert_impl<index, true>();
387  }
388 
392  template <typename alternative_t>
393  constexpr alternative_t convert_unsafely_to() const noexcept
394  requires holds_alternative<alternative_t>()
395  {
396  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
397  return convert_impl<index, false>();
398  }
400 
407  template <typename alternative_t>
408  constexpr bool operator==(alternative_t const & rhs) const noexcept
409  requires holds_alternative<alternative_t>()
410  {
411  return is_alternative<alternative_t>() && (convert_unsafely_to<alternative_t>() == rhs);
412  }
413 
414  template <typename alternative_t>
415  constexpr bool operator!=(alternative_t const & rhs) const noexcept
416  requires holds_alternative<alternative_t>()
417  {
418  return !operator==(rhs);
419  }
421 
429  template <typename indirect_alternative_type>
430  constexpr bool operator==(indirect_alternative_type const & rhs) const noexcept
432  requires detail::one_alternative_is<union_composition,
433  detail::weakly_equality_comparable_with,
434  indirect_alternative_type>
436  {
437  using alternative_t =
438  meta::front<meta::find_if<alternatives,
439  detail::weakly_equality_comparable_with<indirect_alternative_type>>>;
440  return is_alternative<alternative_t>() && (convert_unsafely_to<alternative_t>() == rhs);
441  }
442 
443  template <typename indirect_alternative_type>
444  constexpr bool operator!=(indirect_alternative_type const & rhs) const noexcept
446  requires detail::one_alternative_is<union_composition,
447  detail::weakly_equality_comparable_with,
448  indirect_alternative_type>
450  {
451  return !operator==(rhs);
452  }
454 
455 protected:
457 
462  template <size_t index, bool throws>
463  constexpr auto convert_impl() const noexcept(!throws) -> meta::at_c<alternatives, index>
464  {
465  static_assert(index < value_size, "The union_composition contains less alternatives than you are checking.");
466  using alternative_t = meta::at_c<alternatives, index>;
467 
468  if constexpr (throws)
469  {
470  if (!is_alternative<index>()) // [[unlikely]]
471  {
472  throw std::bad_variant_access{};
473  }
474  }
475 
476  using seqan3::assign_rank;
477  return assign_rank(alternative_t{}, to_rank() - partial_sum_sizes[index]);
478  }
479 
487  static constexpr std::array partial_sum_sizes = []() constexpr
488  {
489  constexpr size_t N = sizeof...(alternative_types) + 1;
490 
491  std::array<rank_type, N> partial_sum{0, alphabet_size_v<alternative_types>...};
492  for (size_t i = 1u; i < N; ++i)
493  partial_sum[i] += partial_sum[i-1];
494 
495  return partial_sum;
496  }();
497 
505  static constexpr std::array<char_type, value_size> rank_to_char = []() constexpr
506  {
507  // Explicitly writing assign_rank_to_char within assign_rank_to_char
508  // causes this bug (g++-7 and g++-8):
509  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84684
510  auto assign_rank_to_char = [](auto alternative, size_t rank) constexpr
511  {
512  return seqan3::to_char(seqan3::assign_rank(alternative, rank));
513  };
514 
515  auto assign_value_to_char = [assign_rank_to_char] (auto alternative, auto & value_to_char, auto & value) constexpr
516  {
517  using alternative_t = std::decay_t<decltype(alternative)>;
518  for (size_t i = 0u; i < alphabet_size_v<alternative_t>; ++i, ++value)
519  value_to_char[value] = assign_rank_to_char(alternative, i);
520  };
521 
522  unsigned value = 0u;
523  std::array<char_type, value_size> value_to_char{};
524 
525  // initializer lists guarantee sequencing;
526  // the following expression behaves as:
527  // for(auto alternative: alternative_types)
528  // assign_rank_to_char(alternative, rank_to_char, value);
529  ((assign_value_to_char(alternative_types{}, value_to_char, value)),...);
530 
531  return value_to_char;
532  }();
533 
541  static constexpr std::array char_to_rank = []() constexpr
542  {
543  constexpr size_t table_size = 1 << (sizeof(char_type) * 8);
544 
545  std::array<rank_type, table_size> char_to_rank{};
546  for (size_t i = 0u; i < rank_to_char.size(); ++i)
547  {
548  using index_t = std::make_unsigned_t<char_type>;
549  rank_type & old_entry = char_to_rank[static_cast<index_t>(rank_to_char[i])];
550  bool is_new_entry = rank_to_char[0] != rank_to_char[i] && old_entry == 0;
551  if (is_new_entry)
552  old_entry = static_cast<rank_type>(i);
553  }
554  return char_to_rank;
555  }();
556 
561  template <size_t index, typename alternative_t>
563  requires holds_alternative<alternative_t>()
565  static constexpr rank_type rank_by_index_(alternative_t const & alternative) noexcept
566  {
567  return partial_sum_sizes[index] + static_cast<rank_type>(seqan3::to_rank(alternative));
568  }
569 
574  template <typename alternative_t>
576  requires holds_alternative<alternative_t>()
578  static constexpr rank_type rank_by_type_(alternative_t const & alternative) noexcept
579  {
580  constexpr size_t index = meta::find_index<alternatives, alternative_t>::value;
581  return rank_by_index_<index>(alternative);
582  }
583 };
584 
590 template <typename lhs_t, typename ...alternative_types>
591 constexpr bool operator==(lhs_t const & lhs, union_composition<alternative_types...> const & rhs) noexcept
593  requires detail::weakly_equality_comparable_by_members_with_concept<union_composition<alternative_types...>, lhs_t> &&
594  !detail::weakly_equality_comparable_by_members_with_concept<lhs_t, union_composition<alternative_types...>>
596 {
597  return rhs == lhs;
598 }
599 
600 template <typename lhs_t, typename ...alternative_types>
601 constexpr bool operator!=(lhs_t const & lhs, union_composition<alternative_types...> const & rhs) noexcept
603  requires detail::weakly_equality_comparable_by_members_with_concept<union_composition<alternative_types...>, lhs_t> &&
604  !detail::weakly_equality_comparable_by_members_with_concept<lhs_t, union_composition<alternative_types...>>
606 {
607  return rhs != lhs;
608 }
610 
611 } // namespace seqan3
Provides concepts for core language types and relations that don&#39;t have concepts in C++20 (yet)...
constexpr auto convert_unsafely_to() const noexcept
Convert to the specified alphabet (undefined behaviour if is_alternative() would be false)...
Definition: union_composition.hpp:357
Contains metaprogramming utilities for integer types.
Provides seqan3::detail::transformation_trait_or.
Provides various metafunctions on a set of types, usually provided as template argument pack...
The main SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:58
Provides implementation detail for seqan3::union_composition and seqan3::cartesian_composition.
constexpr bool is_alternative() const noexcept requires holds_alternative< alternative_t >()
Whether the union alphabet currently holds a value of the given alternative.
Definition: union_composition.hpp:370
constexpr union_composition(indirect_alternative_t const &rhs) noexcept
Construction via the value of a type that an alternative type is constructible from.
Definition: union_composition.hpp:283
A combined alphabet that can hold values of either of its alternatives.
Definition: detail.hpp:194
constexpr bool is_alternative() const noexcept
Whether the union alphabet currently holds a value of the given alternative.
Definition: union_composition.hpp:337
constexpr union_composition(alternative_t const &alternative) noexcept
Construction via the value of an alternative.
Definition: union_composition.hpp:261
constexpr alphabet_type & assign_rank(alphabet_type &alph, underlying_rank_t< alphabet_type > const rank) requires requires(alphabet_type alph)
Implementation of seqan3::semi_alphabet_concept::assign_rank() that delegates to a member function...
Definition: member_exposure.hpp:110
The Concepts library.
static constexpr bool holds_alternative() noexcept
Returns true if alternative_t is one of the given alternative types.
Definition: union_composition.hpp:236
Provides utility functions for tuple like interfaces.
char_t char_type
The type of the alphabet when converted to char (e.g. via to_char()).
Definition: alphabet_base.hpp:87
constexpr auto convert_to() const
Convert to the specified alphabet (throws if is_alternative() would be false).
Definition: union_composition.hpp:348
Provides seqan3::alphabet_base.
The concept std::Same<T, U> is satisfied if and only if T and U denote the same type.
auto const to_char
A view that calls seqan3::to_char() on each element in the input range.
Definition: to_char.hpp:88
Definition: aligned_sequence_concept.hpp:288
constexpr union_composition & operator=(indirect_alternative_t const &rhs) noexcept
Assignment via a value that one of the alternative types is assignable from.
Definition: union_composition.hpp:321
constexpr alternative_t convert_to() const requires holds_alternative< alternative_t >()
Convert to the specified alphabet (throws if is_alternative() would be false).
Definition: union_composition.hpp:382
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:89
Provides C++20 additions to the type_traits header.
::ranges::empty empty
Alias for ranges::empty. Checks whether a range is empty.
Definition: ranges:205
Core alphabet concept and free function/metafunction wrappers.
Provides various metafunctions used by the range module.
constexpr underlying_rank_t< alphabet_type > to_rank(alphabet_type const alph) requires requires(alphabet_type alph)
Implementation of seqan3::semi_alphabet_concept::to_rank() that delegates to a member function...
Definition: member_exposure.hpp:97
constexpr underlying_char_t< alphabet_type > to_char(alphabet_type const alph) requires requires(alphabet_type alph)
Implementation of seqan3::alphabet_concept::to_char() that delegates to a member function.
Definition: member_exposure.hpp:165
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: union_composition.hpp:393
auto const to_rank
A view that calls seqan3::to_rank() on each element in the input range.
Definition: to_rank.hpp:90