diff --git a/include/boost/dynamic_bitset/detail/lowest_bit.hpp b/include/boost/dynamic_bitset/detail/lowest_highest_bit.hpp similarity index 56% rename from include/boost/dynamic_bitset/detail/lowest_bit.hpp rename to include/boost/dynamic_bitset/detail/lowest_highest_bit.hpp index bd2f9475..0ef82b9f 100644 --- a/include/boost/dynamic_bitset/detail/lowest_bit.hpp +++ b/include/boost/dynamic_bitset/detail/lowest_highest_bit.hpp @@ -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 #include 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 ) ); +} + } } diff --git a/include/boost/dynamic_bitset/dynamic_bitset.hpp b/include/boost/dynamic_bitset/dynamic_bitset.hpp index 4f96abb8..e292051a 100644 --- a/include/boost/dynamic_bitset/dynamic_bitset.hpp +++ b/include/boost/dynamic_bitset/dynamic_bitset.hpp @@ -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 @@ -1249,6 +1249,11 @@ class dynamic_bitset // ----------------------------------------------------------------------- BOOST_DYNAMIC_BITSET_CONSTEXPR20 bool intersects( const dynamic_bitset & b ) const; + //! A deprecated synonym for `find_first_one()`. + // ----------------------------------------------------------------------- + BOOST_DEPRECATED( "Use find_first_one(), instead" ) + BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_first( size_type pos = 0 ) const; + //! Finds the first set bit in `*this` with an index >= `pos`, //! if any. //! @@ -1260,8 +1265,13 @@ class dynamic_bitset //! \par Throws //! Nothing. // ----------------------------------------------------------------------- - BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_first( size_type pos = 0 ) const; + BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_first_one( size_type pos = 0 ) const; + //! A deprecated synonym for `find_first_zero()`. + // ----------------------------------------------------------------------- + BOOST_DEPRECATED( "Use find_first_zero(), instead" ) + BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_first_off( size_type pos = 0 ) const; + //! Finds the first unset bit in `*this` with an index >= `pos`, //! if any. //! @@ -1276,7 +1286,20 @@ class dynamic_bitset //! \par Throws //! Nothing. // ----------------------------------------------------------------------- - BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_first_off( size_type pos = 0 ) const; + 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" ) + BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_next( size_type pos ) const; //! Finds the first bit set in `*this` with an index > `pos`, if //! any. @@ -1291,7 +1314,27 @@ class dynamic_bitset //! \par Throws //! Nothing. // ----------------------------------------------------------------------- - BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_next( size_type pos ) const; + 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" ) + BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_next_off( size_type pos ) const; //! Finds the first unset bit in `*this` with an index > `pos`, //! if any. @@ -1303,7 +1346,7 @@ class dynamic_bitset //! The lowest index `i` greater than `pos` such that bit `i` is //! unset, or `npos` if no such index exists. // ----------------------------------------------------------------------- - BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_next_off( size_type pos ) const; + BOOST_DYNAMIC_BITSET_CONSTEXPR20 size_type find_next_zero( size_type pos ) const; template< typename B, typename A > friend BOOST_DYNAMIC_BITSET_CONSTEXPR20 bool operator==( const dynamic_bitset< B, A > & a, const dynamic_bitset< B, A > & b ); diff --git a/include/boost/dynamic_bitset/impl/dynamic_bitset.ipp b/include/boost/dynamic_bitset/impl/dynamic_bitset.ipp index e2ce360d..4361cadd 100644 --- a/include/boost/dynamic_bitset/impl/dynamic_bitset.ipp +++ b/include/boost/dynamic_bitset/impl/dynamic_bitset.ipp @@ -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 @@ -1207,7 +1207,7 @@ dynamic_bitset< Block, AllocatorOrContainer >:: // Check for overflows. This may be a performance burden on very large // bitsets but is required by the specification, sorry. - if ( find_first( ulong_width ) != npos ) { + if ( find_first_one( ulong_width ) != npos ) { BOOST_THROW_EXCEPTION( std::overflow_error( "boost::dynamic_bitset::to_ulong overflow" ) ); } @@ -1392,6 +1392,13 @@ dynamic_bitset< Block, AllocatorOrContainer >::m_do_find_from( size_type first_b template< typename Block, typename AllocatorOrContainer > BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type dynamic_bitset< Block, AllocatorOrContainer >::find_first( size_type pos ) const +{ + return find_first_one( pos ); +} + +template< typename Block, typename AllocatorOrContainer > +BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type +dynamic_bitset< Block, AllocatorOrContainer >::find_first_one( size_type pos ) const { const size_type sz = size(); if ( pos >= sz ) { @@ -1412,6 +1419,13 @@ dynamic_bitset< Block, AllocatorOrContainer >::find_first( size_type pos ) const template< typename Block, typename AllocatorOrContainer > BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type dynamic_bitset< Block, AllocatorOrContainer >::find_first_off( size_type pos ) const +{ + return find_first_zero( pos ); +} + +template< typename Block, typename AllocatorOrContainer > +BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type +dynamic_bitset< Block, AllocatorOrContainer >::find_first_zero( size_type pos ) const { if ( pos >= size() ) { return npos; @@ -1436,22 +1450,86 @@ dynamic_bitset< Block, AllocatorOrContainer >::find_first_off( size_type pos ) c : 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 +{ + return find_next_one( pos ); +} + +template< typename Block, typename AllocatorOrContainer > +BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type +dynamic_bitset< Block, AllocatorOrContainer >::find_next_one( size_type pos ) const { return pos == npos ? npos - : find_first( pos + 1 ); + : 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 +{ + return find_next_zero( pos ); +} + +template< typename Block, typename AllocatorOrContainer > +BOOST_DYNAMIC_BITSET_CONSTEXPR20 typename dynamic_bitset< Block, AllocatorOrContainer >::size_type +dynamic_bitset< Block, AllocatorOrContainer >::find_next_zero( size_type pos ) const { return pos == npos ? npos - : find_first_off( pos + 1 ); + : find_first_zero( pos + 1 ); } //----------------------------------------------------------------------------- diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6d173044..237ff5e3 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -26,7 +26,7 @@ test-suite dynamic_bitset : [ run dyn_bitset_unit_tests4.cpp : : : /boost/filesystem//boost_filesystem /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 ] ] diff --git a/test/bitset_test.hpp b/test/bitset_test.hpp index 3fb18a81..af053ee0 100644 --- a/test/bitset_test.hpp +++ b/test/bitset_test.hpp @@ -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 @@ -1091,8 +1091,8 @@ struct bitset_test find_first( const Bitset & b, typename Bitset::size_type offset = 0, bool value = true ) { const typename Bitset::size_type result = value - ? b.find_first( offset ) - : b.find_first_off( offset ); + ? b.find_first_one( offset ) + : b.find_first_zero( offset ); // find first bit with value `value` from offset onwards, if any typename Bitset::size_type i = offset; @@ -1107,14 +1107,51 @@ 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 ) { find_first( b, pos, value); if ( value ) { - BOOST_TEST( next_bit_on( b, pos ) == b.find_next( pos ) ); + BOOST_TEST( next_bit_on( b, pos ) == b.find_next_one( pos ) ); } else { - BOOST_TEST( next_bit_off( b, pos ) == b.find_next_off( pos ) ); + BOOST_TEST( next_bit_off( b, pos ) == b.find_next_zero( pos ) ); + } + } + + 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 ) ); + } } } diff --git a/test/dyn_bitset_unit_tests3.cpp b/test/dyn_bitset_unit_tests3.cpp index b9739ee1..a8ff7504 100644 --- a/test/dyn_bitset_unit_tests3.cpp +++ b/test/dyn_bitset_unit_tests3.cpp @@ -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 // @@ -299,7 +299,7 @@ run_test_cases() Tests::intersects( a, b ); } //===================================================================== - // Test find_first + // Test find_first_one/find_first_zero { // empty bitset bitset_type b; @@ -333,8 +333,33 @@ 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, find_next_off, offset find_first and offset find_first_off + // Test find_next_one, find_next_zero { // empty bitset bitset_type b; @@ -350,7 +375,7 @@ run_test_cases() Tests::find_pos( b, b.npos, false ); } { - // bitset of size 1 (find_next can never find) + // bitset of size 1 (find_next_one or find_next_zero can never find) bitset_type b( 1, 1ul ); // check @@ -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== { diff --git a/test/test_lowest_bit.cpp b/test/test_lowest_highest_bit.cpp similarity index 65% rename from test/test_lowest_bit.cpp rename to test/test_lowest_highest_bit.cpp index 613cff76..f41fc2e3 100644 --- a/test/test_lowest_bit.cpp +++ b/test/test_lowest_highest_bit.cpp @@ -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(); }