Loading...
Searching...
No Matches
alphabet_variant.hpp
Go to the documentation of this file.
// SPDX-FileCopyrightText: 2006-2025 Knut Reinert & Freie Universität Berlin
// SPDX-FileCopyrightText: 2016-2025 Knut Reinert & MPI für molekulare Genetik
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
#include <algorithm>
#include <array>
#include <cassert>
#include <span>
#include <utility>
#include <variant>
#include <seqan3/alphabet/alphabet_base.hpp>
#include <seqan3/alphabet/composite/detail.hpp>
#include <seqan3/utility/type_list/traits.hpp>
#include <seqan3/utility/type_traits/lazy_conditional.hpp>
namespace seqan3::detail
{
template <typename t, size_t extent = std::dynamic_extent>
struct is_span : std::false_type
{};
template <typename t, size_t extent>
struct is_span<std::span<t, extent>> : std::true_type
{};
template <typename other_t, typename... alternative_types>
inline constexpr bool variant_general_guard =
(!std::same_as<other_t, alphabet_variant<alternative_types...>>)
&& (!std::is_base_of_v<alphabet_variant<alternative_types...>, other_t>)
&& (!(std::same_as<other_t, alternative_types> || ...))
&& (!list_traits::contains<alphabet_variant<alternative_types...>, recursive_required_types_t<other_t>>);
template <typename lhs_t, typename rhs_t, bool lhs_rhs_switched, typename... alternative_types>
inline constexpr bool variant_comparison_guard =
(instantiate_if_v<lazy<weakly_equality_comparable_with_trait, rhs_t, alternative_types>,
(std::same_as<lhs_t, alphabet_variant<alternative_types...>>)
&& (variant_general_guard<rhs_t, alternative_types...>)
&& !(lhs_rhs_switched && is_type_specialisation_of_v<rhs_t, alphabet_variant>)>
|| ...);
} // namespace seqan3::detail
namespace seqan3
{
template <typename... alternative_types>
requires (detail::writable_constexpr_alphabet<alternative_types> && ...) && (std::regular<alternative_types> && ...)
&& (sizeof...(alternative_types) >= 2)
class alphabet_variant :
public alphabet_base<alphabet_variant<alternative_types...>,
(static_cast<size_t>(alphabet_size<alternative_types>) + ...),
char>
{
private:
using base_t = alphabet_base<alphabet_variant<alternative_types...>,
(static_cast<size_t>(alphabet_size<alternative_types>) + ...),
char>;
static_assert((std::is_same_v<alphabet_char_t<alternative_types>, char> && ...),
"The alphabet_variant is currently only tested for alphabets with char_type char. "
"Contact us on GitHub if you have a different use case: https://github.com/seqan/seqan3 .");
friend base_t;
using alternatives = seqan3::type_list<alternative_types...>;
static_assert(((seqan3::list_traits::count<alternative_types, alternatives> == 1) && ... && true),
"All types in a alphabet_variant must be distinct.");
using typename base_t::char_type;
using typename base_t::rank_type;
public:
using base_t::alphabet_size;
using base_t::assign_rank;
using base_t::to_char;
using base_t::to_rank;
using seqan3_required_types = type_list<alternative_types...>;
using seqan3_recursive_required_types = list_traits::concat<
seqan3_required_types,
detail::transformation_trait_or_t<detail::recursive_required_types<alternative_types>, type_list<>>...>;
template <typename alternative_t>
static constexpr bool is_alternative() noexcept
{
return seqan3::pack_traits::contains<alternative_t, alternative_types...>;
}
constexpr alphabet_variant() noexcept = default;
constexpr alphabet_variant(alphabet_variant const &) noexcept = default;
constexpr alphabet_variant(alphabet_variant &&) noexcept = default;
constexpr alphabet_variant & operator=(alphabet_variant const &) noexcept = default;
constexpr alphabet_variant & operator=(alphabet_variant &&) noexcept = default;
~alphabet_variant() noexcept = default;
template <typename alternative_t>
requires (!std::same_as<alternative_t, alphabet_variant>) && (!detail::is_span<alternative_t>::value)
&& (!std::is_base_of_v<alphabet_variant, alternative_t>)
&& (!list_traits::contains<
alphabet_variant,
detail::transformation_trait_or_t<detail::recursive_required_types<alternative_t>, type_list<>>>)
&& (is_alternative<alternative_t>())
constexpr alphabet_variant(alternative_t const alternative) noexcept
{
assign_rank(rank_by_type_(alternative));
}
template <typename indirect_alternative_t>
requires (!detail::is_span<indirect_alternative_t>::value)
&& ((
detail::instantiate_if_v<detail::lazy<std::is_convertible, indirect_alternative_t, alternative_types>,
detail::variant_general_guard<indirect_alternative_t, alternative_types...>>
|| ...))
constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
{
using alternative_predicate = detail::implicitly_convertible_from<indirect_alternative_t>;
constexpr auto alternative_position =
seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
using alternative_t = seqan3::list_traits::at<alternative_position, alternatives>;
assign_rank(rank_by_type_(alternative_t(rhs)));
}
template <typename indirect_alternative_t>
requires (
(!(detail::instantiate_if_v<detail::lazy<std::is_convertible, indirect_alternative_t, alternative_types>,
detail::variant_general_guard<indirect_alternative_t, alternative_types...>>
|| ...))
&& (detail::instantiate_if_v<detail::lazy<std::is_constructible, alternative_types, indirect_alternative_t>,
detail::variant_general_guard<indirect_alternative_t, alternative_types...>>
|| ...))
constexpr explicit alphabet_variant(indirect_alternative_t const rhs) noexcept
{
using alternative_predicate = detail::constructible_from<indirect_alternative_t>;
constexpr auto alternative_position =
seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
using alternative_t = seqan3::list_traits::at<alternative_position, alternatives>;
assign_rank(rank_by_type_(alternative_t(rhs)));
}
template <typename indirect_alternative_t>
requires (detail::variant_general_guard<indirect_alternative_t, alternative_types...>
&& (weakly_assignable_from<alternative_types, indirect_alternative_t> || ...))
constexpr alphabet_variant & operator=(indirect_alternative_t const & rhs) noexcept
{
using alternative_predicate = detail::assignable_from<indirect_alternative_t>;
constexpr auto alternative_position =
seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
using alternative_t = seqan3::list_traits::at<alternative_position, alternatives>;
alternative_t alternative{};
alternative = rhs;
assign_rank(rank_by_type_(alternative));
return *this;
}
template <size_t index>
constexpr bool holds_alternative() const noexcept
{
static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
return (to_rank() >= partial_sum_sizes[index]) && (to_rank() < partial_sum_sizes[index + 1]);
}
template <size_t index>
constexpr auto convert_to() const
{
return convert_impl<index, true>();
}
template <size_t index>
constexpr auto convert_unsafely_to() const noexcept
{
return convert_impl<index, false>();
}
template <typename alternative_t>
constexpr bool holds_alternative() const noexcept
requires (is_alternative<alternative_t>())
{
constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
return holds_alternative<index>();
}
template <typename alternative_t>
constexpr alternative_t convert_to() const
requires (is_alternative<alternative_t>())
{
constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
return convert_impl<index, true>();
}
template <typename alternative_t>
constexpr alternative_t convert_unsafely_to() const noexcept
requires (is_alternative<alternative_t>())
{
constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
return convert_impl<index, false>();
}
template <typename alphabet_variant_t, typename indirect_alternative_type>
requires (!detail::is_span<alphabet_variant_t>::value)
&& (detail::variant_comparison_guard<alphabet_variant_t,
indirect_alternative_type,
false,
alternative_types...>)
friend constexpr bool operator==(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept
{
using alternative_predicate = detail::weakly_equality_comparable_with_<indirect_alternative_type>;
constexpr auto alternative_position =
seqan3::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
using alternative_t = seqan3::list_traits::at<alternative_position, alternatives>;
return lhs.template holds_alternative<alternative_t>()
&& (lhs.template convert_unsafely_to<alternative_t>() == rhs);
}
template <typename alphabet_variant_t, typename indirect_alternative_type>
requires (!detail::is_span<alphabet_variant_t>::value)
&& (detail::variant_comparison_guard<alphabet_variant_t,
indirect_alternative_type,
false,
alternative_types...>)
friend constexpr bool operator!=(alphabet_variant_t const lhs, indirect_alternative_type const rhs) noexcept
{
return !(lhs == rhs);
}
template <typename alphabet_variant_t, typename indirect_alternative_type, typename = void>
requires (!detail::is_span<alphabet_variant_t>::value)
&& (detail::variant_comparison_guard<alphabet_variant_t,
indirect_alternative_type,
true,
alternative_types...>)
friend constexpr bool operator==(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
{
return rhs == lhs;
}
template <typename alphabet_variant_t, typename indirect_alternative_type, typename = void>
requires (!detail::is_span<alphabet_variant_t>::value)
&& (detail::variant_comparison_guard<alphabet_variant_t,
indirect_alternative_type,
true,
alternative_types...>)
friend constexpr bool operator!=(indirect_alternative_type const lhs, alphabet_variant_t const rhs) noexcept
{
return rhs != lhs;
}
static constexpr bool char_is_valid(char_type const chr) noexcept
{
using index_t = std::make_unsigned_t<char_type>;
return first_valid_char_table[static_cast<index_t>(chr)] < sizeof...(alternative_types);
}
protected:
template <size_t index, bool throws>
constexpr auto convert_impl() const noexcept(!throws) -> seqan3::list_traits::at<index, alternatives>
{
static_assert(index < alphabet_size, "The alphabet_variant contains less alternatives than you are checking.");
using alternative_t = seqan3::list_traits::at<index, alternatives>;
if constexpr (throws)
{
if (!holds_alternative<index>()) // [[unlikely]]
{
throw std::bad_variant_access{};
}
}
return seqan3::assign_rank_to(to_rank() - partial_sum_sizes[index], alternative_t{});
}
template <size_t index, typename alternative_t>
requires (is_alternative<alternative_t>())
static constexpr rank_type rank_by_index_(alternative_t const & alternative) noexcept
{
return partial_sum_sizes[index] + static_cast<rank_type>(seqan3::to_rank(alternative));
}
template <typename alternative_t>
requires (is_alternative<alternative_t>())
static constexpr rank_type rank_by_type_(alternative_t const & alternative) noexcept
{
constexpr size_t index = seqan3::list_traits::find<alternative_t, alternatives>;
return rank_by_index_<index>(alternative);
}
static constexpr char_type rank_to_char(rank_type const rank)
{
return rank_to_char_table[rank];
}
static constexpr rank_type char_to_rank(char_type const chr)
{
using index_t = std::make_unsigned_t<char_type>;
return char_to_rank_table[static_cast<index_t>(chr)];
}
static constexpr std::array<rank_type, sizeof...(alternative_types) + 1> partial_sum_sizes{
[]() constexpr
{
constexpr size_t N = sizeof...(alternative_types) + 1;
std::array<rank_type, N> partial_sum{0, seqan3::alphabet_size<alternative_types>...};
for (size_t i = 1u; i < N; ++i)
partial_sum[i] += partial_sum[i - 1];
return partial_sum;
}()};
static constexpr std::array<char_type, alphabet_size> rank_to_char_table{
[]() constexpr
{
auto assign_value_to_char = [](auto alternative, auto & value_to_char, auto & value) constexpr
{
using alternative_t = std::decay_t<decltype(alternative)>;
for (size_t i = 0u; i < seqan3::alphabet_size<alternative_t>; ++i, ++value)
value_to_char[value] = seqan3::to_char(seqan3::assign_rank_to(i, alternative));
};
size_t value{};
std::array<char_type, alphabet_size> value_to_char{};
// initializer lists guarantee sequencing;
// the following expression behaves as:
// for(auto alternative: alternative_types)
// assign_rank_to_char(alternative, rank_to_char, value);
((assign_value_to_char(alternative_types{}, value_to_char, value)), ...);
return value_to_char;
}()};
static constexpr auto first_valid_char_table{
[]() constexpr
{
constexpr size_t alternative_size = sizeof...(alternative_types);
constexpr size_t table_size = detail::size_in_values_v<char_type>;
using first_alphabet_t = detail::min_viable_uint_t<alternative_size>;
std::array<first_alphabet_t, table_size> lookup_table{};
for (size_t i = 0u; i < table_size; ++i)
{
char_type chr = static_cast<char_type>(i);
std::array<bool, alternative_size> valid_chars{char_is_valid_for<alternative_types>(chr)...};
auto found_it = std::find(valid_chars.begin(), valid_chars.end(), true);
lookup_table[i] = found_it - valid_chars.begin();
}
return lookup_table;
}()};
static constexpr std::array<rank_type, detail::size_in_values_v<char_type>> char_to_rank_table{
[]() constexpr
{
constexpr size_t alternative_size = sizeof...(alternative_types);
constexpr size_t table_size = detail::size_in_values_v<char_type>;
std::array<rank_type, table_size> char_to_rank{};
for (size_t i = 0u; i < table_size; ++i)
{
char_type chr = static_cast<char_type>(i);
std::array<rank_type, alternative_size> ranks{
rank_by_type_(assign_char_to(chr, alternative_types{}))...};
// if no char_is_valid_for any alternative, use the rank of the first alternative
char_to_rank[i] = first_valid_char_table[i] < alternative_size ? ranks[first_valid_char_table[i]] : 0;
}
return char_to_rank;
}()};
};
} // namespace seqan3
algorithm
detail.hpp
Provides implementation detail for seqan3::alphabet_variant and seqan3::alphabet_tuple_base.
alphabet_base.hpp
Provides seqan3::alphabet_base.
array
std::bad_variant_access
std::array::begin
T begin(T... args)
cassert
seqan3::alphabet_base< alphabet_variant< alternative_types... >,(static_cast< size_t >(alphabet_size< alternative_types >)+...), char >::alphabet_base
constexpr alphabet_base() noexcept=default
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
Definition alphabet_base.hpp:134
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
Definition alphabet_base.hpp:77
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
Definition alphabet_base.hpp:196
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
Definition alphabet_base.hpp:112
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
Definition alphabet_base.hpp:69
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
Definition alphabet_base.hpp:184
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:470
seqan3::alphabet_variant< alphabet_t, gap >::assign_rank
constexpr alphabet_variant< alternative_types... > & assign_rank(rank_type const c) noexcept
seqan3::alphabet_variant::is_alternative
static constexpr bool is_alternative() noexcept
Returns true if alternative_t is one of the given alternative types.
Definition alphabet_variant.hpp:179
seqan3::alphabet_variant::convert_to
constexpr alternative_t convert_to() const
Convert to the specified alphabet (throws if holds_alternative() would be false).
Definition alphabet_variant.hpp:370
seqan3::alphabet_variant::operator!=
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:460
seqan3::alphabet_variant::holds_alternative
constexpr bool holds_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition alphabet_variant.hpp:317
seqan3::alphabet_variant::alphabet_variant
constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
Constructor for arguments implicitly convertible to an alternative.
Definition alphabet_variant.hpp:238
seqan3::alphabet_variant::holds_alternative
constexpr bool holds_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition alphabet_variant.hpp:356
seqan3::alphabet_variant::alphabet_variant
constexpr alphabet_variant(indirect_alternative_t const rhs) noexcept
Constructor for arguments explicitly (but not implicitly) convertible to an alternative.
Definition alphabet_variant.hpp:271
seqan3::alphabet_variant::convert_unsafely_to
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:383
seqan3::alphabet_variant::operator!=
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:436
seqan3::alphabet_variant::operator==
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
seqan3::alphabet_variant::convert_to
constexpr auto convert_to() const
Convert to the specified alphabet (throws if holds_alternative() would be false).
Definition alphabet_variant.hpp:330
seqan3::alphabet_variant::operator=
constexpr alphabet_variant & operator=(indirect_alternative_t const &rhs) noexcept
Assignment for arguments assignable to an alternative.
Definition alphabet_variant.hpp:294
seqan3::alphabet_variant::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:196
seqan3::alphabet_variant::operator==
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:419
seqan3::alphabet_variant::alphabet_variant
constexpr alphabet_variant() noexcept=default
Defaulted.
seqan3::alphabet_variant::convert_unsafely_to
constexpr auto convert_unsafely_to() const noexcept
Convert to the specified alphabet (undefined behaviour if holds_alternative() would be false).
Definition alphabet_variant.hpp:341
std::array::end
T end(T... args)
std::find
T find(T... args)
seqan3::assign_char_to
constexpr auto assign_char_to
Assign a character to an alphabet object.
Definition alphabet/concept.hpp:517
seqan3::to_char
constexpr auto to_char
Return the char representation of an alphabet object.
Definition alphabet/concept.hpp:381
seqan3::alphabet_size
constexpr auto alphabet_size
A type trait that holds the size of a (semi-)alphabet.
Definition alphabet/concept.hpp:834
seqan3::assign_rank_to
constexpr auto assign_rank_to
Assign a rank to an alphabet object.
Definition alphabet/concept.hpp:288
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 alphabet/concept.hpp:661
seqan3::to_rank
constexpr auto to_rank
Return the rank representation of a (semi-)alphabet object.
Definition alphabet/concept.hpp:152
seqan3::list_traits::contains
constexpr bool contains
Whether a type occurs in a type list or not.
Definition type_list/traits.hpp:249
seqan3::list_traits::at
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
seqan3::list_traits::concat
decltype(detail::concat(lists_t{}...)) concat
Join two seqan3::type_list s into one.
Definition type_list/traits.hpp:339
seqan3::pack_traits::contains
constexpr bool contains
Whether a type occurs in a pack or not.
Definition type_pack/traits.hpp:220
weakly_assignable_from
Resolves to std::is_assignable_v<t>.
std::is_base_of_v
T is_base_of_v
std::is_same_v
T is_same_v
lazy_conditional.hpp
Provides lazy template instantiation traits.
std::make_unsigned_t
seqan3::list_traits
Namespace containing traits for working on seqan3::type_list.
seqan3
The main SeqAn3 namespace.
Definition aligned_sequence_concept.hpp:26
std
SeqAn specific customisations in the standard namespace.
std::partial_sum
T partial_sum(T... args)
seqan3::type_list
Type that contains multiple types.
Definition type_list.hpp:26
traits.hpp
Provides traits for seqan3::type_list.
utility
variant
Hide me