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 bd2f947..0ef82b9 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 232a6b4..e292051 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 @@ -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" ) diff --git a/include/boost/dynamic_bitset/impl/dynamic_bitset.ipp b/include/boost/dynamic_bitset/impl/dynamic_bitset.ipp index 15d7beb..4361cad 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 @@ -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 diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6d17304..237ff5e 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 3293703..af053ee 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 @@ -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 ) { diff --git a/test/dyn_bitset_unit_tests3.cpp b/test/dyn_bitset_unit_tests3.cpp index 7e54396..a8ff750 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 // @@ -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== { 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 613cff7..f41fc2e 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(); }