Files
dynamic_bitset/test/bitset_test.hpp
2025-10-14 19:24:45 +02:00

1524 lines
48 KiB
C++

// -----------------------------------------------------------
// 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/core/lightweight_test.hpp"
#include "boost/dynamic_bitset/dynamic_bitset.hpp"
#include "boost/filesystem.hpp"
#include "boost/limits.hpp"
#include <algorithm>
#include <assert.h> // <cassert> is sometimes macro-guarded :-(
#include <string>
#include <vector>
#if ! defined( BOOST_NO_STD_LOCALE )
# include <locale>
#endif
template< typename Block >
inline 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()
{
boost::filesystem::remove( m_path );
}
const boost::filesystem::path &
path() const
{
return m_path;
}
private:
boost::filesystem::path m_path;
};
#if defined BOOST_NO_STD_LOCALE
template< typename Stream >
bool
is_one_or_zero( const Stream & /*s*/, char c )
{
return c == '1' || c == '0';
}
template< typename Stream >
bool
is_white_space( const Stream & /*s*/, char c )
{
return std::isspace( c );
}
#else
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() );
}
#endif
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;
BOOST_STATIC_CONSTANT( 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 );
}
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 ) {
typename Bitset::block_width_type i = 0;
for ( ; 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
// PRE: std::equal(first1, last1, first2) == true
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 ) {
typename Bitset::block_width_type i = 0;
for ( ; 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 ) {
typename Bitset::block_width_type i = 0;
for ( ; 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 );
}
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
// move constructor (absent from std::bitset)
static void
move_constructor( const Bitset & b )
{
Bitset copy( boost::move( b ) );
BOOST_TEST( b == copy );
}
// 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 = boost::move( c );
b = boost::move( b ); // self assignment check
BOOST_TEST( b == rhs );
}
#endif // BOOST_NO_CXX11_RVALUE_REFERENCES
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 );
// references must be stable under a swap
for ( typename Bitset::size_type i = 0; i < lhs.size(); ++i ) {
Bitset b1( lhs );
Bitset b2( rhs );
typename Bitset::reference ref = b1[ i ];
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 );
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() );
BOOST_TEST( b == lhs );
// Test increase in size
b.resize( lhs.size() * 2, true );
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() );
for ( i = 0; i < lhs.size(); ++i )
BOOST_TEST( b[ i ] == lhs[ i ] );
}
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
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
append_block( const Bitset & lhs )
{
Bitset b( lhs );
Block value( 128 );
b.append( value );
BOOST_TEST( b.size() == lhs.size() + bits_per_block );
for ( typename Bitset::block_width_type i = 0; i < bits_per_block; ++i )
BOOST_TEST( b[ lhs.size() + i ] == bool( ( value >> i ) & 1 ) );
}
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 );
}
// 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();
// 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 );
// 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 ) );
}
}
}
// to_string()
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_test_one( const Bitset & lhs )
{
// empty bitset
Bitset b( lhs );
BOOST_TEST( b.capacity() == 0 );
}
static void
capacity_test_two( const Bitset & lhs )
{
// bitset constructed with size "100"
Bitset b( lhs );
BOOST_TEST( b.capacity() >= 100 );
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() == 0 );
}
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( 200 );
BOOST_TEST( b.capacity() >= 200 );
BOOST_TEST( b.size() == 100 );
b.shrink_to_fit();
BOOST_TEST( b.capacity() < 200 );
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 )
{
// find first non-null bit from offset onwards, if any
typename Bitset::size_type i = offset;
while ( i < b.size() && b[ i ] == 0 )
++i;
if ( i >= b.size() )
BOOST_TEST( b.find_first( offset ) == Bitset::npos ); // not found;
else {
BOOST_TEST( b.find_first( offset ) == i );
BOOST_TEST( b.test( i ) == true );
}
}
static void
find_pos( const Bitset & b, typename Bitset::size_type pos )
{
find_first( b, pos );
BOOST_TEST( next_bit_on( b, pos ) == b.find_next( 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 {
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 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 );
// This test require that os be an output _and_ input stream.
// Of course dynamic_bitset's operator << doesn't require that.
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 ) {
// The VC6 library has a bug/non-conformity in the sentry
// constructor. It uses code like
// // skip whitespaces...
// int_type _C = rdbuf()->sgetc();
// while (!_Tr::eq_int_type(_Tr::eof(), _C) ...
//
// For an empty file the while statement is never "entered"
// and the stream remains in good() state; thus the sentry
// object gives "true" when converted to bool. This is worse
// than the case above, because not only failbit is not set,
// but no bit is set at all, end we end up clearing the
// bitset though there's nothing in the file to be extracted.
// Note that the dynamic_bitset docs say a sentry object is
// constructed and then converted to bool, thus we rely on
// what the underlying library does.
//
#if ! defined( BOOST_DINKUMWARE_STDLIB ) || ( BOOST_DINKUMWARE_STDLIB >= 306 )
BOOST_TEST( b == a_copy );
#else
BOOST_TEST( b.empty() == true );
#endif
} 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