Add find_last_one() and find_previous_one()

This commit is contained in:
Gennaro Prota
2026-01-09 12:56:32 +01:00
parent 35198124e6
commit 423f5ca364
7 changed files with 193 additions and 12 deletions

View File

@@ -1,9 +1,8 @@
// -----------------------------------------------------------
// lowest_bit()
//
// Position of the lowest bit that is set.
// Position of the lowest or highest bit that is set.
//
// Copyright (c) 2003-2004, 2008, 2025 Gennaro Prota
// Copyright (c) 2003-2004, 2008, 2025-2026 Gennaro Prota
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
@@ -11,11 +10,12 @@
//
// -----------------------------------------------------------
#ifndef BOOST_LOWEST_BIT_HPP_GP_20030301
#define BOOST_LOWEST_BIT_HPP_GP_20030301
#ifndef BOOST_LOWEST_HIGHEST_BIT_HPP_GP_20260109
#define BOOST_LOWEST_HIGHEST_BIT_HPP_GP_20260109
#include "boost/assert.hpp"
#include "boost/core/bit.hpp"
#include <limits>
#include <type_traits>
namespace boost {
@@ -30,6 +30,16 @@ lowest_bit( T x )
return boost::core::countr_zero( static_cast< typename std::make_unsigned< T >::type >( x ) );
}
template< typename T >
int
highest_bit( T x )
{
BOOST_ASSERT( x >= 1 );
using Unsigned = typename std::make_unsigned< T >::type;
return ( std::numeric_limits< Unsigned >::digits - 1 ) - boost::core::countl_zero( static_cast< Unsigned >( x ) );
}
}
}

View File

@@ -1,7 +1,7 @@
// -----------------------------------------------------------
//
// Copyright (c) 2001-2002 Chuck Allison and Jeremy Siek
// Copyright (c) 2003-2006, 2008, 2025 Gennaro Prota
// Copyright (c) 2003-2006, 2008, 2025-2026 Gennaro Prota
// Copyright (c) 2014 Ahmed Charles
//
// Copyright (c) 2014 Glen Joseph Fernandes
@@ -1288,6 +1288,14 @@ public:
// -----------------------------------------------------------------------
BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_first_zero( size_type pos = 0 ) const;
//! Finds the last (highest-index) set bit in `*this`, if any; or
//! `npos`.
//!
//! \par Throws
//! Nothing.
// -----------------------------------------------------------------------
BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_last_one() const;
//! A deprecated synonym for `find_next_one()`.
// -----------------------------------------------------------------------
BOOST_DEPRECATED( "Use find_next_one(), instead" )
@@ -1308,6 +1316,21 @@ public:
// -----------------------------------------------------------------------
BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_next_one( size_type pos ) const;
//! Finds the last set bit in `*this` with an index < `pos`, if
//! any.
//!
//! \param pos The upper bound (exclusively) to start the search
//! from.
//!
//! \return
//! The highest index `i` less than `pos` such that bit `i` is
//! set, or `npos` if no such index exists.
//!
//! \par Throws
//! Nothing.
// -----------------------------------------------------------------------
BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_previous_one( size_type pos ) const;
//! A deprecated synonym for `find_next_zero()`.
// -----------------------------------------------------------------------
BOOST_DEPRECATED( "Use find_next_zero(), instead" )

View File

@@ -1,7 +1,7 @@
// -----------------------------------------------------------
//
// Copyright (c) 2001-2002 Chuck Allison and Jeremy Siek
// Copyright (c) 2003-2006, 2008, 2025 Gennaro Prota
// Copyright (c) 2003-2006, 2008, 2025-2026 Gennaro Prota
// Copyright (c) 2014 Ahmed Charles
//
// Copyright (c) 2014 Glen Joseph Fernandes
@@ -19,7 +19,7 @@
#include "boost/assert.hpp"
#include "boost/core/bit.hpp"
#include "boost/core/no_exceptions_support.hpp"
#include "boost/dynamic_bitset/detail/lowest_bit.hpp"
#include "boost/dynamic_bitset/detail/lowest_highest_bit.hpp"
#include "boost/functional/hash/hash.hpp"
#include "boost/throw_exception.hpp"
#include <algorithm>
@@ -1450,6 +1450,23 @@ dynamic_bitset< Block, AllocatorOrContainer >::find_first_zero( size_type pos )
: zero_pos;
}
template< typename Block, typename AllocatorOrContainer >
BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type
dynamic_bitset< Block, AllocatorOrContainer >::find_last_one() const
{
size_type result = npos;
size_type i = num_blocks();
while ( i > 0 ) {
--i;
if ( m_not_empty( m_bits[ i ] ) ) {
result = i * bits_per_block + detail::highest_bit( m_bits[ i ] );
break;
}
}
return result;
}
template< typename Block, typename AllocatorOrContainer >
BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type
dynamic_bitset< Block, AllocatorOrContainer >::find_next( size_type pos ) const
@@ -1466,6 +1483,39 @@ dynamic_bitset< Block, AllocatorOrContainer >::find_next_one( size_type pos ) co
: find_first_one( pos + 1 );
}
template< typename Block, typename AllocatorOrContainer >
BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type
dynamic_bitset< Block, AllocatorOrContainer >::find_previous_one( size_type pos ) const
{
if ( pos == 0 || empty() ) {
return npos;
}
if ( pos >= size() ) {
return find_last_one();
}
const size_type blk = block_index( pos );
const int ind = bit_index( pos );
// mask out bits from ind upwards
Block back = m_bits[ blk ] & ( ( Block( 1 ) << ind ) - 1 );
bool found = m_not_empty( back );
size_type i = blk;
if ( ! found ) {
while ( i > 0 ) {
--i;
back = m_bits[ i ];
if ( m_not_empty( back ) ) {
found = true;
break;
}
}
}
return found
? i * bits_per_block + detail::highest_bit( back )
: npos;
}
template< typename Block, typename AllocatorOrContainer >
BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type
dynamic_bitset< Block, AllocatorOrContainer >::find_next_off( size_type pos ) const

View File

@@ -26,7 +26,7 @@ test-suite dynamic_bitset :
[ run dyn_bitset_unit_tests4.cpp : : : <library>/boost/filesystem//boost_filesystem
<library>/boost/system//boost_system ]
[ run test_ambiguous_set.cpp ]
[ run test_lowest_bit.cpp ]
[ run test_lowest_highest_bit.cpp ]
[ run test_boost_hash.cpp ]
[ run test_std_hash.cpp : : : [ requires cxx11_hdr_unordered_set ] ]

View File

@@ -1,6 +1,6 @@
// -----------------------------------------------------------
// Copyright (c) 2001 Jeremy Siek
// Copyright (c) 2003-2006, 2008, 2025 Gennaro Prota
// Copyright (c) 2003-2006, 2008, 2025-2026 Gennaro Prota
// Copyright (c) 2014 Ahmed Charles
// Copyright (c) 2014 Riccardo Marcangelo
// Copyright (c) 2018 Evgeny Shulgin
@@ -1107,6 +1107,23 @@ struct bitset_test
}
}
static void
find_last_one( const Bitset & b )
{
const typename Bitset::size_type result = b.find_last_one();
if ( b.none() ) {
BOOST_TEST( result == Bitset::npos );
} else {
typename Bitset::size_type i = b.size() - 1;
while ( i > 0 && ! b[ i ] ) {
--i;
}
BOOST_TEST( result == i );
BOOST_TEST( b.test( i ) );
}
}
static void
find_pos( const Bitset & b, typename Bitset::size_type pos, bool value = true )
{
@@ -1118,6 +1135,26 @@ struct bitset_test
}
}
static void
find_previous_one( const Bitset & b, typename Bitset::size_type pos )
{
const typename Bitset::size_type result = b.find_previous_one( pos );
if ( b.none() || pos == 0 ) {
BOOST_TEST( result == Bitset::npos );
} else {
typename Bitset::size_type i = (std::min)(pos - 1, b.size() - 1);
while ( i > 0 && ! b[ i ] ) {
--i;
}
if ( i == 0 && ! b[ i ] ) {
BOOST_TEST( result == Bitset::npos );
} else {
BOOST_TEST( result == i );
BOOST_TEST( b.test( i ) );
}
}
}
static void
operator_equal( const Bitset & a, const Bitset & b )
{

View File

@@ -1,6 +1,6 @@
// -----------------------------------------------------------
// Copyright (c) 2001 Jeremy Siek
// Copyright (c) 2003-2006, 2025 Gennaro Prota
// Copyright (c) 2003-2006, 2025-2026 Gennaro Prota
// Copyright (c) 2014 Ahmed Charles
// Copyright (c) 2014 Riccardo Marcangelo
//
@@ -333,6 +333,31 @@ run_test_cases()
b.set( b.size() - 1, false );
Tests::find_first( b, 0, false );
}
//=====================================================================
// Test find_last_one
{
// empty bitset
bitset_type b;
Tests::find_last_one( b );
}
{
// bitset of size 1
bitset_type b( 1, 1ul );
Tests::find_last_one( b );
b.flip();
Tests::find_last_one( b );
}
{
// multi-block bitset
bitset_type b( 4 * bitset_type::bits_per_block, 1ul );
Tests::find_last_one( b );
b.set( 3 * bitset_type::bits_per_block );
Tests::find_last_one( b );
b.set( 3 * bitset_type::bits_per_block - 1 );
Tests::find_last_one( b );
}
//=====================================================================
// Test find_next_one, find_next_zero
{
@@ -418,6 +443,38 @@ run_test_cases()
Tests::find_pos( b, b.npos );
Tests::find_pos( b, b.npos, false );
}
//=====================================================================
// Test find_previous_one
{
bitset_type b;
Tests::find_previous_one( b, b.npos);
Tests::find_previous_one( b, 2);
Tests::find_previous_one( b, 1);
Tests::find_previous_one( b, 0);
}
{
bitset_type b( 1, 1ul );
Tests::find_previous_one( b, b.npos);
Tests::find_previous_one( b, 2);
Tests::find_previous_one( b, 1);
Tests::find_previous_one( b, 0);
}
{
bitset_type b( 4 * bitset_type::bits_per_block, 0ul );
b.set( 4 );
Tests::find_previous_one( b, b.npos);
Tests::find_previous_one( b, 4 * bitset_type::bits_per_block );
Tests::find_previous_one( b, 4 * bitset_type::bits_per_block - 1 );
Tests::find_previous_one( b, 3 * bitset_type::bits_per_block );
Tests::find_previous_one( b, 3 * bitset_type::bits_per_block - 1 );
Tests::find_previous_one( b, bitset_type::bits_per_block );
Tests::find_previous_one( b, bitset_type::bits_per_block - 1 );
Tests::find_previous_one( b, 6);
Tests::find_previous_one( b, 5);
Tests::find_previous_one( b, 4);
Tests::find_previous_one( b, 0);
}
//=====================================================================
// Test operator==
{

View File

@@ -1,5 +1,6 @@
//
// Copyright (C) 2018 James E. King III
// Copyright 2026 Gennaro Prota
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -8,17 +9,20 @@
#include "boost/core/lightweight_test.hpp"
#include "boost/cstdint.hpp"
#include "boost/dynamic_bitset/detail/lowest_bit.hpp"
#include "boost/dynamic_bitset/detail/lowest_highest_bit.hpp"
int
main( int, char *[] )
{
for ( boost::int32_t i = 1; i < 32; ++i ) {
BOOST_TEST_EQ( i, boost::detail::lowest_bit( 1u << i ) );
BOOST_TEST_EQ( i, boost::detail::highest_bit( 1u << i ) );
}
BOOST_TEST_EQ( 2, boost::detail::lowest_bit( 123456788 ) );
BOOST_TEST_EQ( 30, boost::detail::lowest_bit( static_cast< boost::int64_t >( 1507208177123328 ) ) );
BOOST_TEST_EQ( 15, boost::detail::highest_bit( 0b1000100101111000));
BOOST_TEST_EQ( 53, boost::detail::highest_bit( static_cast< boost::int64_t >( 0x20000000000000 ) ) );
return boost::report_errors();
}