Add find_first_off(), find_first_off( size_type ) and find_next_off( size_type )

This commit is contained in:
Gennaro Prota
2025-09-05 16:25:43 +02:00
parent 54b32707c4
commit e194055264
4 changed files with 169 additions and 20 deletions

View File

@@ -1076,6 +1076,17 @@ public:
// -----------------------------------------------------------------------
size_type find_first() const;
//! Finds the first unset bit in `*this`, if any.
//!
//! \return
//! The lowest index `i` such that bit `i` is unset in `*this`,
//! or `npos` if no such index exists.
//!
//! \par Throws
//! Nothing.
// -----------------------------------------------------------------------
size_type find_first_off() const;
//! Finds the first set bit in `*this` with an index >= `pos`,
//! if any.
//!
@@ -1089,6 +1100,19 @@ public:
// -----------------------------------------------------------------------
size_type find_first( size_type pos ) const;
//! Finds the first unset bit in `*this` with an index >= `pos`,
//! if any.
//!
//! \return
//! The lowest index `i` greater than or equal to `pos` such
//! that bit `i` is unset in `*this`, or `npos` if no such index
//! exists.
//!
//! \par Throws
//! Nothing.
// -----------------------------------------------------------------------
size_type find_first_off( size_type pos ) const;
//! Finds the first bit set in `*this` with an index > `pos`, if
//! any.
//!
@@ -1104,6 +1128,18 @@ public:
// -----------------------------------------------------------------------
size_type find_next( size_type pos ) const;
//! Finds the first unset bit in `*this` with an index > `pos`,
//! if any.
//!
//! \param pos The lower bound (exclusively) to start the search
//! from.
//!
//! \return
//! The lowest index `i` greater than `pos` such that bit `i` is
//! unset, or `npos` if no such index exists.
// -----------------------------------------------------------------------
size_type find_next_off( size_type pos ) const;
template< typename B, typename A >
friend bool operator==( const dynamic_bitset< B, A > & a, const dynamic_bitset< B, A > & b );
@@ -1146,7 +1182,8 @@ private:
bool m_check_invariants() const;
static bool m_not_empty( Block x );
size_type m_do_find_from( size_type first_block ) const;
static bool m_not_full( Block x );
size_type m_do_find_from( size_type first_block, bool value ) const;
int count_extra_bits() const noexcept;
static size_type block_index( size_type pos ) noexcept;

View File

@@ -946,26 +946,39 @@ dynamic_bitset< Block, Allocator >::intersects( const dynamic_bitset & b ) const
// --------------------------------
// lookup
// Look for the first bit "on", starting from the block with index
// Look for the first bit with value `value`, starting from the block with index
// first_block.
template< typename Block, typename Allocator >
typename dynamic_bitset< Block, Allocator >::size_type
dynamic_bitset< Block, Allocator >::m_do_find_from( size_type first_block ) const
dynamic_bitset< Block, Allocator >::m_do_find_from( size_type first_block, bool value ) const
{
size_type i = std::distance( m_bits.begin(), std::find_if( m_bits.begin() + first_block, m_bits.end(), m_not_empty ) );
size_type i = std::distance( m_bits.begin(), std::find_if( m_bits.begin() + first_block, m_bits.end(),
value
? m_not_empty
: m_not_full ) );
if ( i >= num_blocks() ) {
return npos; // not found
}
return i * bits_per_block + static_cast< size_type >( detail::lowest_bit( m_bits[ i ] ) );
const Block b = value
? m_bits[ i ]
: m_bits[ i ] ^ Block( -1 );
return i * bits_per_block + static_cast< size_type >( detail::lowest_bit( b ) );
}
template< typename Block, typename Allocator >
typename dynamic_bitset< Block, Allocator >::size_type
dynamic_bitset< Block, Allocator >::find_first() const
{
return m_do_find_from( 0 );
return m_do_find_from( 0, true );
}
template< typename Block, typename Allocator >
typename dynamic_bitset< Block, Allocator >::size_type
dynamic_bitset< Block, Allocator >::find_first_off() const
{
return m_do_find_from( 0, false );
}
template< typename Block, typename Allocator >
@@ -983,8 +996,36 @@ dynamic_bitset< Block, Allocator >::find_first( size_type pos ) const
// shift bits upto one immediately after current
const Block fore = m_bits[ blk ] >> ind;
return fore ? pos + static_cast< size_type >( detail::lowest_bit( fore ) )
: m_do_find_from( blk + 1 );
const bool found = m_not_empty( fore );
return found ? pos + static_cast< size_type >( detail::lowest_bit( fore ) )
: m_do_find_from( blk + 1, true );
}
template< typename Block, typename Allocator >
typename dynamic_bitset< Block, Allocator >::size_type
dynamic_bitset< Block, Allocator >::find_first_off( size_type pos ) const
{
if ( pos >= size() ) {
return npos;
}
const size_type blk = block_index( pos );
const int ind = bit_index( pos );
const Block fore = m_bits[ blk ] >> ind;
bool found = false;
int lowest_off_bit_pos = -1;
if ( m_not_full( fore ) ) {
lowest_off_bit_pos = detail::lowest_bit( fore ^ Block( -1 ) );
// don't consider a zero introduced by m_bits[ blk ] >> ind as found
found = lowest_off_bit_pos <= ( bits_per_block - 1 - ind );
}
const size_type zero_pos = found
? pos + lowest_off_bit_pos
: m_do_find_from( blk + 1, false );
return zero_pos >= size()
? npos
: zero_pos;
}
template< typename Block, typename Allocator >
@@ -997,6 +1038,15 @@ dynamic_bitset< Block, Allocator >::find_next( size_type pos ) const
return find_first( pos + 1 );
}
template< typename Block, typename Allocator >
typename dynamic_bitset< Block, Allocator >::size_type
dynamic_bitset< Block, Allocator >::find_next_off( size_type pos ) const
{
return pos == npos
? npos
: find_first_off( pos + 1 );
}
//-----------------------------------------------------------------------------
// comparison
@@ -1472,6 +1522,13 @@ dynamic_bitset< Block, Allocator >::m_not_empty( Block x )
return x != Block( 0 );
}
template< typename Block, typename Allocator >
bool
dynamic_bitset< Block, Allocator >::m_not_full( Block x )
{
return x != Block( -1 );
}
template< typename Block, typename Allocator >
int
dynamic_bitset< Block, Allocator >::count_extra_bits() const noexcept

View File

@@ -994,26 +994,34 @@ struct bitset_test
}
static void
find_first( const Bitset & b, typename Bitset::size_type offset = 0 )
find_first( const Bitset & b, typename Bitset::size_type offset = 0, bool value = true )
{
// find first non-null bit from offset onwards, if any
const typename Bitset::size_type result = value
? b.find_first( offset )
: b.find_first_off( offset );
// find first bit with value `value` from offset onwards, if any
typename Bitset::size_type i = offset;
while ( i < b.size() && b[ i ] == 0 )
while ( i < b.size() && b[ i ] != value )
++i;
if ( i >= b.size() )
BOOST_TEST( b.find_first( offset ) == Bitset::npos ); // not found;
BOOST_TEST( result == Bitset::npos ); // not found;
else {
BOOST_TEST( b.find_first( offset ) == i );
BOOST_TEST( b.test( i ) == true );
BOOST_TEST( result == i );
BOOST_TEST( b.test( i ) == value );
}
}
static void
find_pos( const Bitset & b, typename Bitset::size_type pos )
find_pos( const Bitset & b, typename Bitset::size_type pos, bool value = true )
{
find_first( b, pos );
BOOST_TEST( next_bit_on( b, pos ) == b.find_next( pos ) );
find_first( b, pos, value);
if ( value ) {
BOOST_TEST( next_bit_on( b, pos ) == b.find_next( pos ) );
} else {
BOOST_TEST( next_bit_off( b, pos ) == b.find_next_off( pos ) );
}
}
static void
@@ -1106,6 +1114,32 @@ struct bitset_test
return i == b.size() ? Bitset::npos : i;
}
static typename Bitset::size_type
next_bit_off( const Bitset & b, typename Bitset::size_type prev )
{
// helper function for find_pos()
//
if ( b.all() || prev == Bitset::npos ) {
return Bitset::npos;
}
++prev;
if ( prev >= b.size() ) {
return Bitset::npos;
}
typename Bitset::size_type i = prev;
while ( i < b.size() && b[ i ] ) {
++i;
}
return i == b.size()
? Bitset::npos
: i;
}
static void
operator_less_than( const Bitset & a, const Bitset & b )
{

View File

@@ -305,30 +305,37 @@ run_test_cases( BOOST_EXPLICIT_TEMPLATE_TYPE( Block ) )
// empty bitset
bitset_type b;
Tests::find_first( b );
Tests::find_first( b, 0, false );
}
{
// bitset of size 1
bitset_type b( 1, 1ul );
Tests::find_first( b );
Tests::find_first( b, 0, false );
}
{
// all-0s bitset
bitset_type b( 4 * bitset_type::bits_per_block, 0ul );
Tests::find_first( b );
Tests::find_first( b, 0, false );
}
{
// first bit on
// first bit on or off
bitset_type b( 1, 1ul );
Tests::find_first( b );
b.set( 0, false );
Tests::find_first( b, 0, false );
}
{
// last bit on
// last bit on or off
bitset_type b( 4 * bitset_type::bits_per_block - 1, 0ul );
b.set( b.size() - 1 );
Tests::find_first( b );
b.set( b.size() - 1, false );
Tests::find_first( b, 0, false );
}
//=====================================================================
// Test find_next and offset find_first
// Test find_next, find_next_off, offset find_first and offset find_first_off
{
// empty bitset
bitset_type b;
@@ -338,6 +345,10 @@ run_test_cases( BOOST_EXPLICIT_TEMPLATE_TYPE( Block ) )
Tests::find_pos( b, 1 );
Tests::find_pos( b, 200 );
Tests::find_pos( b, b.npos );
Tests::find_pos( b, 0, false );
Tests::find_pos( b, 1, false );
Tests::find_pos( b, 200, false );
Tests::find_pos( b, b.npos, false );
}
{
// bitset of size 1 (find_next can never find)
@@ -348,6 +359,10 @@ run_test_cases( BOOST_EXPLICIT_TEMPLATE_TYPE( Block ) )
Tests::find_pos( b, 1 );
Tests::find_pos( b, 200 );
Tests::find_pos( b, b.npos );
Tests::find_pos( b, 0, false );
Tests::find_pos( b, 1, false );
Tests::find_pos( b, 200, false );
Tests::find_pos( b, b.npos, false );
}
{
// all-1s bitset
@@ -358,8 +373,10 @@ run_test_cases( BOOST_EXPLICIT_TEMPLATE_TYPE( Block ) )
const typename bitset_type::size_type larger_than_size = 5 + b.size();
for ( typename bitset_type::size_type i = 0; i <= larger_than_size; ++i ) {
Tests::find_pos( b, i );
Tests::find_pos( b, i, false );
}
Tests::find_pos( b, b.npos );
Tests::find_pos( b, b.npos, false );
}
{
// a bitset with 1s at block boundary only
@@ -378,8 +395,10 @@ run_test_cases( BOOST_EXPLICIT_TEMPLATE_TYPE( Block ) )
const typename bitset_type::size_type larger_than_size = 5 + b.size();
for ( i = 0; i <= larger_than_size; ++i ) {
Tests::find_pos( b, i );
Tests::find_pos( b, i, false );
}
Tests::find_pos( b, b.npos );
Tests::find_pos( b, b.npos, false );
}
{
// bitset with alternate 1s and 0s
@@ -395,8 +414,10 @@ run_test_cases( BOOST_EXPLICIT_TEMPLATE_TYPE( Block ) )
const typename bitset_type::size_type larger_than_size = 5 + b.size();
for ( i = 0; i <= larger_than_size; ++i ) {
Tests::find_pos( b, i );
Tests::find_pos( b, i, false );
}
Tests::find_pos( b, b.npos );
Tests::find_pos( b, b.npos, false );
}
//=====================================================================
// Test operator==