// ----------------------------------------------------------- // Copyright (c) 2001 Jeremy Siek // Copyright (c) 2003-2006, 2008, 2025 Gennaro Prota // Copyright (c) 2014 Ahmed Charles // Copyright (c) 2014 Riccardo Marcangelo // Copyright (c) 2018 Evgeny Shulgin // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // ----------------------------------------------------------- #ifndef BOOST_BITSET_TEST_HPP_GP_20040319 #define BOOST_BITSET_TEST_HPP_GP_20040319 #include "boost/config.hpp" #include "boost/container/small_vector.hpp" #include "boost/core/lightweight_test.hpp" #include "boost/dynamic_bitset/dynamic_bitset.hpp" #include "boost/filesystem.hpp" #include "boost/limits.hpp" #include #include // is sometimes macro-guarded :-( #include #include #include #include #include #include template< typename T > using small_vector = boost::container::small_vector< T, 8 >; template< typename Block > bool nth_bit( Block num, std::size_t n ) { #ifndef NDEBUG int block_width = std::numeric_limits< Block >::digits; assert( n < (std::size_t)block_width ); #endif return ( num >> n ) & 1; } // A long, 'irregular', string useful for various tests std::string get_long_string() { const char * const p = // 6 5 4 3 2 1 // 3210987654321098765432109876543210987654321098765432109876543210 "1110011100011110000011110000011111110000000000000110101110000000" "1010101000011100011101010111110000101011111000001111100011100011" "0000000110000001000000111100000111100010101111111000000011100011" "1111111111111111111111111111111111111111111111111111111111111100" "1000001100000001111111111111110000000011111000111100001010100000" "101000111100011010101110011011000000010"; return std::string( p ); } class scoped_temp_file { public: scoped_temp_file() : m_path( boost::filesystem::unique_path() ) { } scoped_temp_file( const scoped_temp_file & ) = delete; scoped_temp_file & operator=( const scoped_temp_file & ) = delete; ~scoped_temp_file() { boost::filesystem::remove( m_path ); } const boost::filesystem::path & path() const { return m_path; } private: boost::filesystem::path m_path; }; template< typename Stream, typename Ch > bool is_one_or_zero( const Stream & s, Ch c ) { typedef typename Stream::traits_type Tr; const Ch zero = s.widen( '0' ); const Ch one = s.widen( '1' ); return Tr::eq( c, one ) || Tr::eq( c, zero ); } template< typename Stream, typename Ch > bool is_white_space( const Stream & s, Ch c ) { return std::isspace( c, s.getloc() ); } template< typename Stream > bool has_flags( const Stream & s, std::ios::iostate flags ) { return ( s.rdstate() & flags ) != std::ios::goodbit; } // constructors // default (can't do this generically) template< typename Bitset > struct bitset_test { typedef typename Bitset::block_type Block; static constexpr int bits_per_block = Bitset::bits_per_block; // from unsigned long // // Note: this is templatized so that we check that the do-the-right-thing // constructor dispatch is working correctly. // template< typename NumBits, typename Value > static void from_unsigned_long( NumBits num_bits, Value num ) { // An object of size sz = num_bits is constructed: // - the first m bit positions are initialized to the corresponding // bit values in num (m being the smaller of sz and ulong_width) // // - any remaining bit positions are initialized to zero // Bitset b( static_cast< typename Bitset::size_type >( num_bits ), static_cast< unsigned long >( num ) ); // OK, we can now cast to size_type typedef typename Bitset::size_type size_type; const size_type sz = static_cast< size_type >( num_bits ); BOOST_TEST( b.size() == sz ); const std::size_t ulong_width = std::numeric_limits< unsigned long >::digits; size_type m = sz; if ( ulong_width < sz ) m = ulong_width; size_type i = 0; for ( ; i < m; ++i ) BOOST_TEST( b.test( i ) == nth_bit( static_cast< unsigned long >( num ), i ) ); for ( ; i < sz; ++i ) BOOST_TEST( b.test( i ) == 0 ); } // from string // // Note: The corresponding function in dynamic_bitset (constructor // from a string) has several default arguments. Actually we don't // test the correct working of those defaults here (except for the // default of num_bits). I'm not sure what to do in this regard. // // Note2: the default argument expression for num_bits doesn't use // static_cast, to avoid a gcc 2.95.3 'sorry, not implemented' // template< typename Ch, typename Tr, typename Al > static void from_string( const std::basic_string< Ch, Tr, Al > & str, std::size_t pos, std::size_t max_char, std::size_t num_bits = (std::size_t)( -1 ) ) { std::size_t rlen = (std::min)( max_char, str.size() - pos ); // The resulting size N of the bitset is num_bits, if // that is different from the default arg, rlen otherwise. // Put M = the smaller of N and rlen, then character // position pos + M - 1 corresponds to bit position zero. // Subsequent decreasing character positions correspond to // increasing bit positions. const bool size_upon_string = num_bits == (std::size_t)( -1 ); Bitset b = size_upon_string ? Bitset( str, pos, max_char ) : Bitset( str, pos, max_char, num_bits ); const std::size_t actual_size = size_upon_string ? rlen : num_bits; BOOST_TEST( b.size() == actual_size ); std::size_t m = (std::min)( num_bits, rlen ); std::size_t j; for ( j = 0; j < m; ++j ) BOOST_TEST( b[ j ] == ( str[ pos + m - 1 - j ] == '1' ) ); // If M < N, remaining bit positions are zero for ( ; j < actual_size; ++j ) BOOST_TEST( b[ j ] == 0 ); #if defined( BOOST_DYNAMIC_BITSET_USE_CPP17_OR_LATER ) BOOST_TEST( Bitset( std::basic_string_view< Ch, Tr >( str ).substr( pos, rlen ), num_bits ) == b ); #endif } static void iterate_forward( const Bitset & b ) { std::ptrdiff_t i = 0; for ( auto it = b.begin(); it != b.end(); ++it ) { BOOST_TEST( *it == b[ i ] ); ++i; } } static void iterate_backward( const Bitset & b ) { std::ptrdiff_t i = static_cast< std::ptrdiff_t >( b.size() ) - 1; for ( auto it = b.rbegin(); it != b.rend(); ++it ) { BOOST_TEST( *it == b[ i ] ); --i; } } static void iterator_operations( const Bitset & b ) { if ( b.size() >= 1 ) { BOOST_TEST( *( b.end() - 1 ) == b[ b.size() - 1 ] ); BOOST_TEST( b.begin() < b.end() ); BOOST_TEST( b.begin() <= b.end() ); BOOST_TEST( b.end() > b.begin() ); BOOST_TEST( b.end() >= b.begin() ); BOOST_TEST( b.rbegin() < b.rend() ); BOOST_TEST( b.rbegin() <= b.rend() ); BOOST_TEST( b.rend() > b.rbegin() ); BOOST_TEST( b.rend() >= b.rbegin() ); typename Bitset::const_iterator it = b.begin(); it += b.size() / 2; BOOST_TEST( *it == b[ b.size() / 2 ] ); it -= b.size() / 2; BOOST_TEST( *it == b[ 0 ] ); } else { BOOST_TEST( b.begin() == b.end() ); BOOST_TEST( b.rbegin() == b.rend() ); } if ( b.size() > 1 ) { BOOST_TEST( *( b.begin() + 1 ) == b[ 1 ] ); BOOST_TEST( *( 1 + b.begin() ) == b[ 1 ] ); BOOST_TEST( *( b.end() - 1 ) == b[ b.size() - 1 ] ); } BOOST_TEST( b.end() - b.begin() == static_cast< std::ptrdiff_t >( b.size() ) ); } static void to_block_range( const Bitset & b /*, BlockOutputIterator result*/ ) { typedef typename Bitset::size_type size_type; Block sentinel = 0xF0; int s = 8; // number of sentinels (must be *even*) int offset = s / 2; std::vector< Block > v( b.num_blocks() + s, sentinel ); boost::to_block_range( b, v.begin() + offset ); assert( v.size() >= (size_type)s && ( s >= 2 ) && ( s % 2 == 0 ) ); // check sentinels at both ends for ( int i = 0; i < s / 2; ++i ) { BOOST_TEST( v[ i ] == sentinel ); BOOST_TEST( v[ v.size() - 1 - i ] == sentinel ); } typename std::vector< Block >::const_iterator p = v.begin() + offset; for ( size_type n = 0; n < b.num_blocks(); ++n, ++p ) { for ( int i = 0; i < bits_per_block; ++i ) { size_type bit = n * bits_per_block + i; BOOST_TEST( nth_bit( *p, i ) == ( bit < b.size() ? b[ bit ] : 0 ) ); } } } // TODO from_block_range (below) should be splitted static void from_block_range( const std::vector< Block > & blocks ) { { // test constructor from block range Bitset bset( blocks.begin(), blocks.end() ); std::size_t n = blocks.size(); for ( std::size_t b = 0; b < n; ++b ) { for ( int i = 0; i < bits_per_block; ++i ) { std::size_t bit = b * bits_per_block + i; BOOST_TEST( bset[ bit ] == nth_bit( blocks[ b ], i ) ); } } BOOST_TEST( bset.size() == n * bits_per_block ); } { // test boost::from_block_range const typename Bitset::size_type n = blocks.size(); Bitset bset( n * bits_per_block ); boost::from_block_range( blocks.begin(), blocks.end(), bset ); for ( std::size_t b = 0; b < n; ++b ) { for ( int i = 0; i < bits_per_block; ++i ) { std::size_t bit = b * bits_per_block + i; BOOST_TEST( bset[ bit ] == nth_bit( blocks[ b ], i ) ); } } BOOST_TEST( n <= bset.num_blocks() ); } } // copy constructor (absent from std::bitset) static void copy_constructor( const Bitset & b ) { Bitset copy( b ); BOOST_TEST( b == copy ); // Changes to the copy do not affect the original if ( b.size() > 0 ) { std::size_t pos = copy.size() / 2; copy.flip( pos ); BOOST_TEST( copy[ pos ] != b[ pos ] ); } } // copy assignment operator (absent from std::bitset) static void copy_assignment_operator( const Bitset & lhs, const Bitset & rhs ) { Bitset b( lhs ); b = rhs; b = b; // self assignment check BOOST_TEST( b == rhs ); // Changes to the copy do not affect the original if ( b.size() > 0 ) { std::size_t pos = b.size() / 2; b.flip( pos ); BOOST_TEST( b[ pos ] != rhs[ pos ] ); } } static void max_size( const Bitset & b ) { BOOST_TEST( b.max_size() > 0 ); BOOST_TEST( b.max_size() >= b.size() ); } // move constructor (absent from std::bitset) static void move_constructor( const Bitset & b ) { Bitset copy( b ); Bitset b2( std::move( copy ) ); BOOST_TEST( b2 == b ); } // move assignment operator (absent from std::bitset) static void move_assignment_operator( const Bitset & lhs, const Bitset & rhs ) { Bitset b( lhs ); Bitset c( rhs ); b = std::move( c ); b = std::move( b ); // self assignment check BOOST_TEST( b == rhs ); } static void swap( const Bitset & lhs, const Bitset & rhs ) { // bitsets must be swapped Bitset copy1( lhs ); Bitset copy2( rhs ); copy1.swap( copy2 ); BOOST_TEST( copy1 == rhs ); BOOST_TEST( copy2 == lhs ); for ( typename Bitset::size_type i = 0; i < lhs.size(); ++i ) { Bitset b1( lhs ); Bitset b2( rhs ); typename Bitset::reference ref = b1[ i ]; const bool x = ref; if ( i < b2.size() ) b2[ i ] = ! x; // make sure b2[i] is different b1.swap( b2 ); BOOST_TEST( b2[ i ] == x ); // now it must be equal // b2.flip( i ); // Since we transformed the Allocator parameter into // AllocatorOrContainer, the following is no longer true (think e.g. // of boost::container::small_vector). // BOOST_TEST( ref == b2[ i ] ); // .. and ref must be into b2 // BOOST_TEST( ref == ! x ); } } static void resize( const Bitset & lhs ) { Bitset b( lhs ); // Test no change in size b.resize( lhs.size(), true); BOOST_TEST( b == lhs ); b.resize( lhs.size(), false ); BOOST_TEST( b == lhs ); // Test increase in size with new bits to true b.resize( lhs.size() * 2, true ); BOOST_TEST( b.size() == lhs.size() * 2 ); std::size_t i; for ( i = 0; i < lhs.size(); ++i ) BOOST_TEST( b[ i ] == lhs[ i ] ); for ( ; i < b.size(); ++i ) BOOST_TEST( b[ i ] == true ); // Test decrease in size b.resize( lhs.size() ); BOOST_TEST( b.size() == lhs.size() ); for ( i = 0; i < lhs.size(); ++i ) BOOST_TEST( b[ i ] == lhs[ i ] ); // Test increase in size with new bits to false b.resize( lhs.size() * 2, false ); BOOST_TEST( b.size() == lhs.size() * 2 ); for ( i = 0; i < lhs.size(); ++i ) BOOST_TEST( b[ i ] == lhs[ i ] ); for ( ; i < b.size(); ++i ) BOOST_TEST( b[ i ] == false ); } static void clear( const Bitset & lhs ) { Bitset b( lhs ); b.clear(); BOOST_TEST( b.size() == 0 ); } static void pop_back( const Bitset & lhs ) { Bitset b( lhs ); b.pop_back(); BOOST_TEST( b.size() == lhs.size() - 1 ); for ( std::size_t i = 0; i < b.size(); ++i ) BOOST_TEST( b[ i ] == lhs[ i ] ); b.pop_back(); BOOST_TEST( b.size() == lhs.size() - 2 ); for ( std::size_t j = 0; j < b.size(); ++j ) BOOST_TEST( b[ j ] == lhs[ j ] ); } static void pop_front( const Bitset & lhs ) { Bitset b( lhs ); b.pop_front(); BOOST_TEST( b.size() == lhs.size() - 1 ); for ( std::size_t i = 0; i < b.size(); ++i ) BOOST_TEST( b[ i ] == lhs[ i + 1 ] ); b.pop_front(); BOOST_TEST( b.size() == lhs.size() - 2 ); for ( std::size_t j = 0; j < b.size(); ++j ) BOOST_TEST( b[ j ] == lhs[ j + 2 ] ); } static void append_bit( const Bitset & lhs ) { Bitset b( lhs ); b.push_back( true ); BOOST_TEST( b.size() == lhs.size() + 1 ); BOOST_TEST( b[ b.size() - 1 ] == true ); for ( std::size_t i = 0; i < lhs.size(); ++i ) BOOST_TEST( b[ i ] == lhs[ i ] ); b.push_back( false ); BOOST_TEST( b.size() == lhs.size() + 2 ); BOOST_TEST( b[ b.size() - 1 ] == false ); BOOST_TEST( b[ b.size() - 2 ] == true ); for ( std::size_t j = 0; j < lhs.size(); ++j ) BOOST_TEST( b[ j ] == lhs[ j ] ); } static void prepend_bit( const Bitset & lhs ) { Bitset b( lhs ); b.push_front( true ); BOOST_TEST( b.size() == lhs.size() + 1 ); BOOST_TEST( b[ 0 ] == true ); for ( std::size_t i = 0; i < lhs.size(); ++i ) BOOST_TEST( b[ i + 1 ] == lhs[ i ] ); b.push_front( false ); BOOST_TEST( b.size() == lhs.size() + 2 ); BOOST_TEST( b[ 0 ] == false ); BOOST_TEST( b[ 1 ] == true ); for ( std::size_t j = 0; j < lhs.size(); ++j ) BOOST_TEST( b[ j + 2 ] == lhs[ j ] ); } static void append_block( const Bitset & lhs ) { Bitset b( lhs ); Block value( 128 ); b.append( value ); BOOST_TEST( b.size() == lhs.size() + bits_per_block ); for ( int i = 0; i < bits_per_block; ++i ) BOOST_TEST( b[ lhs.size() + i ] == nth_bit( value, i ) ); } static void append_block_range( const Bitset & lhs, const std::vector< Block > & blocks ) { Bitset b( lhs ), c( lhs ); b.append( blocks.begin(), blocks.end() ); for ( typename std::vector< Block >::const_iterator i = blocks.begin(); i != blocks.end(); ++i ) c.append( *i ); BOOST_TEST( b == c ); } static void append_block_range_input_iter( const Bitset & lhs ) { if ( ! std::is_same< Block, unsigned char >::value ) { Bitset b( lhs ), c( lhs ); std::istringstream ss( "1 2 3" ); b.append( std::istream_iterator< Block >( ss ), std::istream_iterator< Block >() ); c.append( 1 ); c.append( 2 ); c.append( 3 ); BOOST_TEST( b == c ); } } // operator[] and reference members // PRE: b[i] == bit_vec[i] static void operator_bracket( const Bitset & lhs, const std::vector< bool > & bit_vec ) { Bitset b( lhs ); std::size_t i, j, k; // x = b[i] // x = ~b[i] for ( i = 0; i < b.size(); ++i ) { bool x = b[ i ]; BOOST_TEST( x == bit_vec[ i ] ); x = ~b[ i ]; BOOST_TEST( x == ! bit_vec[ i ] ); } Bitset prev( b ); // b[i] = x for ( j = 0; j < b.size(); ++j ) { bool x = ! prev[ j ]; b[ j ] = x; for ( k = 0; k < b.size(); ++k ) if ( j == k ) BOOST_TEST( b[ k ] == x ); else BOOST_TEST( b[ k ] == prev[ k ] ); b[ j ] = prev[ j ]; } b.flip(); // b[i] = b[j] for ( i = 0; i < b.size(); ++i ) { b[ i ] = prev[ i ]; for ( j = 0; j < b.size(); ++j ) { if ( i == j ) BOOST_TEST( b[ j ] == prev[ j ] ); else BOOST_TEST( b[ j ] == ! prev[ j ] ); } b[ i ] = ! prev[ i ]; } // b[i].flip() for ( i = 0; i < b.size(); ++i ) { b[ i ].flip(); for ( j = 0; j < b.size(); ++j ) { if ( i == j ) BOOST_TEST( b[ j ] == prev[ j ] ); else BOOST_TEST( b[ j ] == ! prev[ j ] ); } b[ i ].flip(); } } //=========================================================================== // bitwise operators // bitwise and assignment // PRE: b.size() == rhs.size() static void and_assignment( const Bitset & b, const Bitset & rhs ) { Bitset lhs( b ); Bitset prev( lhs ); lhs &= rhs; // Clears each bit in lhs for which the corresponding bit in rhs is // clear, and leaves all other bits unchanged. for ( std::size_t I = 0; I < lhs.size(); ++I ) if ( rhs[ I ] == 0 ) BOOST_TEST( lhs[ I ] == 0 ); else BOOST_TEST( lhs[ I ] == prev[ I ] ); } // PRE: b.size() == rhs.size() static void or_assignment( const Bitset & b, const Bitset & rhs ) { Bitset lhs( b ); Bitset prev( lhs ); lhs |= rhs; // Sets each bit in lhs for which the corresponding bit in rhs is set, and // leaves all other bits unchanged. for ( std::size_t I = 0; I < lhs.size(); ++I ) if ( rhs[ I ] == 1 ) BOOST_TEST( lhs[ I ] == 1 ); else BOOST_TEST( lhs[ I ] == prev[ I ] ); } // PRE: b.size() == rhs.size() static void xor_assignment( const Bitset & b, const Bitset & rhs ) { Bitset lhs( b ); Bitset prev( lhs ); lhs ^= rhs; // Flips each bit in lhs for which the corresponding bit in rhs is set, // and leaves all other bits unchanged. for ( std::size_t I = 0; I < lhs.size(); ++I ) if ( rhs[ I ] == 1 ) BOOST_TEST( lhs[ I ] == ! prev[ I ] ); else BOOST_TEST( lhs[ I ] == prev[ I ] ); } // PRE: b.size() == rhs.size() static void sub_assignment( const Bitset & b, const Bitset & rhs ) { Bitset lhs( b ); Bitset prev( lhs ); lhs -= rhs; // Resets each bit in lhs for which the corresponding bit in rhs is set, // and leaves all other bits unchanged. for ( std::size_t I = 0; I < lhs.size(); ++I ) if ( rhs[ I ] == 1 ) BOOST_TEST( lhs[ I ] == 0 ); else BOOST_TEST( lhs[ I ] == prev[ I ] ); } static void shift_left_assignment( const Bitset & b, std::size_t pos ) { Bitset lhs( b ); Bitset prev( lhs ); lhs <<= pos; // Replaces each bit at position I in lhs with the following value: // - If I < pos, the new value is zero // - If I >= pos, the new value is the previous value of the bit at // position I - pos for ( std::size_t I = 0; I < lhs.size(); ++I ) if ( I < pos ) BOOST_TEST( lhs[ I ] == 0 ); else BOOST_TEST( lhs[ I ] == prev[ I - pos ] ); } static void shift_right_assignment( const Bitset & b, std::size_t pos ) { Bitset lhs( b ); Bitset prev( lhs ); lhs >>= pos; // Replaces each bit at position I in lhs with the following value: // - If pos >= N - I, the new value is zero // - If pos < N - I, the new value is the previous value of the bit at // position I + pos std::size_t N = lhs.size(); for ( std::size_t I = 0; I < N; ++I ) if ( pos >= N - I ) BOOST_TEST( lhs[ I ] == 0 ); else BOOST_TEST( lhs[ I ] == prev[ I + pos ] ); } static void set_all( const Bitset & b ) { Bitset lhs( b ); lhs.set(); for ( std::size_t I = 0; I < lhs.size(); ++I ) BOOST_TEST( lhs[ I ] == 1 ); } static void set_one( const Bitset & b, std::size_t pos, bool value ) { Bitset lhs( b ); std::size_t N = lhs.size(); if ( pos < N ) { Bitset prev( lhs ); // Stores a new value in the bit at position pos in lhs. lhs.set( pos, value ); BOOST_TEST( lhs[ pos ] == value ); // All other values of lhs remain unchanged for ( std::size_t I = 0; I < N; ++I ) if ( I != pos ) BOOST_TEST( lhs[ I ] == prev[ I ] ); } else { // Not in range, doesn't satisfy precondition. } } static void set_segment( const Bitset & b, std::size_t pos, std::size_t len, bool value ) { Bitset lhs( b ); std::size_t N = lhs.size(); Bitset prev( lhs ); lhs.set( pos, len, value ); for ( std::size_t I = 0; I < N; ++I ) { if ( I < pos || I >= pos + len ) BOOST_TEST( lhs[ I ] == prev[ I ] ); else BOOST_TEST( lhs[ I ] == value ); } } static void reset_all( const Bitset & b ) { Bitset lhs( b ); // Resets all bits in lhs lhs.reset(); for ( std::size_t I = 0; I < lhs.size(); ++I ) BOOST_TEST( lhs[ I ] == 0 ); } static void reset_one( const Bitset & b, std::size_t pos ) { Bitset lhs( b ); std::size_t N = lhs.size(); if ( pos < N ) { Bitset prev( lhs ); lhs.reset( pos ); // Resets the bit at position pos in lhs BOOST_TEST( lhs[ pos ] == 0 ); // All other values of lhs remain unchanged for ( std::size_t I = 0; I < N; ++I ) if ( I != pos ) BOOST_TEST( lhs[ I ] == prev[ I ] ); } else { // Not in range, doesn't satisfy precondition. } } static void reset_segment( const Bitset & b, std::size_t pos, std::size_t len ) { Bitset lhs( b ); std::size_t N = lhs.size(); Bitset prev( lhs ); lhs.reset( pos, len ); for ( std::size_t I = 0; I < N; ++I ) { if ( I < pos || I >= pos + len ) BOOST_TEST( lhs[ I ] == prev[ I ] ); else BOOST_TEST( ! lhs[ I ] ); } } static void operator_flip( const Bitset & b ) { Bitset lhs( b ); Bitset x( lhs ); BOOST_TEST( ~lhs == x.flip() ); } static void flip_all( const Bitset & b ) { Bitset lhs( b ); std::size_t N = lhs.size(); Bitset prev( lhs ); lhs.flip(); BOOST_TEST( lhs.size() == N ); // Toggles all the bits in lhs for ( std::size_t I = 0; I < N; ++I ) BOOST_TEST( lhs[ I ] == ! prev[ I ] ); } static void flip_one( const Bitset & b, std::size_t pos ) { Bitset lhs( b ); std::size_t N = lhs.size(); if ( pos < N ) { Bitset prev( lhs ); lhs.flip( pos ); BOOST_TEST( lhs.size() == N ); // Toggles the bit at position pos in lhs BOOST_TEST( lhs[ pos ] == ! prev[ pos ] ); // All other values of lhs remain unchanged for ( std::size_t I = 0; I < N; ++I ) if ( I != pos ) BOOST_TEST( lhs[ I ] == prev[ I ] ); } else { // Not in range, doesn't satisfy precondition. } } static void flip_segment( const Bitset & b, std::size_t pos, std::size_t len ) { Bitset lhs( b ); std::size_t N = lhs.size(); Bitset prev( lhs ); lhs.flip( pos, len ); for ( std::size_t I = 0; I < N; ++I ) { if ( I < pos || I >= pos + len ) BOOST_TEST( lhs[ I ] == prev[ I ] ); else BOOST_TEST( lhs[ I ] != prev[ I ] ); } } // empty static void empty( const Bitset & b ) { BOOST_TEST( b.empty() == ( b.size() == 0 ) ); } // to_ulong() static void to_ulong( const Bitset & lhs ) { typedef unsigned long result_type; std::size_t n = std::numeric_limits< result_type >::digits; std::size_t sz = lhs.size(); bool will_overflow = false; for ( std::size_t i = n; i < sz; ++i ) { if ( lhs.test( i ) != 0 ) { will_overflow = true; break; } } if ( will_overflow ) { try { (void)lhs.to_ulong(); BOOST_TEST( false ); // It should have thrown an exception } catch ( std::overflow_error & ex ) { // Good! BOOST_TEST( ! ! ex.what() ); } catch ( ... ) { BOOST_TEST( false ); // threw the wrong exception } } else { result_type num = lhs.to_ulong(); // Be sure the number is right if ( sz == 0 ) BOOST_TEST( num == 0 ); else { for ( std::size_t i = 0; i < sz; ++i ) BOOST_TEST( lhs[ i ] == ( i < n ? nth_bit( num, i ) : 0 ) ); } } } static void to_string( const Bitset & b ) { std::string str; boost::to_string( b, str ); BOOST_TEST( str.size() == b.size() ); for ( std::size_t i = 0; i < b.size(); ++i ) BOOST_TEST( str[ b.size() - 1 - i ] == ( b.test( i ) ? '1' : '0' ) ); } static void count( const Bitset & b ) { std::size_t c = b.count(); std::size_t actual = 0; for ( std::size_t i = 0; i < b.size(); ++i ) if ( b[ i ] ) ++actual; BOOST_TEST( c == actual ); } static void size( const Bitset & b ) { BOOST_TEST( Bitset( b ).set().count() == b.size() ); } static void capacity( const Bitset & lhs ) { Bitset b( lhs ); b.resize( 200 ); BOOST_TEST( b.capacity() >= 200 ); } static void reserve_test_one( const Bitset & lhs ) { // empty bitset Bitset b( lhs ); b.reserve( 16 ); BOOST_TEST( b.capacity() >= 16 ); } static void reserve_test_two( const Bitset & lhs ) { // bitset constructed with size "100" Bitset b( lhs ); BOOST_TEST( b.capacity() >= 100 ); b.reserve( 60 ); BOOST_TEST( b.size() == 100 ); BOOST_TEST( b.capacity() >= 100 ); b.reserve( 160 ); BOOST_TEST( b.size() == 100 ); BOOST_TEST( b.capacity() >= 160 ); } static void shrink_to_fit_test_one( const Bitset & lhs ) { // empty bitset Bitset b( lhs ); b.shrink_to_fit(); BOOST_TEST( b.size() == 0 ); BOOST_TEST( b.capacity() == Bitset().capacity() ); } static void shrink_to_fit_test_two( const Bitset & lhs ) { // bitset constructed with size "100" Bitset b( lhs ); b.shrink_to_fit(); BOOST_TEST( b.capacity() >= 100 ); BOOST_TEST( b.size() == 100 ); b.reserve( 550 ); BOOST_TEST( b.capacity() >= 550 ); BOOST_TEST( b.size() == 100 ); b.shrink_to_fit(); BOOST_TEST( b.capacity() < 550 ); BOOST_TEST( b.size() == 100 ); } static void all( const Bitset & b ) { BOOST_TEST( b.all() == ( b.count() == b.size() ) ); bool result = true; for ( std::size_t i = 0; i < b.size(); ++i ) if ( ! b[ i ] ) { result = false; break; } BOOST_TEST( b.all() == result ); } static void any( const Bitset & b ) { BOOST_TEST( b.any() == ( b.count() != 0 ) ); bool result = false; for ( std::size_t i = 0; i < b.size(); ++i ) if ( b[ i ] ) { result = true; break; } BOOST_TEST( b.any() == result ); } static void none( const Bitset & b ) { bool result = true; for ( std::size_t i = 0; i < b.size(); ++i ) { if ( b[ i ] ) { result = false; break; } } BOOST_TEST( b.none() == result ); // sanity BOOST_TEST( b.none() == ! b.any() ); BOOST_TEST( b.none() == ( b.count() == 0 ) ); } static void subset( const Bitset & a, const Bitset & b ) { BOOST_TEST( a.size() == b.size() ); // PRE bool is_subset = true; if ( b.size() ) { // could use b.any() but let's be safe for ( std::size_t i = 0; i < a.size(); ++i ) { if ( a.test( i ) && ! b.test( i ) ) { is_subset = false; break; } } } else { // sanity BOOST_TEST( a.count() == 0 ); BOOST_TEST( a.any() == false ); // is_subset = (a.any() == false); } BOOST_TEST( a.is_subset_of( b ) == is_subset ); } static void proper_subset( const Bitset & a, const Bitset & b ) { // PRE: a.size() == b.size() BOOST_TEST( a.size() == b.size() ); bool is_proper = false; if ( b.size() != 0 ) { // check it's a subset subset( a, b ); // is it proper? for ( std::size_t i = 0; i < a.size(); ++i ) { if ( ! a.test( i ) && b.test( i ) ) { is_proper = true; // sanity BOOST_TEST( a.count() < b.count() ); BOOST_TEST( b.any() ); } } } BOOST_TEST( a.is_proper_subset_of( b ) == is_proper ); if ( is_proper ) BOOST_TEST( b.is_proper_subset_of( a ) != is_proper ); // antisymmetry } static void intersects( const Bitset & a, const Bitset & b ) { bool have_intersection = false; typename Bitset::size_type m = a.size() < b.size() ? a.size() : b.size(); for ( typename Bitset::size_type i = 0; i < m && ! have_intersection; ++i ) if ( a[ i ] == true && b[ i ] == true ) have_intersection = true; BOOST_TEST( a.intersects( b ) == have_intersection ); // also check commutativity BOOST_TEST( b.intersects( a ) == have_intersection ); } static void 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 ); // find first bit with value `value` from offset onwards, if any typename Bitset::size_type i = offset; while ( i < b.size() && b[ i ] != value ) ++i; if ( i >= b.size() ) BOOST_TEST( result == Bitset::npos ); // not found; else { BOOST_TEST( result == i ); BOOST_TEST( b.test( i ) == value ); } } 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 ) ); } else { BOOST_TEST( next_bit_off( b, pos ) == b.find_next_off( pos ) ); } } static void operator_equal( const Bitset & a, const Bitset & b ) { if ( a == b ) { for ( std::size_t I = 0; I < a.size(); ++I ) BOOST_TEST( a[ I ] == b[ I ] ); } else { if ( a.size() == b.size() ) { bool diff = false; for ( std::size_t I = 0; I < a.size(); ++I ) if ( a[ I ] != b[ I ] ) { diff = true; break; } BOOST_TEST( diff ); } } } static void operator_not_equal( const Bitset & a, const Bitset & b ) { if ( a != b ) { if ( a.size() == b.size() ) { bool diff = false; for ( std::size_t I = 0; I < a.size(); ++I ) if ( a[ I ] != b[ I ] ) { diff = true; break; } BOOST_TEST( diff ); } } else { BOOST_TEST( a.size() == b.size() ); for ( std::size_t I = 0; I < a.size(); ++I ) BOOST_TEST( a[ I ] == b[ I ] ); } } static bool less_than( const Bitset & a, const Bitset & b ) { typedef BOOST_DEDUCED_TYPENAME Bitset::size_type size_type; size_type asize( a.size() ); size_type bsize( b.size() ); if ( ! bsize ) { return false; } else if ( ! asize ) { return true; } else { // Compare from most significant to least. size_type leqsize( std::min BOOST_PREVENT_MACRO_SUBSTITUTION( asize, bsize ) ); size_type I; for ( I = 0; I < leqsize; ++I, --asize, --bsize ) { size_type i = asize - 1; size_type j = bsize - 1; if ( a[ i ] < b[ j ] ) return true; else if ( a[ i ] > b[ j ] ) return false; // if (a[i] = b[j]) skip to next } return a.size() < b.size(); } } static typename Bitset::size_type next_bit_on( const Bitset & b, typename Bitset::size_type prev ) { // helper function for find_next() // if ( b.none() == true || 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 ] == 0 ) ++i; 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 ) { if ( less_than( a, b ) ) BOOST_TEST( a < b ); else BOOST_TEST( ! ( a < b ) ); } static void operator_greater_than( const Bitset & a, const Bitset & b ) { if ( less_than( a, b ) || a == b ) BOOST_TEST( ! ( a > b ) ); else BOOST_TEST( a > b ); } static void operator_less_than_eq( const Bitset & a, const Bitset & b ) { if ( less_than( a, b ) || a == b ) BOOST_TEST( a <= b ); else BOOST_TEST( ! ( a <= b ) ); } static void operator_greater_than_eq( const Bitset & a, const Bitset & b ) { if ( less_than( a, b ) ) BOOST_TEST( ! ( a >= b ) ); else BOOST_TEST( a >= b ); } static void test_bit( const Bitset & b, std::size_t pos ) { Bitset lhs( b ); std::size_t N = lhs.size(); if ( pos < N ) { BOOST_TEST( lhs.test( pos ) == lhs[ pos ] ); } else { // Not in range, doesn't satisfy precondition. } } static void test_set_bit( const Bitset & b, std::size_t pos, bool value ) { Bitset lhs( b ); std::size_t N = lhs.size(); if ( pos < N ) { Bitset prev( lhs ); // Stores a new value in the bit at position pos in lhs. BOOST_TEST( lhs.test_set( pos, value ) == prev[ pos ] ); BOOST_TEST( lhs[ pos ] == value ); // All other values of lhs remain unchanged for ( std::size_t I = 0; I < N; ++I ) if ( I != pos ) BOOST_TEST( lhs[ I ] == prev[ I ] ); } else { // Not in range, doesn't satisfy precondition. } } static void operator_shift_left( const Bitset & lhs, std::size_t pos ) { Bitset x( lhs ); BOOST_TEST( ( lhs << pos ) == ( x <<= pos ) ); } static void operator_shift_right( const Bitset & lhs, std::size_t pos ) { Bitset x( lhs ); BOOST_TEST( ( lhs >> pos ) == ( x >>= pos ) ); } static void at( const Bitset & lhs, const std::vector< bool > & bit_vec ) { Bitset b( lhs ); std::size_t i, j, k; // x = b.at(i) // x = ~b.at(i) for ( i = 0; i < b.size(); ++i ) { bool x = b.at( i ); BOOST_TEST( x == bit_vec.at( i ) ); x = ~b.at( i ); BOOST_TEST( x == ! bit_vec.at( i ) ); } Bitset prev( b ); // b.at(i) = x for ( j = 0; j < b.size(); ++j ) { bool x = ! prev.at( j ); b.at( j ) = x; for ( k = 0; k < b.size(); ++k ) if ( j == k ) BOOST_TEST( b.at( k ) == x ); else BOOST_TEST( b.at( k ) == prev.at( k ) ); b.at( j ) = prev.at( j ); } b.flip(); // b.at(i) = b.at(j) for ( i = 0; i < b.size(); ++i ) { b.at( i ) = prev.at( i ); for ( j = 0; j < b.size(); ++j ) { if ( i == j ) BOOST_TEST( b.at( j ) == prev.at( j ) ); else BOOST_TEST( b.at( j ) == ! prev.at( j ) ); } b.at( i ) = ! prev.at( i ); } // b.at(i).flip() for ( i = 0; i < b.size(); ++i ) { b.at( i ).flip(); for ( j = 0; j < b.size(); ++j ) { if ( i == j ) BOOST_TEST( b.at( j ) == prev.at( j ) ); else BOOST_TEST( b.at( j ) == ! prev.at( j ) ); } b.at( i ).flip(); } // b.at(b.size()) bool will_out_of_range = false; for ( i = 0; i <= b.size(); ++i ) { if ( i == b.size() ) { will_out_of_range = true; break; } b.at( i ); } if ( will_out_of_range ) { try { b.at( b.size() ); BOOST_TEST( false ); // It should have thrown an exception } catch ( const std::out_of_range & ex ) { // Good! BOOST_TEST( ! ! ex.what() ); } catch ( ... ) { BOOST_TEST( false ); // threw the wrong exception } } } // operator| static void operator_or( const Bitset & lhs, const Bitset & rhs ) { Bitset x( lhs ); BOOST_TEST( ( lhs | rhs ) == ( x |= rhs ) ); } // operator& static void operator_and( const Bitset & lhs, const Bitset & rhs ) { Bitset x( lhs ); BOOST_TEST( ( lhs & rhs ) == ( x &= rhs ) ); } // operator^ static void operator_xor( const Bitset & lhs, const Bitset & rhs ) { Bitset x( lhs ); BOOST_TEST( ( lhs ^ rhs ) == ( x ^= rhs ) ); } // operator- static void operator_sub( const Bitset & lhs, const Bitset & rhs ) { Bitset x( lhs ); BOOST_TEST( ( lhs - rhs ) == ( x -= rhs ) ); } //------------------------------------------------------------------------------ // I/O TESTS // The following tests assume the results of extraction (i.e.: contents, // state and width of is, contents of b) only depend on input (the string // str). In other words, they don't consider "unexpected" errors such as // stream corruption or out of memory. The reason is simple: if e.g. the // stream buffer throws, the stream layer may eat the exception and // transform it into a badbit. But we can't trust the stream state here, // because one of the things that we want to test is exactly whether it // is set correctly. Similarly for insertion. // // To provide for these cases would require that the test functions know // in advance whether the stream buffer and/or allocations will fail, and // when; that is, we should write both a special allocator and a special // stream buffer capable of throwing "on demand" and pass them here. // Seems overkill for these kinds of unit tests. //------------------------------------------------------------------------- // operator<<( [basic_]ostream, template< typename Stream > static void stream_inserter( const Bitset & b, Stream & s, const char * file_name ) { typedef typename Stream::char_type char_type; typedef std::basic_string< char_type > string_type; typedef std::basic_ifstream< char_type > corresponding_input_stream_type; std::ios::iostate except = s.exceptions(); typedef typename Bitset::size_type size_type; std::streamsize w = s.width(); char_type fill_char = s.fill(); std::ios::iostate oldstate = s.rdstate(); bool stream_was_good = s.good(); bool did_throw = false; try { s << b; } catch ( const std::ios_base::failure & ) { BOOST_TEST( ( except & s.rdstate() ) != 0 ); did_throw = true; } catch ( ... ) { did_throw = true; } BOOST_TEST( did_throw || ! stream_was_good || ( s.width() == 0 ) ); if ( ! stream_was_good ) { BOOST_TEST( s.good() == false ); // this should actually be oldstate == s.rdstate() // but some implementations add badbit in the // sentry constructor // BOOST_TEST( ( oldstate & s.rdstate() ) == oldstate ); BOOST_TEST( s.width() == w ); } else { if ( ! did_throw ) BOOST_TEST( s.width() == 0 ); size_type total_len = w <= 0 || static_cast< size_type >( w ) < b.size() ? b.size() : static_cast< size_type >( w ); const string_type padding( total_len - b.size(), fill_char ); string_type expected; boost::to_string( b, expected ); if ( ( s.flags() & std::ios::adjustfield ) != std::ios::left ) expected = padding + expected; else expected = expected + padding; assert( expected.length() == total_len ); // close, and reopen the file stream to verify contents s.close(); corresponding_input_stream_type is( file_name ); string_type contents; std::getline( is, contents, char_type() ); BOOST_TEST( contents == expected ); } } // operator>>( [basic_]istream template< typename Stream, typename String > static void stream_extractor( Bitset & b, Stream & is, const String & str ) { // save necessary info then do extraction // const std::streamsize w = is.width(); Bitset a_copy( b ); bool stream_was_good = is.good(); bool did_throw = false; const std::ios::iostate except = is.exceptions(); bool has_stream_exceptions = true; try { is >> b; } catch ( const std::ios::failure & ) { did_throw = true; } // postconditions BOOST_TEST( except == is.exceptions() ); // paranoid //------------------------------------------------------------------ // postconditions BOOST_TEST( b.size() <= b.max_size() ); if ( w > 0 ) BOOST_TEST( b.size() <= static_cast< typename Bitset::size_type >( w ) ); // throw if and only if required if ( has_stream_exceptions ) { const bool exceptional_state = has_flags( is, is.exceptions() ); BOOST_TEST( exceptional_state == did_throw ); } typedef typename String::size_type size_type; typedef typename String::value_type Ch; size_type after_digits = 0; if ( ! stream_was_good ) { BOOST_TEST( has_flags( is, std::ios::failbit ) ); BOOST_TEST( b == a_copy ); BOOST_TEST( is.width() == ( did_throw ? w : 0 ) ); } else { // stream was good(), parse the string; // it may contain three parts, all of which are optional // {spaces} {digits} {non-digits} // opt opt opt // // The values of b.max_size() and is.width() may lead to // ignore part of the digits, if any. size_type pos = 0; size_type len = str.length(); // {spaces} for ( ; pos < len && is_white_space( is, str[ pos ] ); ++pos ) { } size_type after_spaces = pos; // {digits} or part of them const typename Bitset::size_type max_digits = w > 0 && static_cast< typename Bitset::size_type >( w ) < b.max_size() ? static_cast< typename Bitset::size_type >( w ) : b.max_size(); for ( ; pos < len && ( pos - after_spaces ) < max_digits; ++pos ) { if ( ! is_one_or_zero( is, str[ pos ] ) ) break; } after_digits = pos; size_type num_digits = after_digits - after_spaces; // eofbit if ( ( after_digits == len && max_digits > num_digits ) ) BOOST_TEST( has_flags( is, std::ios::eofbit ) ); else BOOST_TEST( ! has_flags( is, std::ios::eofbit ) ); // failbit <=> there are no digits, except for the library // issue explained below. // if ( num_digits == 0 ) { if ( after_digits == len && has_stream_exceptions && ( is.exceptions() & std::ios::eofbit ) != std::ios::goodbit ) { // This is a special case related to library issue 195: // reaching eof when skipping whitespaces in the sentry ctor. // The resolution says the sentry constructor should set *both* // eofbit and failbit; but many implementations deliberately // set eofbit only. See for instance: // http://gcc.gnu.org/ml/libstdc++/2000-q1/msg00086.html // BOOST_TEST( did_throw ); } else { BOOST_TEST( has_flags( is, std::ios::failbit ) ); } } else BOOST_TEST( ! has_flags( is, std::ios::failbit ) ); if ( num_digits == 0 && after_digits == len ) { BOOST_TEST( b == a_copy ); } else { String sub = str.substr( after_spaces, num_digits ); BOOST_TEST( b == Bitset( sub ) ); } // check width BOOST_TEST( is.width() == 0 || ( after_digits == len && num_digits == 0 && did_throw ) ); } // clear the stream to allow further reading then // retrieve any remaining chars with a single getline() is.exceptions( std::ios::goodbit ); is.clear(); String remainder; std::getline( is, remainder, Ch() ); if ( stream_was_good ) BOOST_TEST( remainder == str.substr( after_digits ) ); else BOOST_TEST( remainder == str ); } }; #endif // include guard