This is easier:
#pragma once
#include <iterator>
#include <concepts>
#include <compare>
#include <cassert>
#include <algorithm>
template<typename RandomIt, typename T, typename Pred>
std::pair<RandomIt, RandomIt> xequal_range( RandomIt begin, RandomIt
end, T const &key, Pred pred )
requires std::random_access_iterator<RandomIt>
&&
requires( Pred pred, typename
std::iterator_traits<RandomIt>::value_type &elem, T const &key )
{
{ pred( elem, key ) } -> std::convertible_to<std::strong_ordering>;
}
{
using namespace std;
size_t lower, upper, mid, hit;
strong_ordering so;
for( lower = 0, upper = end - begin, hit = -1; lower < upper; )
{
mid = (lower + upper) / 2;
so = pred( begin[mid], key );
if( so >= 0 )
{
if( so == 0 )
hit = mid;
upper = mid;
}
else
lower = mid + 1;
}
if( hit == -1 )
return pair<RandomIt, RandomIt>( end, end );
begin += hit;
lower = 0;
upper = end - begin;
do
{
mid = (lower + upper) / 2;
so = pred( begin[mid], key );
if( so > 0 )
upper = mid;
else
// so == 0
lower = mid + 1;
} while( lower != upper );
end = begin + lower;
return pair<RandomIt, RandomIt>( begin, end );
}