From b27f7f3bb6078145918714a2b8843b743deb6bbd Mon Sep 17 00:00:00 2001 From: Matthias Hochsteger Date: Wed, 28 Aug 2019 14:10:09 +0200 Subject: [PATCH] Add BitArray from NGSolve Deprecate method BitArray::Set(), instead use either SetBit() or SetBitAtomic() --- libsrc/core/CMakeLists.txt | 3 +- libsrc/core/bitarray.cpp | 143 +++++++++++++++++++ libsrc/core/bitarray.hpp | 200 +++++++++++++++++++++++++++ libsrc/core/ngcore.hpp | 1 + libsrc/core/python_ngcore.hpp | 1 + libsrc/core/python_ngcore_export.cpp | 80 ++++++++++- tests/pytest/test_bitarray.py | 35 +++++ 7 files changed, 461 insertions(+), 2 deletions(-) create mode 100644 libsrc/core/bitarray.cpp create mode 100644 libsrc/core/bitarray.hpp create mode 100644 tests/pytest/test_bitarray.py diff --git a/libsrc/core/CMakeLists.txt b/libsrc/core/CMakeLists.txt index 710faa12..9f074709 100644 --- a/libsrc/core/CMakeLists.txt +++ b/libsrc/core/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(ngcore SHARED archive.cpp + bitarray.cpp localheap.cpp logging.cpp flags.cpp @@ -58,7 +59,7 @@ target_link_libraries(ngcore PUBLIC netgen_mpi PRIVATE ${CMAKE_THREAD_LIBS_INIT} install(FILES ngcore.hpp archive.hpp type_traits.hpp version.hpp ngcore_api.hpp logging.hpp exception.hpp symboltable.hpp paje_trace.hpp utils.hpp profiler.hpp mpi_wrapper.hpp array.hpp taskmanager.hpp concurrentqueue.h localheap.hpp python_ngcore.hpp flags.hpp - xbool.hpp signal.hpp + xbool.hpp signal.hpp bitarray.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/core COMPONENT netgen_devel) if(ENABLE_CPP_CORE_GUIDELINES_CHECK) diff --git a/libsrc/core/bitarray.cpp b/libsrc/core/bitarray.cpp new file mode 100644 index 00000000..daef541e --- /dev/null +++ b/libsrc/core/bitarray.cpp @@ -0,0 +1,143 @@ +/**************************************************************************/ +/* File: bitarray.cpp */ +/* Autho: Joachim Schoeberl */ +/* Date: 01. Jun. 95 */ +/**************************************************************************/ + +/* + data type BitArray +*/ + +#include "bitarray.hpp" + +namespace ngcore +{ + BitArray :: BitArray (size_t asize) + { + size = 0; + data = NULL; + SetSize (asize); + } + + BitArray :: BitArray (size_t asize, LocalHeap & lh) + { + size = asize; + data = new (lh) unsigned char [Addr (size)+1]; + owns_data = false; + } + + BitArray :: BitArray (const BitArray & ba2) + { + size = 0; + data = NULL; + (*this) = ba2; + } + + void BitArray :: SetSize (size_t asize) + { + if (size == asize) return; + if (owns_data) delete [] data; + + size = asize; + data = new unsigned char [Addr (size)+1]; + } + + BitArray & BitArray :: Set () throw() + { + if (!size) return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] = UCHAR_MAX; + return *this; + } + + BitArray & BitArray :: Clear () throw() + { + if (!size) return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] = 0; + return *this; + } + + BitArray & BitArray :: Invert () + { + if (!size) return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] ^= 255; + return *this; + } + + BitArray & BitArray :: And (const BitArray & ba2) + { + if (!size) return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] &= ba2.data[i]; + return *this; + } + + + BitArray & BitArray :: Or (const BitArray & ba2) + { + if (!size) return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] |= ba2.data[i]; + return *this; + } + + + BitArray & BitArray :: operator= (const BitArray & ba2) + { + SetSize (ba2.Size()); + if (!size) + return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] = ba2.data[i]; + return *this; + } + + std::ostream & operator<<(std::ostream & s, const BitArray & ba) + { + size_t n = ba.Size(); + for (size_t i = 0; i < n; i++) + { + if (i % 50 == 0) s << i << ": "; + s << int(ba[i]); + if (i % 50 == 49) s << "\n"; + } + s << std::flush; + return s; + } + + size_t BitArray :: NumSet () const + { + size_t cnt = 0; + for (size_t i = 0; i < Size(); i++) + if (Test(i)) cnt++; + return cnt; + } + + Archive & operator & (Archive & archive, BitArray & ba) + { + if (archive.Output()) + { + archive << ba.Size(); + for (size_t i = 0; i < ba.Size(); i++) + archive << ba[i]; + } + else + { + int size; + archive & size; + ba.SetSize (size); + ba.Clear(); + for (size_t i = 0; i < size; i++) + { + bool b; + archive & b; + if (b) ba.SetBit(i); + } + } + return archive; + } + + +} diff --git a/libsrc/core/bitarray.hpp b/libsrc/core/bitarray.hpp new file mode 100644 index 00000000..9c0823cf --- /dev/null +++ b/libsrc/core/bitarray.hpp @@ -0,0 +1,200 @@ +#ifndef NETGEN_CORE_BITARRAY +#define NETGEN_CORE_BITARRAY + +/**************************************************************************/ +/* File: bitarray.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 01. Jun. 95 */ +/**************************************************************************/ + +#include +#include +#include + +#include "archive.hpp" +#include "array.hpp" +#include "localheap.hpp" +#include "ngcore_api.hpp" +#include "utils.hpp" + +namespace ngcore +{ + +/** + A compressed array of bools. + + Provides bit-operations and whole array operations. +*/ +class BitArray +{ +protected: + /// number of bits + size_t size; + + /// the data + unsigned char * data; + /// + bool owns_data = true; +public: + /// empty array + BitArray () + : size(0), data(nullptr) { ; } + /// array of asize bits + NGCORE_API BitArray (size_t asize); + /// array of asize bits + NGCORE_API BitArray (size_t asize, LocalHeap & lh); + /// + NGCORE_API BitArray (const BitArray & ba2); + BitArray (BitArray && ba2) + : size(ba2.size), data(ba2.data), owns_data(ba2.owns_data) + { + ba2.owns_data = false; + ba2.data = nullptr; + } + + template + NETGEN_INLINE BitArray (std::initializer_list list) + : BitArray (list.size()) + { + Clear(); + int cnt = 0; + for (auto i = list.begin(); i < list.end(); i++, cnt++) + if (*i) SetBit(cnt); + } + + /// delete data + ~BitArray () + { + if (owns_data) + delete [] data; + } + + /// Set size, loose values + NGCORE_API void SetSize (size_t asize); + + /// the size + size_t Size () const { return size; } + + /// set all bits + NGCORE_API BitArray & Set () throw(); + + /// clear all bits + NGCORE_API BitArray & Clear () throw(); + + /// set bit i + [[deprecated("Use either SetBit() or SetBitAtomic()")]] + void Set (size_t i) { SetBitAtomic(i); } + + /// set bit i ( not thread safe ) + void SetBit (size_t i) + { + NETGEN_CHECK_RANGE(i, 0, size); + data[Addr(i)] |= Mask(i); + } + + /// set bit i ( thread safe ) + void SetBitAtomic (size_t i) + { + NETGEN_CHECK_RANGE(i, 0, size); + unsigned char * p = data+Addr(i); + unsigned char mask = Mask(i); + + AsAtomic(*p) |= mask; + } + + /// clear bit i + void Clear (size_t i) + { + NETGEN_CHECK_RANGE(i, 0, size); + data[Addr(i)] &= ~Mask(i); + } + + /// check bit i + bool Test (size_t i) const + { + NETGEN_CHECK_RANGE(i, 0, size); + return (data[Addr(i)] & Mask(i)) ? true : false; + } + + /// set all bits to b + BitArray & operator= (bool b) + { + if (b) Set(); + else Clear(); + return *this; + } + + /// check bit i + bool operator[] (size_t i) const + { + NETGEN_CHECK_RANGE(i, 0, size); + return Test(i); + } + + + /// invert all bits + NGCORE_API BitArray & Invert (); + + /// logical AND with ba2 + NGCORE_API BitArray & And (const BitArray & ba2); + + /// logical OR with ba2 + NGCORE_API BitArray & Or (const BitArray & ba2); + + /// copy from ba2 + NGCORE_API BitArray & operator= (const BitArray & ba2); + + NGCORE_API size_t NumSet () const; +private: + /// + unsigned char Mask (size_t i) const + { return char(1) << (i % CHAR_BIT); } + + /// + size_t Addr (size_t i) const + { return (i / CHAR_BIT); } + +}; + + + inline BitArray & operator|= (BitArray & me, const BitArray & you) + { + me.Or(you); + return me; + } + + inline BitArray & operator&= (BitArray & me, const BitArray & you) + { + me.And(you); + return me; + } + + inline BitArray operator| (const BitArray & a, const BitArray & b) + { + BitArray res = a; + res |= b; + return res; + } + + inline BitArray operator& (const BitArray & a, const BitArray & b) + { + BitArray res = a; + res &= b; + return res; + } + + inline BitArray operator~ (const BitArray & a) + { + BitArray res = a; + res.Invert(); + return res; + } + + + NGCORE_API std::ostream & operator<<(std::ostream & s, const BitArray & ba); + + NGCORE_API Archive & operator & (Archive & archive, BitArray & ba); + +} // namespace ngcore + +#endif // NETGEN_CORE_BITARRAY diff --git a/libsrc/core/ngcore.hpp b/libsrc/core/ngcore.hpp index a2cda645..9aa69a6f 100644 --- a/libsrc/core/ngcore.hpp +++ b/libsrc/core/ngcore.hpp @@ -3,6 +3,7 @@ #include "archive.hpp" #include "array.hpp" +#include "bitarray.hpp" #include "exception.hpp" #include "flags.hpp" #include "localheap.hpp" diff --git a/libsrc/core/python_ngcore.hpp b/libsrc/core/python_ngcore.hpp index 1a071dc2..69a97543 100644 --- a/libsrc/core/python_ngcore.hpp +++ b/libsrc/core/python_ngcore.hpp @@ -3,6 +3,7 @@ #include "ngcore_api.hpp" // for operator new #include +#include #include "array.hpp" #include "archive.hpp" diff --git a/libsrc/core/python_ngcore_export.cpp b/libsrc/core/python_ngcore_export.cpp index 2f409aa4..e86573ed 100644 --- a/libsrc/core/python_ngcore_export.cpp +++ b/libsrc/core/python_ngcore_export.cpp @@ -1,5 +1,6 @@ - #include "python_ngcore.hpp" +#include "bitarray.hpp" + using namespace ngcore; using namespace std; @@ -11,6 +12,83 @@ PYBIND11_MODULE(pyngcore, m) // NOLINT ExportArray(m); ExportArray(m); + py::class_> (m, "BitArray") + .def(py::init([] (size_t n) { return make_shared(n); }),py::arg("n")) + .def(py::init([] (const BitArray& a) { return make_shared(a); } ), py::arg("ba")) + .def(py::init([] (const vector & a) + { + auto ba = make_shared(a.size()); + ba->Clear(); + for (size_t i = 0; i < a.size(); i++) + if (a[i]) ba->SetBit(i); + return ba; + } ), py::arg("vec")) + .def("__str__", &ToString) + .def("__len__", &BitArray::Size) + .def("__getitem__", [] (BitArray & self, int i) + { + if (i < 0 || i >= self.Size()) + throw py::index_error(); + return self.Test(i); + }, py::arg("pos"), "Returns bit from given position") + .def("__setitem__", [] (BitArray & self, int i, bool b) + { + if (i < 0 || i >= self.Size()) + throw py::index_error(); + if (b) self.SetBit(i); else self.Clear(i); + }, py::arg("pos"), py::arg("value"), "Clear/Set bit at given position") + + .def("__setitem__", [] (BitArray & self, py::slice inds, bool b) + { + size_t start, step, stop, n; + if (!inds.compute(self.Size(), &start, &stop, &step, &n)) + throw py::error_already_set(); + + if (start == 0 && n == self.Size() && step == 1) + { // base branch + if (b) + self.Set(); + else + self.Clear(); + } + else + { + if (b) + for (size_t i=0; i(m, "Flags") .def(py::init<>()) .def("__str__", &ToString) diff --git a/tests/pytest/test_bitarray.py b/tests/pytest/test_bitarray.py new file mode 100644 index 00000000..ed3d6fd8 --- /dev/null +++ b/tests/pytest/test_bitarray.py @@ -0,0 +1,35 @@ +from pyngcore import BitArray + +def test_bitarray(): + a = BitArray(498) + assert len(a) == 498 + + a.Set() + for b in a: + assert b == True + + a.Clear(23) + assert a[22] == True + assert a[23] == False + assert a[24] == True + + a.Clear() + for b in a: + assert b == False + + a.Set(23) + assert a[22] == False + assert a[23] == True + assert a[24] == False + + a.Clear() + a[100:200:9] = True + for i in range(len(a)): + assert a[i] == bool(100<=i and i<200 and i%9==100%9) + + ac = ~a + + for b,bc in zip(a,ac): + assert b == (not bc) + +