Skip to content

Commit 136979e

Browse files
committed
Add find_last_one() and find_previous_one()
1 parent c015e21 commit 136979e

File tree

7 files changed

+178
-12
lines changed

7 files changed

+178
-12
lines changed

include/boost/dynamic_bitset/detail/lowest_bit.hpp renamed to include/boost/dynamic_bitset/detail/lowest_highest_bit.hpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
// -----------------------------------------------------------
2-
// lowest_bit()
32
//
4-
// Position of the lowest bit that is set.
3+
// Position of the lowest or highest bit that is set.
54
//
6-
// Copyright (c) 2003-2004, 2008, 2025 Gennaro Prota
5+
// Copyright (c) 2003-2004, 2008, 2025-2026 Gennaro Prota
76
//
87
// Distributed under the Boost Software License, Version 1.0.
98
// (See accompanying file LICENSE_1_0.txt or copy at
109
// http://www.boost.org/LICENSE_1_0.txt)
1110
//
1211
// -----------------------------------------------------------
1312

14-
#ifndef BOOST_LOWEST_BIT_HPP_GP_20030301
15-
#define BOOST_LOWEST_BIT_HPP_GP_20030301
13+
#ifndef BOOST_LOWEST_HIGHEST_BIT_HPP_GP_20260109
14+
#define BOOST_LOWEST_HIGHEST_BIT_HPP_GP_20260109
1615

1716
#include "boost/assert.hpp"
1817
#include "boost/core/bit.hpp"
18+
#include <limits>
1919
#include <type_traits>
2020

2121
namespace boost {
@@ -30,6 +30,16 @@ lowest_bit( T x )
3030
return boost::core::countr_zero( static_cast< typename std::make_unsigned< T >::type >( x ) );
3131
}
3232

33+
template< typename T >
34+
int
35+
highest_bit( T x )
36+
{
37+
BOOST_ASSERT( x >= 1 );
38+
39+
using Unsigned = typename std::make_unsigned< T >::type;
40+
return ( std::numeric_limits< Unsigned >::digits - 1 ) - boost::core::countl_zero( static_cast< Unsigned >( x ) );
41+
}
42+
3343
}
3444
}
3545

include/boost/dynamic_bitset/dynamic_bitset.hpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// -----------------------------------------------------------
22
//
33
// Copyright (c) 2001-2002 Chuck Allison and Jeremy Siek
4-
// Copyright (c) 2003-2006, 2008, 2025 Gennaro Prota
4+
// Copyright (c) 2003-2006, 2008, 2025-2026 Gennaro Prota
55
// Copyright (c) 2014 Ahmed Charles
66
//
77
// Copyright (c) 2014 Glen Joseph Fernandes
@@ -1288,6 +1288,14 @@ class dynamic_bitset
12881288
// -----------------------------------------------------------------------
12891289
BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_first_zero( size_type pos = 0 ) const;
12901290

1291+
//! Finds the last (highest-index) set bit in `*this`, if any; or
1292+
//! `npos`.
1293+
//!
1294+
//! \par Throws
1295+
//! Nothing.
1296+
// -----------------------------------------------------------------------
1297+
BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_last_one() const;
1298+
12911299
//! A deprecated synonym for `find_next_one()`.
12921300
// -----------------------------------------------------------------------
12931301
[[deprecated]]
@@ -1308,6 +1316,21 @@ class dynamic_bitset
13081316
// -----------------------------------------------------------------------
13091317
BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_next_one( size_type pos ) const;
13101318

1319+
//! Finds the last set bit in `*this` with an index < `pos`, if
1320+
//! any.
1321+
//!
1322+
//! \param pos The upper bound (exclusively) to start the search
1323+
//! from.
1324+
//!
1325+
//! \return
1326+
//! The highest index `i` less than `pos` such that bit `i` is
1327+
//! set, or `npos` if no such index exists.
1328+
//!
1329+
//! \par Throws
1330+
//! Nothing.
1331+
// -----------------------------------------------------------------------
1332+
BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_previous_one( size_type pos ) const;
1333+
13111334
//! A deprecated synonym for `find_next_zero()`.
13121335
// -----------------------------------------------------------------------
13131336
[[ deprecated ]]

include/boost/dynamic_bitset/impl/dynamic_bitset.ipp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// -----------------------------------------------------------
22
//
33
// Copyright (c) 2001-2002 Chuck Allison and Jeremy Siek
4-
// Copyright (c) 2003-2006, 2008, 2025 Gennaro Prota
4+
// Copyright (c) 2003-2006, 2008, 2025-2026 Gennaro Prota
55
// Copyright (c) 2014 Ahmed Charles
66
//
77
// Copyright (c) 2014 Glen Joseph Fernandes
@@ -19,7 +19,7 @@
1919
#include "boost/assert.hpp"
2020
#include "boost/core/bit.hpp"
2121
#include "boost/core/no_exceptions_support.hpp"
22-
#include "boost/dynamic_bitset/detail/lowest_bit.hpp"
22+
#include "boost/dynamic_bitset/detail/lowest_highest_bit.hpp"
2323
#include "boost/functional/hash/hash.hpp"
2424
#include "boost/throw_exception.hpp"
2525
#include <algorithm>
@@ -1450,6 +1450,23 @@ dynamic_bitset< Block, AllocatorOrContainer >::find_first_zero( size_type pos )
14501450
: zero_pos;
14511451
}
14521452

1453+
template< typename Block, typename AllocatorOrContainer >
1454+
BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type
1455+
dynamic_bitset< Block, AllocatorOrContainer >::find_last_one() const
1456+
{
1457+
size_type result = npos;
1458+
1459+
size_type i = num_blocks();
1460+
while ( i > 0 ) {
1461+
-- i;
1462+
if ( m_not_empty( m_bits[ i ] ) ) {
1463+
result = i * bits_per_block + detail::highest_bit( m_bits[ i ] );
1464+
break;
1465+
}
1466+
}
1467+
return result;
1468+
}
1469+
14531470
template< typename Block, typename AllocatorOrContainer >
14541471
BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type
14551472
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
14661483
: find_first_one( pos + 1 );
14671484
}
14681485

1486+
template< typename Block, typename AllocatorOrContainer >
1487+
BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type
1488+
dynamic_bitset< Block, AllocatorOrContainer >::find_previous_one( size_type pos ) const
1489+
{
1490+
if ( pos == 0 || empty() ) {
1491+
return npos;
1492+
}
1493+
1494+
if ( pos >= size() ) {
1495+
return find_last_one();
1496+
}
1497+
1498+
const size_type blk = block_index( pos );
1499+
const int ind = bit_index( pos );
1500+
// mask out bits from ind upwards
1501+
Block back = m_bits[ blk ] & ( ( Block( 1 ) << ind ) - 1 );
1502+
bool found = m_not_empty( back );
1503+
size_type i = blk;
1504+
if ( ! found ) {
1505+
while ( i > 0 ) {
1506+
-- i;
1507+
back = m_bits[ i ];
1508+
if ( m_not_empty( back ) ) {
1509+
found = true;
1510+
break;
1511+
}
1512+
}
1513+
}
1514+
return found
1515+
? i * bits_per_block + detail::highest_bit( back )
1516+
: npos;
1517+
}
1518+
14691519
template< typename Block, typename AllocatorOrContainer >
14701520
BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type
14711521
dynamic_bitset< Block, AllocatorOrContainer >::find_next_off( size_type pos ) const

test/Jamfile.v2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ test-suite dynamic_bitset :
2626
[ run dyn_bitset_unit_tests4.cpp : : : <library>/boost/filesystem//boost_filesystem
2727
<library>/boost/system//boost_system ]
2828
[ run test_ambiguous_set.cpp ]
29-
[ run test_lowest_bit.cpp ]
29+
[ run test_lowest_highest_bit.cpp ]
3030

3131
[ run test_boost_hash.cpp ]
3232
[ run test_std_hash.cpp : : : [ requires cxx11_hdr_unordered_set ] ]

test/bitset_test.hpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// -----------------------------------------------------------
22
// Copyright (c) 2001 Jeremy Siek
3-
// Copyright (c) 2003-2006, 2008, 2025 Gennaro Prota
3+
// Copyright (c) 2003-2006, 2008, 2025-2026 Gennaro Prota
44
// Copyright (c) 2014 Ahmed Charles
55
// Copyright (c) 2014 Riccardo Marcangelo
66
// Copyright (c) 2018 Evgeny Shulgin
@@ -1107,6 +1107,23 @@ struct bitset_test
11071107
}
11081108
}
11091109

1110+
static void
1111+
find_last_one( const Bitset & b )
1112+
{
1113+
const typename Bitset::size_type result = b.find_last_one();
1114+
1115+
if ( b.none() ) {
1116+
BOOST_TEST( result == Bitset::npos );
1117+
} else {
1118+
typename Bitset::size_type i = b.size() - 1;
1119+
while ( i > 0 && ! b[ i ] ) {
1120+
--i;
1121+
}
1122+
BOOST_TEST( result == i );
1123+
BOOST_TEST( b.test( i ) );
1124+
}
1125+
}
1126+
11101127
static void
11111128
find_pos( const Bitset & b, typename Bitset::size_type pos, bool value = true )
11121129
{
@@ -1118,6 +1135,26 @@ struct bitset_test
11181135
}
11191136
}
11201137

1138+
static void
1139+
find_previous_one( const Bitset & b, typename Bitset::size_type pos )
1140+
{
1141+
const typename Bitset::size_type result = b.find_previous_one( pos );
1142+
if ( b.none() || pos == 0 ) {
1143+
BOOST_TEST( result == Bitset::npos );
1144+
} else {
1145+
typename Bitset::size_type i = (std::min)(pos - 1, b.size() - 1);
1146+
while ( i > 0 && ! b[ i ] ) {
1147+
--i;
1148+
}
1149+
if ( i == 0 && ! b[ i ] ) {
1150+
BOOST_TEST( result == Bitset::npos );
1151+
} else {
1152+
BOOST_TEST( result == i );
1153+
BOOST_TEST( b.test( i ) );
1154+
}
1155+
}
1156+
}
1157+
11211158
static void
11221159
operator_equal( const Bitset & a, const Bitset & b )
11231160
{

test/dyn_bitset_unit_tests3.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// -----------------------------------------------------------
22
// Copyright (c) 2001 Jeremy Siek
3-
// Copyright (c) 2003-2006, 2025 Gennaro Prota
3+
// Copyright (c) 2003-2006, 2025-2026 Gennaro Prota
44
// Copyright (c) 2014 Ahmed Charles
55
// Copyright (c) 2014 Riccardo Marcangelo
66
//
@@ -333,6 +333,22 @@ run_test_cases()
333333
b.set( b.size() - 1, false );
334334
Tests::find_first( b, 0, false );
335335
}
336+
337+
//=====================================================================
338+
// Test find_last_one
339+
{
340+
// empty bitset
341+
bitset_type b;
342+
Tests::find_last_one( b );
343+
}
344+
{
345+
// bitset of size 1
346+
bitset_type b( 1, 1ul );
347+
Tests::find_last_one( b );
348+
b.flip();
349+
Tests::find_last_one( b );
350+
}
351+
336352
//=====================================================================
337353
// Test find_next_one, find_next_zero
338354
{
@@ -418,6 +434,32 @@ run_test_cases()
418434
Tests::find_pos( b, b.npos );
419435
Tests::find_pos( b, b.npos, false );
420436
}
437+
438+
//=====================================================================
439+
// Test find_previous_one
440+
{
441+
bitset_type b;
442+
Tests::find_previous_one( b, b.npos);
443+
Tests::find_previous_one( b, 2);
444+
Tests::find_previous_one( b, 1);
445+
Tests::find_previous_one( b, 0);
446+
}
447+
{
448+
bitset_type b( 1, 1ul );
449+
Tests::find_previous_one( b, b.npos);
450+
Tests::find_previous_one( b, 2);
451+
Tests::find_previous_one( b, 1);
452+
Tests::find_previous_one( b, 0);
453+
}
454+
{
455+
bitset_type b( 4 * bitset_type::bits_per_block, 0ul );
456+
b.set( 4 );
457+
Tests::find_previous_one( b, b.npos);
458+
Tests::find_previous_one( b, 6);
459+
Tests::find_previous_one( b, 5);
460+
Tests::find_previous_one( b, 4);
461+
Tests::find_previous_one( b, 0);
462+
}
421463
//=====================================================================
422464
// Test operator==
423465
{
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//
22
// Copyright (C) 2018 James E. King III
3+
// Copyright 2026 Gennaro Prota
34
//
45
// Distributed under the Boost Software License, Version 1.0. (See
56
// accompanying file LICENSE_1_0.txt or copy at
@@ -8,17 +9,20 @@
89

910
#include "boost/core/lightweight_test.hpp"
1011
#include "boost/cstdint.hpp"
11-
#include "boost/dynamic_bitset/detail/lowest_bit.hpp"
12+
#include "boost/dynamic_bitset/detail/lowest_highest_bit.hpp"
1213

1314
int
1415
main( int, char *[] )
1516
{
1617
for ( boost::int32_t i = 1; i < 32; ++i ) {
1718
BOOST_TEST_EQ( i, boost::detail::lowest_bit( 1u << i ) );
19+
BOOST_TEST_EQ( i, boost::detail::highest_bit( 1u << i ) );
1820
}
1921

2022
BOOST_TEST_EQ( 2, boost::detail::lowest_bit( 123456788 ) );
2123
BOOST_TEST_EQ( 30, boost::detail::lowest_bit( static_cast< boost::int64_t >( 1507208177123328 ) ) );
24+
BOOST_TEST_EQ( 15, boost::detail::highest_bit( 0b1000100101111000));
25+
BOOST_TEST_EQ( 53, boost::detail::highest_bit( static_cast< boost::int64_t >( 0x20000000000000 ) ) );
2226

2327
return boost::report_errors();
2428
}

0 commit comments

Comments
 (0)