From 8b43c83ed7e5fc630b04caf8c9355c5bf6a5a9a6 Mon Sep 17 00:00:00 2001
From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz>
Date: Mon, 20 Feb 2017 14:17:21 +0100
Subject: [PATCH] bitwise operations on vector bool

---
 alib2std/src/extensions/vector.hpp          | 187 +++++++++++++-
 alib2std/test-src/extensions/VectorTest.cpp | 269 ++++++++++++++++++++
 alib2std/test-src/extensions/VectorTest.h   |  10 +
 3 files changed, 465 insertions(+), 1 deletion(-)

diff --git a/alib2std/src/extensions/vector.hpp b/alib2std/src/extensions/vector.hpp
index 5dcfb42832..69164eb3ba 100644
--- a/alib2std/src/extensions/vector.hpp
+++ b/alib2std/src/extensions/vector.hpp
@@ -47,7 +47,192 @@ string to_string ( const std::vector < T, Ts ... > & value ) {
 	return ss.str();
 }
 
+template < class ... Ts >
+vector < bool, Ts ... > & operator |= ( vector < bool, Ts ... > & A, const vector < bool, Ts ... > & B ) {
+	A.resize ( std::max ( A.size ( ), B.size ( ) ) );
+
+	typename vector < bool, Ts ... >::iterator itA = A.begin ( );
+	typename vector < bool, Ts ... >::const_iterator itB = B.begin ( );
+
+	// c++ implementation-specific
+	while ( itB < B.end ( ) ) // A is longer or of the same size as B
+		* ( itA._M_p ++ ) |= * ( itB._M_p ++ ); // word-at-a-time bitwise operation
+
+	// The rest of A above the size of B can be left intact
+
+	return A;
+}
+
+template < class ... Ts >
+vector < bool, Ts ... > operator | ( vector < bool, Ts ... > A, const vector < bool, Ts ... > & B ) {
+	A |= B;
+	return A;
+}
+
+template < class ... Ts >
+vector < bool, Ts ... > & operator &= ( vector < bool, Ts ... > & A, const vector < bool, Ts ... > & B ) {
+	A.resize ( std::max ( A.size ( ), B.size ( ) ) );
+
+	typename vector < bool, Ts ... >::iterator itA = A.begin ( );
+	typename vector < bool, Ts ... >::const_iterator itB = B.begin ( );
+
+	// c++ implementation-specific
+	while ( itB < B.end ( ) ) // A is longer or of the same size as B
+		* ( itA._M_p ++ ) &= * ( itB._M_p ++ ); // word-at-a-time bitwise operation
+
+	while ( itA < A.end ( ) ) // The rest of A above the size of B shall be zeroed
+		* ( itA._M_p ++ ) = 0;
+
+	return A;
+}
+
+template < class ... Ts >
+vector < bool, Ts ... > operator & ( vector < bool, Ts ... > A, const vector < bool, Ts ... > & B ) {
+	A &= B;
+	return A;
+}
+
+template < class ... Ts >
+vector < bool, Ts ... > & operator ^= ( vector < bool, Ts ... > & A, const vector < bool, Ts ... > & B ) {
+	A.resize ( std::max ( A.size ( ), B.size ( ) ) );
+
+	typename vector < bool, Ts ... >::iterator itA = A.begin ( );
+	typename vector < bool, Ts ... >::const_iterator itB = B.begin ( );
+
+	// c++ implementation-specific
+	while ( itB < B.end ( ) ) // A is longer or of the same size as B
+		* ( itA._M_p ++ ) ^= * ( itB._M_p ++ ); // word-at-a-time bitwise operation
+
+	while ( itA < A.end ( ) ) // The rest of A above the size of B shall be flipped
+		* ( itA._M_p ++ ) = ~ * ( itA._M_p ++ );
+
+	return A;
+}
+
+template < class ... Ts >
+vector < bool, Ts ... > operator ^ ( vector < bool, Ts ... > A, const vector < bool, Ts ... > & B ) {
+	A ^= B;
+	return A;
+}
+
+template < class ... Ts >
+vector < bool, Ts ... > operator ~ ( vector < bool, Ts ... > A ) {
+	A.flip ( );
+	return A;
+}
+
+// the same type as the vector bool's internal storage type
+typedef typename decay < decltype ( * ( declval < vector < bool > > ( ).begin ( )._M_p ) ) >::type vectorBoolInternalType;
+// the size of the vector bool's internal storage type
+const unsigned vectorBoolInternalTypeInBits = sizeof ( vectorBoolInternalType ) * 8;
+
+// private helper for mask computation
+inline vectorBoolInternalType getMask ( size_t dist ) {
+	return ( ( vectorBoolInternalType { } + 1 ) << dist ) - 1;
+}
+
+template < class ... Ts >
+vector < bool, Ts ... > & operator <<= ( vector < bool, Ts ... > & A, size_t dist ) {
+	size_t distBlocks = dist / vectorBoolInternalTypeInBits;
+	size_t distWithin = dist % vectorBoolInternalTypeInBits;
+	size_t backDist = vectorBoolInternalTypeInBits - distWithin;
+
+	// shift blocks if needed
+	if ( distBlocks ) {
+		// simulate behavior of reverse iterator
+		auto itAReverse = A.end ( ) - 1;
+		auto itASource = itAReverse;
+		itASource._M_p -= distBlocks;
+
+		while ( itASource >= A.begin ( ) )
+			* ( itAReverse._M_p -- ) = * ( itASource._M_p -- );
+
+		while ( itAReverse >= A.begin ( ) )
+			* ( itAReverse._M_p -- ) = 0;
+	}
+
+	// shift by the rest dist
+	{
+		vectorBoolInternalType bits1 { };
+		vectorBoolInternalType bits2 { };
+		// reuse bits1 which is zeroed in initialization to create one and the mask
+		vectorBoolInternalType mask = ~ getMask ( distWithin );
+
+		// it might be more clear to iterate from the begining. However this is working nicely
+		auto itA = A.begin ( );
+
+		while ( itA < A.end ( ) ) {
+			bits2 = * ( itA._M_p ) & mask;
+			* ( itA._M_p ) = * ( itA._M_p ) << distWithin | bits1 >> backDist;
+			bits1 = bits2;
+
+			itA._M_p ++;
+		}
+	}
+
+	return A;
+}
+
+template < class ... Ts >
+vector < bool, Ts ... > operator << ( vector < bool, Ts ... > A, size_t dist ) {
+	A <<= dist;
+	return A;
+}
+
+template < class ... Ts >
+vector < bool, Ts ... > & operator >>= ( vector < bool, Ts ... > & A, size_t dist ) {
+	size_t distBlocks = dist / vectorBoolInternalTypeInBits;
+	size_t distWithin = dist % vectorBoolInternalTypeInBits;
+	size_t sizeWithin = A.size ( ) % vectorBoolInternalTypeInBits;
+	size_t backDist = vectorBoolInternalTypeInBits - distWithin;
+
+	// shift blocks if needed
+	if ( distBlocks ) {
+		auto itA = A.begin ( );
+		auto itASource = itA;
+		itASource._M_p += distBlocks;
+
+		while ( itASource < A.end ( ) )
+			* ( itA._M_p ++ ) = * ( itASource._M_p ++ );
+
+		while ( itA < A.end ( ) )
+			* ( itA._M_p ++ ) = 0;
+	}
+
+	// shift by the rest dist
+	{
+		vectorBoolInternalType bits1 { };
+		vectorBoolInternalType bits2 { };
+		// reuse bits1 which is zeroed in initialization to create one and the mask
+		vectorBoolInternalType mask = getMask ( distWithin );
+
+		// it might be more clear to iterate from the begining. However this is working nicely
+		auto itAReverse = A.end ( ) - 1;
+
+		// upper part of the last word in the vector can contain some garbage so it needs to be cleared
+		vectorBoolInternalType maskTopWord = getMask ( sizeWithin );
+		if ( maskTopWord != 0 )
+			* ( itAReverse._M_p ) &= maskTopWord;
+
+		// simulate behavior of reverse iterator
+		while ( itAReverse >= A.begin ( ) ) {
+			bits2 = * ( itAReverse._M_p ) & mask;
+			* ( itAReverse._M_p ) = * ( itAReverse._M_p ) >> distWithin | bits1 << backDist;
+			bits1 = bits2;
+
+			itAReverse._M_p --;
+		}
+	}
+
+	return A;
+}
+
+template < class ... Ts >
+vector < bool, Ts ... > operator >> ( vector < bool, Ts ... > A, size_t dist ) {
+	A >>= dist;
+	return A;
+}
+
 } /* namespace std */
 
 #endif /* __VECTOR_HPP_ */
-
diff --git a/alib2std/test-src/extensions/VectorTest.cpp b/alib2std/test-src/extensions/VectorTest.cpp
index e7420bda41..19a2a51bc7 100644
--- a/alib2std/test-src/extensions/VectorTest.cpp
+++ b/alib2std/test-src/extensions/VectorTest.cpp
@@ -18,3 +18,272 @@ void VectorTest::testProperties() {
 
 	CPPUNIT_ASSERT((std::is_same<decltype(constData[0]), const int&>::value));
 }
+
+void VectorTest::testVectorBool() {
+	std::vector < bool > A;
+	A.push_back ( true );
+	A.push_back ( false );
+	A.push_back ( true );
+	A.push_back ( false );
+
+	std::vector < bool > B;
+	B.push_back ( true );
+	B.push_back ( true );
+	B.push_back ( false );
+	B.push_back ( false );
+
+	{
+		std::vector < bool > C = A | B;
+		std::vector < bool > D = A;
+		D |= B;
+
+		std::vector < bool > X;
+		X.push_back ( true );
+		X.push_back ( true );
+		X.push_back ( true );
+		X.push_back ( false );
+
+		std::cout << X << std::endl;
+		std::cout << C << std::endl;
+
+		CPPUNIT_ASSERT ( C == X );
+		CPPUNIT_ASSERT ( D == X );
+	}
+
+	{
+		std::vector < bool > C = A & B;
+		std::vector < bool > D = A;
+		D &= B;
+
+		std::vector < bool > X;
+		X.push_back ( true );
+		X.push_back ( false );
+		X.push_back ( false );
+		X.push_back ( false );
+
+		CPPUNIT_ASSERT ( C == X );
+		CPPUNIT_ASSERT ( D == X );
+	}
+
+	{
+		std::vector < bool > C = A ^ B;
+		std::vector < bool > D = A;
+		D ^= B;
+
+		std::vector < bool > X;
+		X.push_back ( false );
+		X.push_back ( true );
+		X.push_back ( true );
+		X.push_back ( false );
+
+		CPPUNIT_ASSERT ( C == X );
+		CPPUNIT_ASSERT ( D == X );
+	}
+}
+
+void testOffset ( int offset ) {
+	unsigned long long shadow = 0x2A76B147D6521C87ULL;
+	std::vector < bool > data;
+
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8 + offset; ++ i ) {
+		data.push_back ( true );
+	}
+
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		data.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+
+	data >>= sizeof ( unsigned long long ) * 6 - 1;
+
+	std::vector < bool > ref;
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 2 + offset; ++ i ) {
+		ref.push_back ( true );
+	}
+
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		ref.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+
+	ref.resize ( 128 + offset );
+
+	std::cout << "data = " << data << std::endl;
+	std::cout << "ref  = " << ref << std::endl;
+
+	CPPUNIT_ASSERT ( data == ref );
+}
+
+void VectorTest::testVectorBool2() {
+	unsigned long long shadow = 0x2A76B147D6521C87ULL;
+	std::vector < bool > data;
+
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		data.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+
+	shadow >>= 10;
+	data >>= 10;
+
+	std::vector < bool > ref;
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		ref.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+
+	// std::cout << "data = " << data << std::endl;
+	// std::cout << "ref  = " << ref << std::endl;
+
+	CPPUNIT_ASSERT ( data == ref );
+
+	for ( unsigned long i = - sizeof ( unsigned long long ) / 2 ; i < sizeof ( unsigned long long ) * 8; ++ i )
+		testOffset ( i );
+}
+
+void VectorTest::testVectorBool3() {
+	unsigned long long shadow = 0x2A76B147D6521C87ULL;
+	std::vector < bool > data;
+
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		data.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		data.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		data.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+
+	std::vector < bool > data2 = data;
+	data2 >>= 64;
+
+	data >>= 32;
+	data >>= 16;
+	data >>= 8;
+	data >>= 4;
+	data >>= 2;
+	data >>= 1;
+	data >>= 1;
+
+	std::cout << "data  = " << data  << std::endl;
+	std::cout << "data2 = " << data2 << std::endl;
+
+	data2 >>= 1;
+	data2 >>= 2;
+	data2 >>= 4;
+	data2 >>= 8;
+	data2 >>= 16;
+	data2 >>= 32;
+	data2 >>= 1;
+
+	data >>= 27;
+	data >>= 17;
+	data >>= 13;
+	data >>= 7;
+
+	std::cout << "data  = " << data  << std::endl;
+	std::cout << "data2 = " << data2 << std::endl;
+
+	CPPUNIT_ASSERT ( data2 == data );
+}
+
+void testOffset2 ( int offset ) {
+	unsigned long long shadow = 0x2A76B147D6521C87ULL;
+	std::vector < bool > data;
+
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8 + offset; ++ i ) {
+		data.push_back ( true );
+	}
+
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		data.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+
+	data <<= sizeof ( unsigned long long ) * 2 + 1;
+
+	std::vector < bool > ref;
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 10 + offset; ++ i ) {
+		ref.push_back ( true );
+	}
+
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		ref.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+
+	ref.resize ( 128 + offset );
+
+	std::cout << "data = " << data << std::endl;
+	std::cout << "ref  = " << ref << std::endl;
+
+	CPPUNIT_ASSERT ( data == ref );
+}
+
+void VectorTest::testVectorBool4() {
+	unsigned long long shadow = 0x2A76B147D6521C87ULL;
+	std::vector < bool > data;
+
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		data.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+
+	shadow <<= 10;
+	data <<= 10;
+
+	std::vector < bool > ref;
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		ref.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+
+	std::cout << "data = " << data << std::endl;
+	std::cout << "ref  = " << ref << std::endl;
+
+	CPPUNIT_ASSERT ( data == ref );
+
+	for ( unsigned long i = - sizeof ( unsigned long long ) / 2 ; i < sizeof ( unsigned long long ) * 8; ++ i )
+		testOffset2 ( i );
+}
+
+void VectorTest::testVectorBool5() {
+	unsigned long long shadow = 0x2A76B147D6521C87ULL;
+	std::vector < bool > data;
+
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		data.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		data.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+	for ( unsigned i = 0; i < sizeof ( unsigned long long ) * 8; ++ i ) {
+		data.push_back ( ( bool ) ( shadow & ( 1ULL << i ) ) );
+	}
+
+	std::vector < bool > data2 = data;
+	data2 <<= 64;
+
+	data <<= 32;
+	data <<= 16;
+	data <<= 8;
+	data <<= 4;
+	data <<= 2;
+	data <<= 1;
+	data <<= 1;
+
+	std::cout << "data  = " << data  << std::endl;
+	std::cout << "data2 = " << data2 << std::endl;
+
+	data2 <<= 1;
+	data2 <<= 2;
+	data2 <<= 4;
+	data2 <<= 8;
+	data2 <<= 16;
+	data2 <<= 32;
+	data2 <<= 1;
+
+	data <<= 27;
+	data <<= 17;
+	data <<= 13;
+	data <<= 7;
+
+	std::cout << "data  = " << data  << std::endl;
+	std::cout << "data2 = " << data2 << std::endl;
+
+	CPPUNIT_ASSERT ( data2 == data );
+}
+
diff --git a/alib2std/test-src/extensions/VectorTest.h b/alib2std/test-src/extensions/VectorTest.h
index 4416d62c59..52d7684aa3 100644
--- a/alib2std/test-src/extensions/VectorTest.h
+++ b/alib2std/test-src/extensions/VectorTest.h
@@ -8,6 +8,11 @@ class VectorTest : public CppUnit::TestFixture
 {
   CPPUNIT_TEST_SUITE( VectorTest );
   CPPUNIT_TEST( testProperties );
+  CPPUNIT_TEST( testVectorBool );
+  CPPUNIT_TEST( testVectorBool2 );
+  CPPUNIT_TEST( testVectorBool3 );
+  CPPUNIT_TEST( testVectorBool4 );
+  CPPUNIT_TEST( testVectorBool5 );
   CPPUNIT_TEST_SUITE_END();
 
 public:
@@ -15,6 +20,11 @@ public:
   void tearDown();
 
   void testProperties();
+  void testVectorBool();
+  void testVectorBool2();
+  void testVectorBool3();
+  void testVectorBool4();
+  void testVectorBool5();
 };
 
 #endif  // VARIANT_TEST_H_
-- 
GitLab