template<class R, class T>
concept output_range =
range<R> && output_iterator<iterator_t<R>, T>;
template<class T>
concept input_range =
range<T> && input_iterator<iterator_t<T>>;
template<class T>
concept forward_range =
input_range<T> && forward_iterator<iterator_t<T>>;
template<class T>
concept bidirectional_range =
forward_range<T> && bidirectional_iterator<iterator_t<T>>;
template<class T>
concept random_access_range =
bidirectional_range<T> && random_access_iterator<iterator_t<T>>;
template<class T>
concept contiguous_range =
random_access_range<T> && contiguous_iterator<iterator_t<T>> &&
requires(T& t) {
{ ranges::data(t) } -> same_as<add_pointer_t<range_reference_t<T>>>;
};
template<class T>
concept common_range =
range<T> && same_as<iterator_t<T>, sentinel_t<T>>;
template<class T>
concept viewable_range =
range<T> && (borrowed_range<T> || view<remove_cvref_t<T>>);