From 100d861336ce04c49384d6fac80ebf102a6c8754 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 Aug 2024 18:20:55 +0200 Subject: [PATCH 01/15] WIP: tl::BitSet plus tests --- src/tl/tl/tl.pro | 6 + src/tl/tl/tlBitSet.cc | 199 ++++++++++++++++++++++++ src/tl/tl/tlBitSet.h | 240 +++++++++++++++++++++++++++++ src/tl/tl/tlBitSetMap.cc | 0 src/tl/tl/tlBitSetMap.h | 0 src/tl/tl/tlBitSetMatch.cc | 0 src/tl/tl/tlBitSetMatch.h | 0 src/tl/unit_tests/tlBitSetTests.cc | 208 +++++++++++++++++++++++++ src/tl/unit_tests/unit_tests.pro | 1 + 9 files changed, 654 insertions(+) create mode 100644 src/tl/tl/tlBitSet.cc create mode 100644 src/tl/tl/tlBitSet.h create mode 100644 src/tl/tl/tlBitSetMap.cc create mode 100644 src/tl/tl/tlBitSetMap.h create mode 100644 src/tl/tl/tlBitSetMatch.cc create mode 100644 src/tl/tl/tlBitSetMatch.h create mode 100644 src/tl/unit_tests/tlBitSetTests.cc diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index 7d1cf0c152..21bdf05b8a 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -11,6 +11,9 @@ FORMS = SOURCES = \ tlAssert.cc \ tlBase64.cc \ + tlBitSet.cc \ + tlBitSetMap.cc \ + tlBitSetMatch.cc \ tlColor.cc \ tlClassRegistry.cc \ tlCopyOnWrite.cc \ @@ -62,6 +65,9 @@ HEADERS = \ tlAlgorithm.h \ tlAssert.h \ tlBase64.h \ + tlBitSet.h \ + tlBitSetMap.h \ + tlBitSetMatch.h \ tlColor.h \ tlClassRegistry.h \ tlCopyOnWrite.h \ diff --git a/src/tl/tl/tlBitSet.cc b/src/tl/tl/tlBitSet.cc new file mode 100644 index 0000000000..669b5b749e --- /dev/null +++ b/src/tl/tl/tlBitSet.cc @@ -0,0 +1,199 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlCommon.h" + +#include "tlBitSet.h" + +namespace tl +{ + +static inline unsigned int nwords (BitSet::size_type size) +{ + return (size + (sizeof (BitSet::data_type) * 8 - 1)) / (sizeof (BitSet::data_type) * 8); +} + +static inline unsigned int word (BitSet::size_type index) +{ + return index / (sizeof (BitSet::data_type) * 8); +} + +static inline unsigned int bit (BitSet::size_type index) +{ + // first bit is the highest bit, so that comparing the uint's is good enough + // for lexical order. + return 31 - (index % (sizeof (BitSet::data_type) * 8)); +} + +BitSet & +BitSet::operator= (const BitSet &other) +{ + if (&other != this) { + + clear (); + + // reallocate + m_size = other.m_size; + unsigned int words = nwords (m_size); + mp_data = new data_type[words]; + data_type *t = mp_data; + data_type *s = other.mp_data; + for (unsigned int i = 0; i < words; ++i) { + *t++ = *s++; + } + + } + return *this; +} + +BitSet & +BitSet::operator= (BitSet &&other) +{ + if (&other != this) { + swap (other); + } + return *this; +} + +void +BitSet::clear () +{ + if (mp_data) { + delete [] mp_data; + } + mp_data = 0; + m_size = 0; +} + +void +BitSet::resize (size_type size) +{ + if (size > m_size) { + + unsigned int words = nwords (m_size); + unsigned int new_words = nwords (size); + + if (new_words > words) { + + // reallocate + data_type *new_data = new data_type[new_words]; + data_type *t = new_data; + data_type *s = mp_data; + unsigned int i; + for (i = 0; i < words; ++i) { + *t++ = *s++; + } + for (; i < new_words; ++i) { + *t++ = 0; + } + delete mp_data; + mp_data = new_data; + m_size = size; + + } + + } +} + +bool +BitSet::operator== (const BitSet &other) const +{ + unsigned int words = nwords (m_size); + unsigned int other_words = nwords (other.m_size); + + const data_type *p = mp_data; + const data_type *op = other.mp_data; + unsigned int i; + for (i = 0; i < words && i < other_words; ++i) { + if (*p++ != *op++) { + return false; + } + } + for (; i < words; ++i) { + if (*p++ != 0) { + return false; + } + } + for (; i < other_words; ++i) { + if (0 != *op++) { + return false; + } + } + return true; +} + +bool +BitSet::operator< (const BitSet &other) const +{ + unsigned int words = nwords (m_size); + unsigned int other_words = nwords (other.m_size); + + const data_type *p = mp_data; + const data_type *op = other.mp_data; + unsigned int i; + for (i = 0; i < words && i < other_words; ++i, ++p, ++op) { + if (*p != *op) { + return *p < *op; + } + } + for (; i < other_words; ++i, ++op) { + if (0 != *op) { + return true; + } + } + return false; +} + +void +BitSet::set (index_type index) +{ + unsigned int wi = word (index); + if (wi >= nwords (m_size)) { + resize (index + 1); + } else if (index >= m_size) { + m_size = index + 1; + } + mp_data [wi] |= (1 << bit (index)); +} + +void +BitSet::reset (index_type index) +{ + if (index < m_size) { + unsigned int wi = word (index); + mp_data [wi] &= ~(1 << bit (index)); + } +} + +bool +BitSet::operator[] (index_type index) const +{ + if (index < m_size) { + unsigned int wi = word (index); + return (mp_data [wi] & (1 << bit (index))) != 0; + } else { + return false; + } +} + +} diff --git a/src/tl/tl/tlBitSet.h b/src/tl/tl/tlBitSet.h new file mode 100644 index 0000000000..9fbcf4e7e5 --- /dev/null +++ b/src/tl/tl/tlBitSet.h @@ -0,0 +1,240 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_tlBitSet +#define HDR_tlBitSet + +#include "tlCommon.h" + +#include +#include + +namespace tl +{ + +/** + * @brief A bit set + * + * This object can store a set of n bits, each being true or false. + * Essentially is it like a vector, but optimized to cooperate + * with tl::BitSetMap and tl::BitSetMatch. + * + * Allocation is dynamic when a bit is accessed for write. Bits beyond the + * size are treated as false. + */ +class TL_PUBLIC BitSet +{ +public: + typedef unsigned int index_type; + typedef unsigned int size_type; + typedef uint32_t data_type; + + /** + * @brief Default constructor: creates an empty bit set + */ + BitSet () + : mp_data (0), m_size (0) + { + // .. nothing yet .. + } + + /** + * @brief Creates and initializes a bit set from a range of indexes + * Every bit given by an index from the range is set. + */ + template + BitSet (Iter from, Iter to) + : mp_data (0), m_size (0) + { + set (from, to); + } + + /** + * @brief Copy constructor + */ + BitSet (const BitSet &other) + : mp_data (0), m_size (0) + { + operator= (other); + } + + /** + * @brief Move constructor + */ + BitSet (BitSet &&other) + : mp_data (0), m_size (0) + { + operator= (std::move (other)); + } + + /** + * @brief Destructor + */ + ~BitSet () + { + clear (); + } + + /** + * @brief Assignment + */ + BitSet &operator= (const BitSet &other); + + /** + * @brief Move assignment + */ + BitSet &operator= (BitSet &&other); + + /** + * @brief Swaps the contents of this bit set with the other + */ + void swap (BitSet &other) + { + std::swap (mp_data, other.mp_data); + std::swap (m_size, other.m_size); + } + + /** + * @brief Clears this bit set + */ + void clear (); + + /** + * @brief Sizes the bit set to "size" bits + * + * New bits are set to false. + */ + void resize (size_type size); + + /** + * @brief Equality + */ + bool operator== (const BitSet &other) const; + + /** + * @brief Inequality + */ + bool operator!= (const BitSet &other) const + { + return !operator== (other); + } + + /** + * @brief Less operator + * + * The bits are compared in lexical order, first bit first. + */ + bool operator< (const BitSet &other) const; + + /** + * @brief Sets the given bit + */ + void set (index_type index); + + /** + * @brief Sets a range of bits + * The indexes are taken from the sequence delivered by the iterator. + */ + template + void set (Iter from, Iter to) + { + for (Iter i = from; i != to; ++i) { + set (*i); + } + } + + /** + * @brief Resets the given bit + */ + void reset (index_type index); + + /** + * @brief Resets a range of bits + * The indexes are taken from the sequence delivered by the iterator. + */ + template + void reset (Iter from, Iter to) + { + for (Iter i = from; i != to; ++i) { + reset (*i); + } + } + + /** + * @brief Sets the values for a given bit + */ + void set_value (index_type index, bool f) + { + if (f) { + set (index); + } else { + reset (index); + } + } + + /** + * @brief Sets the values for a range of bits + * The indexes are taken from the sequence delivered by the iterator. + */ + template + void set_value (Iter from, Iter to, bool f) + { + for (Iter i = from; i != to; ++i) { + set_value (*i, f); + } + } + + /** + * @brief Gets a bit from the given index + */ + bool operator[] (index_type index) const; + + /** + * @brief Gets a value indicating whether the set is empty + * + * "empty" means, no bits have been written yet. "empty" does NOT mean + * all bits are zero. + */ + bool is_empty () const + { + return m_size == 0; + } + + /** + * @brief Gets the number of bits stored + * + * The number of bits is the highest bit written so far. + */ + size_type size () const + { + return m_size; + } + +private: + data_type *mp_data; + size_type m_size; +}; + +} + +#endif diff --git a/src/tl/tl/tlBitSetMap.cc b/src/tl/tl/tlBitSetMap.cc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/tl/tl/tlBitSetMap.h b/src/tl/tl/tlBitSetMap.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/tl/tl/tlBitSetMatch.cc b/src/tl/tl/tlBitSetMatch.cc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/tl/tl/tlBitSetMatch.h b/src/tl/tl/tlBitSetMatch.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/tl/unit_tests/tlBitSetTests.cc b/src/tl/unit_tests/tlBitSetTests.cc new file mode 100644 index 0000000000..39587ae776 --- /dev/null +++ b/src/tl/unit_tests/tlBitSetTests.cc @@ -0,0 +1,208 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlBitSet.h" +#include "tlUnitTest.h" +#include "tlString.h" + +namespace +{ + +static std::string l2s (const tl::BitSet &s) +{ + std::string x; + for (tl::BitSet::index_type i = 0; i < s.size (); ++i) { + x += s[i] ? "1" : "0"; + } + return x; +} + +TEST(1_Basic) +{ + tl::BitSet bs; + EXPECT_EQ (bs.is_empty (), true); + EXPECT_EQ (bs.size (), 0u); + EXPECT_EQ (l2s (bs), ""); + + bs.set (1); + EXPECT_EQ (bs.size (), 2u); + EXPECT_EQ (l2s (bs), "01"); + + bs.set (32); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "010000000000000000000000000000001"); + + bs.set (3); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "010100000000000000000000000000001"); + + unsigned int indexes[] = { 5, 6, 7 }; + bs.set (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0])); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "010101110000000000000000000000001"); + + bs.reset (128); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "010101110000000000000000000000001"); + + bs.reset (1); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "000101110000000000000000000000001"); + + bs.reset (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0])); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "000100000000000000000000000000001"); + + bs.set_value (0, true); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "100100000000000000000000000000001"); + + bs.set_value (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]), true); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "100101110000000000000000000000001"); + + bs.set_value (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]), false); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "100100000000000000000000000000001"); + + bs.set_value (0, false); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "000100000000000000000000000000001"); + + bs.clear (); + EXPECT_EQ (bs.size (), 0u); + EXPECT_EQ (l2s (bs), ""); + + bs.resize (6); + EXPECT_EQ (bs.size (), 6u); + EXPECT_EQ (l2s (bs), "000000"); +} + +TEST(2_Equality) +{ + tl::BitSet bs1, bs2, bs3; + + EXPECT_EQ (bs1 == bs2, true); + EXPECT_EQ (bs1 != bs2, false); + + bs1.set (0); + EXPECT_EQ (bs1 == bs2, false); + EXPECT_EQ (bs1 != bs2, true); + + bs1.set (32); + EXPECT_EQ (bs1 == bs2, false); + EXPECT_EQ (bs1 != bs2, true); + + bs2.set (0); + bs2.set (32); + EXPECT_EQ (bs1 == bs2, true); + EXPECT_EQ (bs1 == bs3, false); + EXPECT_EQ (bs1 != bs2, false); + EXPECT_EQ (bs1 != bs3, true); + + bs1.reset (0); + bs1.reset (32); + EXPECT_EQ (bs1 == bs2, false); + EXPECT_EQ (bs1 == bs3, true); + EXPECT_EQ (bs1 != bs2, true); + EXPECT_EQ (bs1 != bs3, false); +} + +TEST(3_Compare) +{ + tl::BitSet bs1, bs2, bs3; + + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, false); + + bs1.set (0); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, true); + + bs1.set (32); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, true); + + bs2.set (0); + bs2.set (32); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs1 < bs3, false); + EXPECT_EQ (bs2 < bs1, false); + EXPECT_EQ (bs3 < bs1, true); + + bs1.reset (0); + bs1.reset (32); + EXPECT_EQ (bs1 < bs2, true); + EXPECT_EQ (bs1 < bs3, false); + EXPECT_EQ (bs2 < bs1, false); + EXPECT_EQ (bs3 < bs1, false); +} + +TEST(4_Assign) +{ + tl::BitSet bs; + EXPECT_EQ (l2s (bs), ""); + EXPECT_EQ (l2s (tl::BitSet (bs)), ""); + + bs.set (3); + bs.set (32); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "000100000000000000000000000000001"); + EXPECT_EQ (tl::BitSet (bs).size (), 33u); + EXPECT_EQ (l2s (tl::BitSet (bs)), "000100000000000000000000000000001"); + + tl::BitSet bs2; + bs2.swap (bs); + EXPECT_EQ (bs.size (), 0u); + EXPECT_EQ (bs2.size (), 33u); + EXPECT_EQ (l2s (bs), ""); + EXPECT_EQ (l2s (bs2), "000100000000000000000000000000001"); + + bs = bs2; + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "000100000000000000000000000000001"); + + bs2.clear (); + EXPECT_EQ (bs2.size (), 0u); + EXPECT_EQ (l2s (bs2), ""); + + bs2 = std::move (bs); + EXPECT_EQ (bs.size (), 0u); + EXPECT_EQ (l2s (bs), ""); + EXPECT_EQ (bs2.size (), 33u); + EXPECT_EQ (l2s (bs2), "000100000000000000000000000000001"); + + tl::BitSet bs3 (std::move (bs2)); + EXPECT_EQ (bs2.size (), 0u); + EXPECT_EQ (l2s (bs2), ""); + EXPECT_EQ (bs3.size (), 33u); + EXPECT_EQ (l2s (bs3), "000100000000000000000000000000001"); + + bs.clear (); + + unsigned int indexes[] = { 5, 6, 7 }; + bs = std::move (tl::BitSet (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]))); + EXPECT_EQ (bs.size (), 8u); + EXPECT_EQ (l2s (bs), "00000111"); +} + +} diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index 3d8b066765..b6f3e0f4f1 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ tlAlgorithmTests.cc \ tlBase64Tests.cc \ + tlBitSetTests.cc \ tlClassRegistryTests.cc \ tlCommandLineParserTests.cc \ tlColorTests.cc \ From 7219742b87347a56dad49867cecd5b24857782a4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 Aug 2024 20:26:48 +0200 Subject: [PATCH 02/15] WIP: tl::BitSetMask --- src/tl/tl/tl.pro | 4 +- src/tl/tl/tlBitSet.h | 2 + src/tl/tl/tlBitSetMask.cc | 291 +++++++++++++++++++++++++ src/tl/tl/tlBitSetMask.h | 189 ++++++++++++++++ src/tl/tl/tlBitSetMatch.cc | 0 src/tl/tl/tlBitSetMatch.h | 0 src/tl/unit_tests/tlBitSetMaskTests.cc | 264 ++++++++++++++++++++++ src/tl/unit_tests/unit_tests.pro | 1 + 8 files changed, 749 insertions(+), 2 deletions(-) create mode 100644 src/tl/tl/tlBitSetMask.cc create mode 100644 src/tl/tl/tlBitSetMask.h delete mode 100644 src/tl/tl/tlBitSetMatch.cc delete mode 100644 src/tl/tl/tlBitSetMatch.h create mode 100644 src/tl/unit_tests/tlBitSetMaskTests.cc diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index 21bdf05b8a..c7d4207ed7 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -13,7 +13,7 @@ SOURCES = \ tlBase64.cc \ tlBitSet.cc \ tlBitSetMap.cc \ - tlBitSetMatch.cc \ + tlBitSetMask.cc \ tlColor.cc \ tlClassRegistry.cc \ tlCopyOnWrite.cc \ @@ -67,7 +67,7 @@ HEADERS = \ tlBase64.h \ tlBitSet.h \ tlBitSetMap.h \ - tlBitSetMatch.h \ + tlBitSetMask.h \ tlColor.h \ tlClassRegistry.h \ tlCopyOnWrite.h \ diff --git a/src/tl/tl/tlBitSet.h b/src/tl/tl/tlBitSet.h index 9fbcf4e7e5..3a1f5034ae 100644 --- a/src/tl/tl/tlBitSet.h +++ b/src/tl/tl/tlBitSet.h @@ -231,6 +231,8 @@ class TL_PUBLIC BitSet } private: + friend class BitSetMask; + data_type *mp_data; size_type m_size; }; diff --git a/src/tl/tl/tlBitSetMask.cc b/src/tl/tl/tlBitSetMask.cc new file mode 100644 index 0000000000..741e1c4833 --- /dev/null +++ b/src/tl/tl/tlBitSetMask.cc @@ -0,0 +1,291 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlCommon.h" + +#include "tlBitSetMask.h" +#include "tlBitSet.h" + +namespace tl +{ + +static inline unsigned int nwords (BitSetMask::size_type size) +{ + return (size + (sizeof (BitSetMask::data_type) * 8 - 1)) / (sizeof (BitSetMask::data_type) * 8); +} + +static inline unsigned int word (BitSetMask::size_type index) +{ + return index / (sizeof (BitSetMask::data_type) * 8); +} + +static inline unsigned int bit (BitSetMask::size_type index) +{ + // first bit is the highest bit, so that comparing the uint's is good enough + // for lexical order. + return 31 - (index % (sizeof (BitSetMask::data_type) * 8)); +} + +BitSetMask & +BitSetMask::operator= (const BitSetMask &other) +{ + if (&other != this) { + + clear (); + + // reallocate + m_size = other.m_size; + unsigned int words = nwords (m_size); + mp_data0 = new data_type[words]; + mp_data1 = new data_type[words]; + data_type *t0 = mp_data0; + data_type *s0 = other.mp_data0; + data_type *t1 = mp_data1; + data_type *s1 = other.mp_data1; + for (unsigned int i = 0; i < words; ++i) { + *t0++ = *s0++; + *t1++ = *s1++; + } + + } + return *this; +} + +BitSetMask & +BitSetMask::operator= (BitSetMask &&other) +{ + if (&other != this) { + swap (other); + } + return *this; +} + +void +BitSetMask::clear () +{ + if (mp_data0) { + delete [] mp_data0; + } + mp_data0 = 0; + if (mp_data1) { + delete [] mp_data1; + } + mp_data1 = 0; + m_size = 0; +} + +void +BitSetMask::resize (size_type size) +{ + if (size > m_size) { + + unsigned int words = nwords (m_size); + unsigned int new_words = nwords (size); + + if (new_words > words) { + + // reallocate + data_type *new_data0 = new data_type[new_words]; + data_type *new_data1 = new data_type[new_words]; + data_type *t0 = new_data0; + data_type *s0 = mp_data0; + data_type *t1 = new_data1; + data_type *s1 = mp_data1; + unsigned int i; + for (i = 0; i < words; ++i) { + *t0++ = *s0++; + *t1++ = *s1++; + } + for (; i < new_words; ++i) { + // corresponds to "Any" + *t0++ = 0; + *t1++ = 0; + } + delete mp_data0; + mp_data0 = new_data0; + delete mp_data1; + mp_data1 = new_data1; + m_size = size; + + } + + } +} + +bool +BitSetMask::operator== (const BitSetMask &other) const +{ + unsigned int words = nwords (m_size); + unsigned int other_words = nwords (other.m_size); + + const data_type *p0 = mp_data0; + const data_type *p1 = mp_data1; + const data_type *op0 = other.mp_data0; + const data_type *op1 = other.mp_data1; + unsigned int i; + for (i = 0; i < words && i < other_words; ++i) { + if (*p0++ != *op0++) { + return false; + } + if (*p1++ != *op1++) { + return false; + } + } + for (; i < words; ++i) { + if (*p0++ != 0) { + return false; + } + if (*p1++ != 0) { + return false; + } + } + for (; i < other_words; ++i) { + if (0 != *op0++) { + return false; + } + if (0 != *op1++) { + return false; + } + } + return true; +} + +/** + * @brief Gets the most significant bit of a bit set + * + * For example b:00101101 will give b:00100000. + */ +static inline BitSetMask::data_type msb_only (BitSetMask::data_type value) +{ + const unsigned int smax = sizeof (BitSetMask::data_type) * 8; + + BitSetMask::data_type m = value; + for (unsigned int s = 1; s < smax; s *= 2) { + m |= (m >> s); + } + return value & ~(m >> 1); +} + +bool +BitSetMask::operator< (const BitSetMask &other) const +{ + unsigned int words = nwords (m_size); + unsigned int other_words = nwords (other.m_size); + + const data_type *p0 = mp_data0; + const data_type *p1 = mp_data1; + const data_type *op0 = other.mp_data0; + const data_type *op1 = other.mp_data1; + + unsigned int i; + for (i = 0; i < words && i < other_words; ++i, ++p0, ++p1, ++op0, ++op1) { + data_type diff = (*p0 ^ *op0) | (*p1 ^ *op1); + if (diff) { + // compare the most significant position of the differences by value + data_type mb = msb_only (diff); + unsigned int m = ((*p0 & mb) != 0 ? 1 : 0) + ((*p1 & mb) != 0 ? 2 : 0); + unsigned int om = ((*op0 & mb) != 0 ? 1 : 0) + ((*op1 & mb) != 0 ? 2 : 0); + return m < om; + } + } + + // the remaining part of other is simply checked for + // not being zero + for (; i < other_words; ++i, ++op0, ++op1) { + if (0 != *op0 || 0 != *op1) { + return true; + } + } + + return false; + +} + +void +BitSetMask::set (index_type index, mask_type mask) +{ + if (index >= m_size && mask == Any) { + return; + } + + unsigned int wi = word (index); + if (wi >= nwords (m_size)) { + resize (index + 1); + } else if (index >= m_size) { + m_size = index + 1; + } + + unsigned int mi = (unsigned int) mask; + data_type bm = (1 << bit (index)); + if (mi & 1) { + mp_data0 [wi] |= bm; + } else { + mp_data0 [wi] &= ~bm; + } + if (mi & 2) { + mp_data1 [wi] |= bm; + } else { + mp_data1 [wi] &= ~bm; + } +} + +BitSetMask::mask_type +BitSetMask::operator[] (index_type index) const +{ + if (index < m_size) { + unsigned int wi = word (index); + data_type bm = (1 << bit (index)); + unsigned int mi = ((mp_data0 [wi] & bm) != 0 ? 1 : 0) | ((mp_data1 [wi] & bm) != 0 ? 2 : 0); + return mask_type (mi); + } else { + return Any; + } +} + +bool +BitSetMask::match (const tl::BitSet &bs) const +{ + unsigned nw_bs = nwords (bs.m_size); + unsigned nw = nwords (m_size); + + const tl::BitSet::data_type *d0 = mp_data0, *d1 = mp_data1; + const tl::BitSet::data_type *s = bs.mp_data; + + unsigned int i; + for (i = 0; i < nw_bs && i < nw; ++i, ++d0, ++d1, ++s) { + tl::BitSet::data_type d = *s; + // A "true" in place of "false expected" gives "no match" + if ((*d0 & d) != 0) { + return false; + } + // A "false" in place of "true expected" gives "no match" + if ((*d1 & ~d) != 0) { + return false; + } + } + + // as "not set" corresponds to "Any", we can stop here and have a match. + return true; +} + +} diff --git a/src/tl/tl/tlBitSetMask.h b/src/tl/tl/tlBitSetMask.h new file mode 100644 index 0000000000..27541a6db8 --- /dev/null +++ b/src/tl/tl/tlBitSetMask.h @@ -0,0 +1,189 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_tlBitSetMask +#define HDR_tlBitSetMask + +#include "tlCommon.h" +#include "tlBitSet.h" + +#include +#include + +namespace tl +{ + +/** + * @brief A bit set + * + * This object can store a mask for a bit set. + * Each element of the mask corresponds to one bit. Each element can be "True" + * (matching to true), "False" (matching to false), "Any" matching to true + * or false and "Never" matches neither to true nor false. + * + * A bit set match can be matched against a bit set and will return true if + * the bit set corresponds to the mask. + * + * Allocation is dynamic when a mask element is accessed for write. Bits beyond the + * size are treated as "Any". + */ +class TL_PUBLIC BitSetMask +{ +public: + typedef enum { Any = 0, False = 1, True = 2, Never = 3 } mask_type; + + typedef tl::BitSet::index_type index_type; + typedef tl::BitSet::size_type size_type; + typedef tl::BitSet::data_type data_type; + + /** + * @brief Default constructor: creates an empty bit set + */ + BitSetMask () + : mp_data0 (0), mp_data1 (0), m_size (0) + { + // .. nothing yet .. + } + + /** + * @brief Copy constructor + */ + BitSetMask (const BitSetMask &other) + : mp_data0 (0), mp_data1 (0), m_size (0) + { + operator= (other); + } + + /** + * @brief Move constructor + */ + BitSetMask (BitSetMask &&other) + : mp_data0 (0), mp_data1 (0), m_size (0) + { + operator= (std::move (other)); + } + + /** + * @brief Destructor + */ + ~BitSetMask () + { + clear (); + } + + /** + * @brief Assignment + */ + BitSetMask &operator= (const BitSetMask &other); + + /** + * @brief Move assignment + */ + BitSetMask &operator= (BitSetMask &&other); + + /** + * @brief Swaps the contents of this bit set with the other + */ + void swap (BitSetMask &other) + { + std::swap (mp_data0, other.mp_data0); + std::swap (mp_data1, other.mp_data1); + std::swap (m_size, other.m_size); + } + + /** + * @brief Clears this bit set + */ + void clear (); + + /** + * @brief Sizes the bit set to "size" bits + * + * New bits are set to false. + */ + void resize (size_type size); + + /** + * @brief Equality + */ + bool operator== (const BitSetMask &other) const; + + /** + * @brief Inequality + */ + bool operator!= (const BitSetMask &other) const + { + return !operator== (other); + } + + /** + * @brief Less operator + * + * The bits are compared in lexical order, first bit first. + */ + bool operator< (const BitSetMask &other) const; + + /** + * @brief Sets the mask for the given bit + */ + void set (index_type index, mask_type mask); + + /** + * @brief Gets a mask from the given bit + */ + mask_type operator[] (index_type index) const; + + /** + * @brief Gets a value indicating whether the set is empty + * + * "empty" means, no bits have been written yet. "empty" does NOT mean + * all masks are of some specific value. + */ + bool is_empty () const + { + return m_size == 0; + } + + /** + * @brief Gets the number of bits for the mask stored + * + * The number of bits is the highest bit written so far. + */ + size_type size () const + { + return m_size; + } + + /** + * @brief Matches the given bit set against this mask + */ + bool match (const tl::BitSet &) const; + +private: + data_type *mp_data0, *mp_data1; + size_type m_size; +}; + +} + +#endif diff --git a/src/tl/tl/tlBitSetMatch.cc b/src/tl/tl/tlBitSetMatch.cc deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/tl/tl/tlBitSetMatch.h b/src/tl/tl/tlBitSetMatch.h deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/tl/unit_tests/tlBitSetMaskTests.cc b/src/tl/unit_tests/tlBitSetMaskTests.cc new file mode 100644 index 0000000000..7010a95aba --- /dev/null +++ b/src/tl/unit_tests/tlBitSetMaskTests.cc @@ -0,0 +1,264 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlBitSetMask.h" +#include "tlUnitTest.h" +#include "tlString.h" + +namespace +{ + +static std::string l2s (const tl::BitSetMask &s) +{ + std::string x; + for (tl::BitSetMask::index_type i = 0; i < s.size (); ++i) { + switch (s[i]) { + case tl::BitSetMask::Any: + x += "X"; + break; + case tl::BitSetMask::True: + x += "1"; + break; + case tl::BitSetMask::False: + x += "0"; + break; + default: + x += "-"; + break; + } + } + return x; +} + +TEST(1_Basic) +{ + tl::BitSetMask bs; + EXPECT_EQ (bs.is_empty (), true); + EXPECT_EQ (bs.size (), 0u); + EXPECT_EQ (l2s (bs), ""); + + bs.set (1, tl::BitSetMask::True); + EXPECT_EQ (bs.size (), 2u); + EXPECT_EQ (l2s (bs), "X1"); + + bs.set (32, tl::BitSetMask::False); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "X1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + + bs.set (3, tl::BitSetMask::False); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + + bs.set (128, tl::BitSetMask::Any); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + + bs.clear (); + EXPECT_EQ (bs.size (), 0u); + EXPECT_EQ (l2s (bs), ""); + + bs.resize (6); + EXPECT_EQ (bs.size (), 6u); + EXPECT_EQ (l2s (bs), "XXXXXX"); +} + +TEST(2_Equality) +{ + tl::BitSetMask bs1, bs2, bs3; + + EXPECT_EQ (bs1 == bs2, true); + EXPECT_EQ (bs1 != bs2, false); + + bs1.set (0, tl::BitSetMask::True); + EXPECT_EQ (bs1 == bs2, false); + EXPECT_EQ (bs1 != bs2, true); + + bs1.set (32, tl::BitSetMask::False); + EXPECT_EQ (bs1 == bs2, false); + EXPECT_EQ (bs1 != bs2, true); + + bs2.set (0, tl::BitSetMask::True); + bs2.set (32, tl::BitSetMask::False); + EXPECT_EQ (bs1 == bs2, true); + EXPECT_EQ (bs1 == bs3, false); + EXPECT_EQ (bs1 != bs2, false); + EXPECT_EQ (bs1 != bs3, true); + + bs1.set (0, tl::BitSetMask::Any); + bs1.set (32, tl::BitSetMask::Any); + EXPECT_EQ (bs1 == bs2, false); + EXPECT_EQ (bs1 == bs3, true); + EXPECT_EQ (bs1 != bs2, true); + EXPECT_EQ (bs1 != bs3, false); +} + +TEST(3_Compare) +{ + tl::BitSetMask bs1, bs2, bs3; + + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, false); + + bs1.set (0, tl::BitSetMask::True); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, true); + + bs1.set (32, tl::BitSetMask::False); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, true); + + bs2.set (32, tl::BitSetMask::False); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs1 < bs3, false); + EXPECT_EQ (bs2 < bs1, true); + EXPECT_EQ (bs3 < bs1, true); + + bs2.set (0, tl::BitSetMask::True); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs1 < bs3, false); + EXPECT_EQ (bs2 < bs1, false); + EXPECT_EQ (bs3 < bs1, true); + + bs1.set (0, tl::BitSetMask::Any); + bs1.set (32, tl::BitSetMask::Any); + EXPECT_EQ (bs1 < bs2, true); + EXPECT_EQ (bs1 < bs3, false); + EXPECT_EQ (bs2 < bs1, false); + EXPECT_EQ (bs3 < bs1, false); + + bs1.clear (); + bs2.clear (); + bs1.set (0, tl::BitSetMask::Any); + + bs2.set (0, tl::BitSetMask::Any); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, false); + + bs2.set (0, tl::BitSetMask::False); + EXPECT_EQ (bs1 < bs2, true); + EXPECT_EQ (bs2 < bs1, false); + + bs2.set (0, tl::BitSetMask::True); + EXPECT_EQ (bs1 < bs2, true); + EXPECT_EQ (bs2 < bs1, false); + + bs2.set (0, tl::BitSetMask::Never); + EXPECT_EQ (bs1 < bs2, true); + EXPECT_EQ (bs2 < bs1, false); + + bs1.set (0, tl::BitSetMask::False); + + bs2.set (0, tl::BitSetMask::Any); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, true); + + bs2.set (0, tl::BitSetMask::False); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, false); + + bs2.set (0, tl::BitSetMask::True); + EXPECT_EQ (bs1 < bs2, true); + EXPECT_EQ (bs2 < bs1, false); + + bs2.set (0, tl::BitSetMask::Never); + EXPECT_EQ (bs1 < bs2, true); + EXPECT_EQ (bs2 < bs1, false); + + bs1.set (0, tl::BitSetMask::True); + + bs2.set (0, tl::BitSetMask::Any); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, true); + + bs2.set (0, tl::BitSetMask::False); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, true); + + bs2.set (0, tl::BitSetMask::True); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, false); + + bs2.set (0, tl::BitSetMask::Never); + EXPECT_EQ (bs1 < bs2, true); + EXPECT_EQ (bs2 < bs1, false); + + bs1.set (0, tl::BitSetMask::Never); + + bs2.set (0, tl::BitSetMask::Any); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, true); + + bs2.set (0, tl::BitSetMask::False); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, true); + + bs2.set (0, tl::BitSetMask::True); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, true); + + bs2.set (0, tl::BitSetMask::Never); + EXPECT_EQ (bs1 < bs2, false); + EXPECT_EQ (bs2 < bs1, false); +} + +TEST(4_Assign) +{ + tl::BitSetMask bs; + EXPECT_EQ (l2s (bs), ""); + EXPECT_EQ (l2s (tl::BitSetMask (bs)), ""); + + bs.set (3, tl::BitSetMask::True); + bs.set (32, tl::BitSetMask::False); + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + EXPECT_EQ (tl::BitSetMask (bs).size (), 33u); + EXPECT_EQ (l2s (tl::BitSetMask (bs)), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + + tl::BitSetMask bs2; + bs2.swap (bs); + EXPECT_EQ (bs.size (), 0u); + EXPECT_EQ (bs2.size (), 33u); + EXPECT_EQ (l2s (bs), ""); + EXPECT_EQ (l2s (bs2), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + + bs = bs2; + EXPECT_EQ (bs.size (), 33u); + EXPECT_EQ (l2s (bs), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + + bs2.clear (); + EXPECT_EQ (bs2.size (), 0u); + EXPECT_EQ (l2s (bs2), ""); + + bs2 = std::move (bs); + EXPECT_EQ (bs.size (), 0u); + EXPECT_EQ (l2s (bs), ""); + EXPECT_EQ (bs2.size (), 33u); + EXPECT_EQ (l2s (bs2), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + + tl::BitSetMask bs3 (std::move (bs2)); + EXPECT_EQ (bs2.size (), 0u); + EXPECT_EQ (l2s (bs2), ""); + EXPECT_EQ (bs3.size (), 33u); + EXPECT_EQ (l2s (bs3), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); +} + +} diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index b6f3e0f4f1..a65a773e7f 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ tlAlgorithmTests.cc \ tlBase64Tests.cc \ + tlBitSetMaskTests.cc \ tlBitSetTests.cc \ tlClassRegistryTests.cc \ tlCommandLineParserTests.cc \ From e7f8290edbc7725947a4579e1e2434c3d78b011c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 Aug 2024 22:33:28 +0200 Subject: [PATCH 03/15] WIP: debugging, map implementation --- src/tl/tl/tlBitSet.h | 13 +- src/tl/tl/tlBitSetMap.cc | 31 +++ src/tl/tl/tlBitSetMap.h | 338 +++++++++++++++++++++++++ src/tl/tl/tlBitSetMask.cc | 27 +- src/tl/tl/tlBitSetMask.h | 13 +- src/tl/unit_tests/tlBitSetMaskTests.cc | 59 +++++ 6 files changed, 473 insertions(+), 8 deletions(-) diff --git a/src/tl/tl/tlBitSet.h b/src/tl/tl/tlBitSet.h index 3a1f5034ae..b39c7d3e3f 100644 --- a/src/tl/tl/tlBitSet.h +++ b/src/tl/tl/tlBitSet.h @@ -40,7 +40,7 @@ namespace tl * with tl::BitSetMap and tl::BitSetMatch. * * Allocation is dynamic when a bit is accessed for write. Bits beyond the - * size are treated as false. + * allocated size are treated as "false" or zero. */ class TL_PUBLIC BitSet { @@ -239,4 +239,15 @@ class TL_PUBLIC BitSet } +namespace std +{ + +inline void +swap (tl::BitSet &a, tl::BitSet &b) +{ + a.swap (b); +} + +} + #endif diff --git a/src/tl/tl/tlBitSetMap.cc b/src/tl/tl/tlBitSetMap.cc index e69de29bb2..8c759eb93e 100644 --- a/src/tl/tl/tlBitSetMap.cc +++ b/src/tl/tl/tlBitSetMap.cc @@ -0,0 +1,31 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlCommon.h" + +#include "tlBitSetMap.h" + +namespace tl +{ + // .. nothing yet .. +} diff --git a/src/tl/tl/tlBitSetMap.h b/src/tl/tl/tlBitSetMap.h index e69de29bb2..db1ddc47e1 100644 --- a/src/tl/tl/tlBitSetMap.h +++ b/src/tl/tl/tlBitSetMap.h @@ -0,0 +1,338 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_tlBitSetMap +#define HDR_tlBitSetMap + +#include "tlCommon.h" +#include "tlBitSetMask.h" +#include "tlBitSet.h" +#include "tlAssert.h" + +#include + +namespace tl +{ + +template +struct TL_PUBLIC bit_set_mask_node +{ + bit_set_mask_node () + : mask (), next (0), value () + { + } + + tl::BitSetMask mask; + size_t next; + Value value; + + void swap (bit_set_mask_node &other) + { + std::swap (mask, other.mask); + std::swap (next, other.next); + std::swap (value, other.value); + } +}; + +template +class TL_PUBLIC bit_set_mask_compare +{ +public: + bit_set_mask_compare (tl::BitSetMask::index_type bit, tl::BitSetMask::mask_type mask) + : m_bit (bit), m_mask (mask) + { + } + + bool operator() (const bit_set_mask_node &node) const + { + return node.mask [m_bit] < m_mask; + } + +private: + tl::BitSetMask::index_type m_bit; + tl::BitSetMask::mask_type m_mask; +}; + +} + +namespace std +{ + +template +inline void +swap (tl::bit_set_mask_node &a, tl::bit_set_mask_node &b) +{ + a.swap (b); +} + +} + +namespace tl +{ + +/** + * @brief A bit set map + * + * This specialized map stores tl::BitSetMask keys and corresponding values. + * tl::BitSet objects can be used to retrieve values. Masks may overlap, hence + * multiple matches are possible. The "lookup" method employs a visitor + * pattern to deliver these multiple matches. + * + * In order to use the map, it first has to be sorted. Insert masks using + * "insert" and do a "sort" before using "lookup". + */ +template +class TL_PUBLIC bit_set_map +{ +public: + typedef std::vector > node_list; + typedef typename node_list::const_iterator const_iterator; + typedef typename node_list::iterator iterator; + + /** + * @brief Default constructor: creates an empty bit set + */ + bit_set_map () + { + // .. nothing yet .. + } + + /** + * @brief Copy constructor + */ + bit_set_map (const bit_set_map &other) + { + operator= (other); + } + + /** + * @brief Move constructor + */ + bit_set_map (bit_set_map &&other) + { + operator= (std::move (other)); + } + + /** + * @brief Assignment + */ + bit_set_map &operator= (const bit_set_map &other) + { + if (this != &other) { + m_nodes = other.m_nodes; + m_sorted = other.m_sorted; + } + return *this; + } + + /** + * @brief Move assignment + */ + bit_set_map &operator= (bit_set_map &&other) + { + if (this != &other) { + swap (other); + } + return *this; + } + + /** + * @brief Swaps the contents of this bit set with the other + */ + void swap (bit_set_map &other) + { + if (this != &other) { + m_nodes.swap (other.m_nodes); + std::swap (m_sorted, other.m_sorted); + } + } + + /** + * @brief Clears this map + */ + void clear () + { + m_nodes.clear (); + m_sorted = true; + } + + /** + * @brief Reserves "size" entries + * + * Use this method to specify the intended size of the map. + * This optimizes performance and memory allocation. + */ + void reserve (size_t n) + { + m_nodes.reserve (n); + } + + /** + * @brief Inserts an item into the map + */ + void insert (const tl::BitSetMask &mask, const Value &value) + { + m_nodes.push_back (tl::bit_set_mask_node ()); + m_nodes.back ().mask = mask; + m_nodes.back ().next = 0; + m_nodes.back ().value = value; + + m_sorted = false; + } + + /** + * @brief Sorts the map + * + * "sort" needs to be called before "lookup" can be used. + */ + void sort () + { + if (! m_sorted) { + sort_range (0, m_nodes.begin (), m_nodes.end ()); + m_sorted = true; + } + } + + /** + * @brief Gets a value indicating whether the set is empty + * + * "empty" means, no bits have been written yet. "empty" does NOT mean + * all masks are of some specific value. + */ + bool is_empty () const + { + return m_nodes.empty (); + } + + /** + * @brief Gets the number of bits for the mask stored + * + * The number of bits is the highest bit written so far. + */ + size_t size () const + { + return m_nodes.size (); + } + + /** + * @brief Looks up items by bit set + * + * For each item found, the value is delivered through the + * Inserter provided. + * + * The return value is true, if any value has been found. + */ + template + bool lookup (const tl::BitSet &bit_set, Inserter inserter) + { + tl_assert (m_sorted); + return partial_lookup (0, m_nodes.begin (), m_nodes.end (), bit_set, inserter); + } + +private: + node_list m_nodes; + bool m_sorted; + + void sort_range (tl::BitSetMask::index_type bit, iterator from, iterator to) + { + if (from == to) { + return; + } + + // special case of identical entries which creates a sequence of + // single entries + bool all_same = true; + for (auto i = from + 1; i != to && all_same; ++i) { + if (*i != *from) { + all_same = false; + } + } + if (all_same) { + // this is also the case for a single element + for (auto i = from + 1; i != to; ++i) { + i->next = 1; + } + return; + } + + // we have at least one element. The first one is taken for the previous level node, so we start partitioning + // at the second node + ++from; + + auto middle_false = std::partition (from, to, tl::bit_set_mask_compare (bit, tl::BitSetMask::False)); + auto middle_true = std::partition (middle_false, to, tl::bit_set_mask_compare (bit, tl::BitSetMask::True)); + auto middle_never = std::partition (middle_true, to, tl::bit_set_mask_compare (bit, tl::BitSetMask::Never)); + + from->next = middle_false - from; + if (middle_false != to) { + middle_false->next = middle_true - middle_false; + } + if (middle_true != to) { + middle_true->next = middle_never - middle_true; + } + if (middle_never != to) { + middle_never->next = to - middle_never; + } + + sort_range (bit + 1, from, middle_false); + sort_range (bit + 1, middle_false, middle_true); + sort_range (bit + 1, middle_true, middle_never); + sort_range (bit + 1, middle_never, to); + } + + template + bool partial_lookup (tl::BitSetMask::index_type bit, const_iterator from, const_iterator to, const tl::BitSet &bit_set, Inserter inserter) + { + if (from == to) { + return false; + } + + bool any = false; + if (from->mask.match (bit_set)) { + *inserter++ = from->value; + any = true; + } + + bool b = bit_set [bit]; + + auto i = ++from; + while (i != to) { + auto m = i->mask [bit]; + if (m == tl::BitSetMask::Any || (m == tl::BitSetMask::True && b) || (m == tl::BitSetMask::False && !b)) { + if (partial_lookup (bit + 1, i, i + i->next, bit_set, inserter)) { + any = true; + } + } + if (i->next) { + break; + } + i += i->next; + } + + return any; + } +}; + +} + +#endif diff --git a/src/tl/tl/tlBitSetMask.cc b/src/tl/tl/tlBitSetMask.cc index 741e1c4833..1d749827c9 100644 --- a/src/tl/tl/tlBitSetMask.cc +++ b/src/tl/tl/tlBitSetMask.cc @@ -265,23 +265,38 @@ BitSetMask::operator[] (index_type index) const bool BitSetMask::match (const tl::BitSet &bs) const { - unsigned nw_bs = nwords (bs.m_size); - unsigned nw = nwords (m_size); + unsigned int nw_bs = nwords (bs.m_size); + unsigned int nw = nwords (m_size); const tl::BitSet::data_type *d0 = mp_data0, *d1 = mp_data1; const tl::BitSet::data_type *s = bs.mp_data; unsigned int i; - for (i = 0; i < nw_bs && i < nw; ++i, ++d0, ++d1, ++s) { - tl::BitSet::data_type d = *s; + for (i = 0; i < nw; ++i, ++d0, ++d1) { + + tl::BitSet::data_type d = i < nw_bs ? *s++ : 0; + + tl::BitSet::data_type invalid = 0; + if (i >= nw_bs) { + invalid = ~invalid; + } else if (bs.m_size < (i + 1) * (sizeof (tl::BitSet::data_type) * 8)) { + invalid = (1 << ((i + 1) * (sizeof (tl::BitSet::data_type) * 8) - bs.m_size)) - 1; + } + + // "never" matches no valid bit ("never" is: d0 and d1 bits are ones) + if (((*d0 & *d1) & ~invalid) != 0) { + return false; + } + // A "true" in place of "false expected" gives "no match" - if ((*d0 & d) != 0) { + if ((*d0 & ~*d1 & d) != 0) { return false; } // A "false" in place of "true expected" gives "no match" - if ((*d1 & ~d) != 0) { + if ((*d1 & ~*d0 & ~d) != 0) { return false; } + } // as "not set" corresponds to "Any", we can stop here and have a match. diff --git a/src/tl/tl/tlBitSetMask.h b/src/tl/tl/tlBitSetMask.h index 27541a6db8..95195ff68b 100644 --- a/src/tl/tl/tlBitSetMask.h +++ b/src/tl/tl/tlBitSetMask.h @@ -45,7 +45,7 @@ namespace tl * the bit set corresponds to the mask. * * Allocation is dynamic when a mask element is accessed for write. Bits beyond the - * size are treated as "Any". + * allocated size are treated as "Any". */ class TL_PUBLIC BitSetMask { @@ -186,4 +186,15 @@ class TL_PUBLIC BitSetMask } +namespace std +{ + +inline void +swap (tl::BitSetMask &a, tl::BitSetMask &b) +{ + a.swap (b); +} + +} + #endif diff --git a/src/tl/unit_tests/tlBitSetMaskTests.cc b/src/tl/unit_tests/tlBitSetMaskTests.cc index 7010a95aba..4c90995fd5 100644 --- a/src/tl/unit_tests/tlBitSetMaskTests.cc +++ b/src/tl/unit_tests/tlBitSetMaskTests.cc @@ -49,6 +49,18 @@ static std::string l2s (const tl::BitSetMask &s) return x; } +static tl::BitSet bs (const char *s) +{ + tl::BitSet res; + for (unsigned int i = 0; *s; ++i, ++s) { + res.set (i); + if (*s == '0') { + res.reset (i); + } + } + return res; +} + TEST(1_Basic) { tl::BitSetMask bs; @@ -261,4 +273,51 @@ TEST(4_Assign) EXPECT_EQ (l2s (bs3), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); } +TEST(5_Match) +{ + tl::BitSetMask bsm; + EXPECT_EQ (bsm.match (bs ("")), true); + EXPECT_EQ (bsm.match (bs ("0")), true); + EXPECT_EQ (bsm.match (bs ("1")), true); + EXPECT_EQ (bsm.match (bs ("10101")), true); + + bsm.set (1, tl::BitSetMask::Never); + EXPECT_EQ (l2s (bsm), "X-"); + EXPECT_EQ (bsm.match (bs ("")), true); + EXPECT_EQ (bsm.match (bs ("0")), true); + EXPECT_EQ (bsm.match (bs ("1")), true); + EXPECT_EQ (bsm.match (bs ("10101")), false); // fails because "never bit" is used. + + bsm.clear (); + bsm.set (1, tl::BitSetMask::True); + bsm.set (2, tl::BitSetMask::False); + EXPECT_EQ (l2s (bsm), "X10"); + + EXPECT_EQ (bsm.match (bs ("")), false); + EXPECT_EQ (bsm.match (bs ("0")), false); + EXPECT_EQ (bsm.match (bs ("000")), false); + EXPECT_EQ (bsm.match (bs ("001")), false); + EXPECT_EQ (bsm.match (bs ("010")), true); + EXPECT_EQ (bsm.match (bs ("011")), false); + EXPECT_EQ (bsm.match (bs ("1")), false); + EXPECT_EQ (bsm.match (bs ("100")), false); + EXPECT_EQ (bsm.match (bs ("101")), false); + EXPECT_EQ (bsm.match (bs ("110")), true); + EXPECT_EQ (bsm.match (bs ("111")), false); + EXPECT_EQ (bsm.match (bs ("10101")), false); + EXPECT_EQ (bsm.match (bs ("11001")), true); + + bsm.clear (); + bsm.set (32, tl::BitSetMask::True); + EXPECT_EQ (bsm.match (bs ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0")), false); + EXPECT_EQ (bsm.match (bs ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1")), true); + EXPECT_EQ (bsm.match (bs ("")), false); + + bsm.clear (); + bsm.set (32, tl::BitSetMask::False); + EXPECT_EQ (bsm.match (bs ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0")), true); + EXPECT_EQ (bsm.match (bs ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1")), false); + EXPECT_EQ (bsm.match (bs ("")), true); // because unset bits count as zero +} + } From 1890a17544b1292bd488cd0c355a9bc64224cbf2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 Aug 2024 23:12:53 +0200 Subject: [PATCH 04/15] First tests for bit set map. Good performance :) --- src/tl/tl/tlBitSetMap.h | 51 ++++++-- src/tl/unit_tests/tlBitSetMapTests.cc | 165 ++++++++++++++++++++++++++ src/tl/unit_tests/unit_tests.pro | 1 + 3 files changed, 209 insertions(+), 8 deletions(-) create mode 100644 src/tl/unit_tests/tlBitSetMapTests.cc diff --git a/src/tl/tl/tlBitSetMap.h b/src/tl/tl/tlBitSetMap.h index db1ddc47e1..3928bfa7ef 100644 --- a/src/tl/tl/tlBitSetMap.h +++ b/src/tl/tl/tlBitSetMap.h @@ -208,7 +208,10 @@ class TL_PUBLIC bit_set_map void sort () { if (! m_sorted) { - sort_range (0, m_nodes.begin (), m_nodes.end ()); + if (! m_nodes.empty ()) { + m_nodes.front ().next = m_nodes.size (); + sort_range (0, m_nodes.begin (), m_nodes.end ()); + } m_sorted = true; } } @@ -243,12 +246,44 @@ class TL_PUBLIC bit_set_map * The return value is true, if any value has been found. */ template - bool lookup (const tl::BitSet &bit_set, Inserter inserter) + bool lookup (const tl::BitSet &bit_set, Inserter inserter) const { tl_assert (m_sorted); return partial_lookup (0, m_nodes.begin (), m_nodes.end (), bit_set, inserter); } + /** + * @brief Begin iterator + */ + iterator begin () + { + return m_nodes.begin (); + } + + /** + * @brief End iterator + */ + iterator end () + { + return m_nodes.end (); + } + + /** + * @brief Begin iterator (const version) + */ + const_iterator begin () const + { + return m_nodes.begin (); + } + + /** + * @brief End iterator (const version) + */ + const_iterator end () const + { + return m_nodes.end (); + } + private: node_list m_nodes; bool m_sorted; @@ -263,7 +298,7 @@ class TL_PUBLIC bit_set_map // single entries bool all_same = true; for (auto i = from + 1; i != to && all_same; ++i) { - if (*i != *from) { + if (i->mask != from->mask) { all_same = false; } } @@ -279,9 +314,9 @@ class TL_PUBLIC bit_set_map // at the second node ++from; - auto middle_false = std::partition (from, to, tl::bit_set_mask_compare (bit, tl::BitSetMask::False)); - auto middle_true = std::partition (middle_false, to, tl::bit_set_mask_compare (bit, tl::BitSetMask::True)); - auto middle_never = std::partition (middle_true, to, tl::bit_set_mask_compare (bit, tl::BitSetMask::Never)); + auto middle_false = std::partition (from, to, tl::bit_set_mask_compare (bit, tl::BitSetMask::False)); + auto middle_true = std::partition (middle_false, to, tl::bit_set_mask_compare (bit, tl::BitSetMask::True)); + auto middle_never = std::partition (middle_true, to, tl::bit_set_mask_compare (bit, tl::BitSetMask::Never)); from->next = middle_false - from; if (middle_false != to) { @@ -301,7 +336,7 @@ class TL_PUBLIC bit_set_map } template - bool partial_lookup (tl::BitSetMask::index_type bit, const_iterator from, const_iterator to, const tl::BitSet &bit_set, Inserter inserter) + bool partial_lookup (tl::BitSetMask::index_type bit, const_iterator from, const_iterator to, const tl::BitSet &bit_set, Inserter inserter) const { if (from == to) { return false; @@ -323,7 +358,7 @@ class TL_PUBLIC bit_set_map any = true; } } - if (i->next) { + if (i->next == 0) { break; } i += i->next; diff --git a/src/tl/unit_tests/tlBitSetMapTests.cc b/src/tl/unit_tests/tlBitSetMapTests.cc new file mode 100644 index 0000000000..272a314284 --- /dev/null +++ b/src/tl/unit_tests/tlBitSetMapTests.cc @@ -0,0 +1,165 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlBitSetMap.h" +#include "tlUnitTest.h" +#include "tlString.h" +#include "tlTimer.h" + +static tl::BitSet bs (const char *s) +{ + tl::BitSet res; + for (unsigned int i = 0; *s; ++i, ++s) { + res.set (i); + if (*s == '0') { + res.reset (i); + } + } + return res; +} + +static tl::BitSetMask bsm (const char *s) +{ + tl::BitSetMask res; + for (unsigned int i = 0; *s; ++i, ++s) { + if (*s == '0') { + res.set (i, tl::BitSetMask::False); + } else if (*s == '1') { + res.set (i, tl::BitSetMask::True); + } else if (*s == 'X') { + res.set (i, tl::BitSetMask::Any); + } else if (*s == '-') { + res.set (i, tl::BitSetMask::Never); + } + } + return res; +} + +struct SetInserter +{ + SetInserter (std::set &s) : ps (&s) { } + + SetInserter &operator++(int) { return *this; } + SetInserter &operator* () { return *this; } + + SetInserter &operator= (int v) { ps->insert (v); return *this; } + + std::set *ps; +}; + +static std::string match (const tl::bit_set_map &bsm, const tl::BitSet &bs) +{ + std::set values; + bsm.lookup (bs, SetInserter (values)); + + std::string res; + for (auto i = values.begin (); i != values.end (); ++i) { + if (!res.empty ()) { + res += ","; + } + res += tl::to_string (*i); + } + return res; +} + +namespace +{ + +TEST(1_Basic) +{ + tl::bit_set_map map; + + map.insert (bsm ("X10"), 1); + map.insert (bsm ("X10"), 11); + map.insert (bsm ("1"), 2); + map.insert (bsm ("101"), 3); + map.insert (bsm ("1X0"), 4); + map.insert (bsm ("110"), 5); + map.sort (); + + EXPECT_EQ (match (map, bs ("")), ""); + EXPECT_EQ (match (map, bs ("1")), "2,4"); + EXPECT_EQ (match (map, bs ("110")), "1,2,4,5,11"); + EXPECT_EQ (match (map, bs ("01")), "1,11"); + EXPECT_EQ (match (map, bs ("010000")), "1,11"); + + map.insert (bsm (""), 0); + try { + match (map, bs ("")); + EXPECT_EQ (true, false); // not sorted + } catch (...) { } + + map.sort (); + EXPECT_EQ (match (map, bs ("")), "0"); +} + +static std::string bitstr (unsigned int n, unsigned int nbits) +{ + std::string r; + while (nbits > 0) { + r += ((n & 1) != 0 ? '1' : '0'); + n >>= 1; + --nbits; + } + return r; +} + +TEST(2_Regular) +{ + tl::bit_set_map map; + + unsigned int num = 10000; + unsigned int nbits = 20; + + for (unsigned int i = 0; i < num; ++i) { + map.insert (bsm (bitstr (i, nbits).c_str ()), int (i)); + } + + { + tl::SelfTimer timer ("sorting"); + map.sort (); + } + + { + tl::SelfTimer timer ("match method"); + for (unsigned int i = 0; i < num; ++i) { + EXPECT_EQ (match (map, bs (bitstr (i, nbits).c_str ())), tl::to_string (i)); + } + } + + // brute force + { + tl::SelfTimer timer ("brute force"); + for (unsigned int i = 0; i < num; ++i) { + tl::BitSet k = bs (bitstr (i, nbits).c_str ()); + int value = 0; + for (auto j = map.begin (); j != map.end (); ++j) { + if (j->mask.match (k)) { + value = j->value; + } + } + EXPECT_EQ (value, int (i)); + } + } +} + +} diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index a65a773e7f..7ec4203dd6 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ tlAlgorithmTests.cc \ tlBase64Tests.cc \ + tlBitSetMapTests.cc \ tlBitSetMaskTests.cc \ tlBitSetTests.cc \ tlClassRegistryTests.cc \ From b74e9567fd3bf7e5e587afac0808ecc933453e26 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 27 Aug 2024 22:07:23 +0200 Subject: [PATCH 05/15] More tests --- src/tl/unit_tests/tlBitSetMapTests.cc | 80 +++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/src/tl/unit_tests/tlBitSetMapTests.cc b/src/tl/unit_tests/tlBitSetMapTests.cc index 272a314284..af5be63c8a 100644 --- a/src/tl/unit_tests/tlBitSetMapTests.cc +++ b/src/tl/unit_tests/tlBitSetMapTests.cc @@ -25,6 +25,8 @@ #include "tlString.h" #include "tlTimer.h" +#include + static tl::BitSet bs (const char *s) { tl::BitSet res; @@ -66,11 +68,8 @@ struct SetInserter std::set *ps; }; -static std::string match (const tl::bit_set_map &bsm, const tl::BitSet &bs) +static std::string s2s (const std::set &values) { - std::set values; - bsm.lookup (bs, SetInserter (values)); - std::string res; for (auto i = values.begin (); i != values.end (); ++i) { if (!res.empty ()) { @@ -81,6 +80,13 @@ static std::string match (const tl::bit_set_map &bsm, const tl::BitSet &bs) return res; } +static std::string match (const tl::bit_set_map &bsm, const tl::BitSet &bs) +{ + std::set values; + bsm.lookup (bs, SetInserter (values)); + return s2s (values); +} + namespace { @@ -162,4 +168,70 @@ TEST(2_Regular) } } +TEST(3_IrregularTest) +{ + srand (0); + + tl::bit_set_map map; + + unsigned int num = 10000; + unsigned int nbits_min = 10; + unsigned int nbits_max = 20; + + for (unsigned int i = 0; i < num; ++i) { + std::string s; + unsigned int n = nbits_min + (rand () % (nbits_max - nbits_min)); + for (unsigned int j = 0; j < n; ++j) { + // this pattern gives roughly 5 matches per entry with 10k entries + s += "010101X"[rand () % 7]; + } + map.insert (bsm (s.c_str ()), int (i)); + } + + std::vector test_vectors; + for (unsigned int i = 0; i < num; ++i) { + std::string s; + unsigned int n = nbits_min + (rand () % (nbits_max - nbits_min)); + for (unsigned int j = 0; j < n; ++j) { + s += "01"[rand () % 2]; + } + test_vectors.push_back (bs (s.c_str ())); + } + + { + tl::SelfTimer timer ("sorting"); + map.sort (); + } + + std::vector matches; + + { + tl::SelfTimer timer ("match method"); + for (auto i = test_vectors.begin (); i != test_vectors.end (); ++i) { + matches.push_back (match (map, *i)); + } + } + + size_t max_matches = 0; + + // brute force + { + tl::SelfTimer timer ("brute force"); + for (auto i = test_vectors.begin (); i != test_vectors.end (); ++i) { + std::set values; + for (auto j = map.begin (); j != map.end (); ++j) { + if (j->mask.match (*i)) { + values.insert(j->value); + } + } + max_matches = std::max (max_matches, values.size ()); + EXPECT_EQ (s2s (values), matches [i - test_vectors.begin ()]); + } + } + + // sanity check + tl::info << "Max. matches: " << max_matches; + EXPECT_EQ (max_matches > 5, true); +} + } From 863d835ff74df001bca87f9b2a6fac1e83b8ec8b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 Aug 2024 00:12:22 +0200 Subject: [PATCH 06/15] WIP: tagged area collector --- src/db/db/db.pro | 2 + src/db/db/dbAreaCollector.cc | 30 +++ src/db/db/dbAreaCollector.h | 221 ++++++++++++++++++++++ src/db/db/dbEdgeProcessor.cc | 1 + src/db/db/dbEdgeProcessor.h | 1 + src/db/unit_tests/dbAreaCollectorTests.cc | 72 +++++++ src/db/unit_tests/unit_tests.pro | 1 + 7 files changed, 328 insertions(+) create mode 100644 src/db/db/dbAreaCollector.cc create mode 100644 src/db/db/dbAreaCollector.h create mode 100644 src/db/unit_tests/dbAreaCollectorTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 299f029e37..83991f2c2c 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -7,6 +7,7 @@ include($$PWD/../../lib.pri) DEFINES += MAKE_DB_LIBRARY SOURCES = \ + dbAreaCollector.cc \ dbArray.cc \ dbBox.cc \ dbBoxConvert.cc \ @@ -232,6 +233,7 @@ SOURCES = \ dbShapeCollectionUtils.cc HEADERS = \ + dbAreaCollector.h \ dbArray.h \ dbBoxConvert.h \ dbBox.h \ diff --git a/src/db/db/dbAreaCollector.cc b/src/db/db/dbAreaCollector.cc new file mode 100644 index 0000000000..545d261998 --- /dev/null +++ b/src/db/db/dbAreaCollector.cc @@ -0,0 +1,30 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbAreaCollector.h" + +namespace db +{ + + // .. nothing yet .. + +} diff --git a/src/db/db/dbAreaCollector.h b/src/db/db/dbAreaCollector.h new file mode 100644 index 0000000000..3bf038e784 --- /dev/null +++ b/src/db/db/dbAreaCollector.h @@ -0,0 +1,221 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbAreaCollector +#define HDR_dbAreaCollector + +#include "dbCommon.h" + +#include "dbEdgeProcessor.h" +#include "tlBitSetMap.h" + +namespace db { + +/** + * @brief The receiver for the tagged partial areas + * + * See description of tagged_area_collector for details. + */ +template +class DB_PUBLIC tagged_area_receiver +{ +public: + typedef db::coord_traits::area_type area_type; + + /** + * @brief Constructor + */ + tagged_area_receiver () { } + + /** + * @brief Destructor + */ + virtual ~tagged_area_receiver () { } + + /** + * @brief This method gets called when the scanline process starts + */ + virtual void start () { } + + /** + * @brief This method gets called when the scanline process finishes + */ + virtual void finish () { } + + /** + * @brief Adds some partial area with the given value + */ + virtual void add_area (area_type /*area*/, const Value & /*value*/) { } +}; + +/** + * @brief A helper class providing an inserter that is the connection between the receiver and the provider + */ +template +class DB_PUBLIC tagged_area_inserter +{ +public: + typedef db::coord_traits::area_type area_type; + + tagged_area_inserter (area_type area, tagged_area_receiver *receiver) + : m_area (area), mp_receiver (receiver) + { + // .. nothing yet .. + } + + // methods necessary, so this object can act as an inserter + tagged_area_inserter &operator* () { return *this; } + tagged_area_inserter &operator++ (int) { return *this; } + + tagged_area_inserter &operator= (const Value &value) + { + mp_receiver->add_area (m_area, value); + return *this; + } + +private: + area_type m_area; + tagged_area_receiver *mp_receiver; +}; + +/** + * @brief Provides the operation and edge receiver part of the tagged area collector + * + * Use this object both as the edge operator and as an edge collector. + * After running the edge processor, use "area" to obtain the area. + * + * This method collects "tagged areas". That is, each field of the area divided by + * the edges carries a bit set which is made from the combinations of overlapping + * layers. The layers are given by the property number where the number is the + * bit set in the bit field. Hence, every field is associated with a bit set. + * + * The Area collector will now report the field's areas for accumulation together with + * a field value that is obtained from the bit set map. As the bit set map + * may deliver multiple fields, multiple such values can be present for each field. + * The areas are reported through the tagged_area_receiver object. This object + * is supposed to add up the areas in an application specific fashion. + */ +template +class DB_PUBLIC tagged_area_collector + : public EdgeEvaluatorBase, + public EdgeSink +{ +public: + typedef db::coord_traits::area_type area_type; + + /** + * @brief Constructor + */ + tagged_area_collector (const tl::bit_set_map &bsm, tagged_area_receiver &receiver) + : mp_bsm (&bsm), mp_receiver (&receiver) + { + // .. nothing yet .. + } + + // implementation of EdgeEvaluatorBase + virtual void reset () + { + m_prev = tl::BitSet (); + m_state = tl::BitSet (); + } + + virtual void begin_group () + { + m_prev = m_state; + } + + virtual int edge (bool north, bool enter, property_type p) + { + if (north) { + + m_counts.resize (p + 1, 0); + + int &count = m_counts [p]; + if (enter) { + if (count == 0) { + m_state.set (p); + } + ++count; + } else { + --count; + if (count == 0) { + m_state.reset (p); + } + } + + // this will call "put" when the group is finished + return 1; + + } else { + return 0; + } + } + + virtual bool is_reset () const + { + // that is a dummy + return true; + } + + virtual bool prefer_touch () const + { + // leave events come before enter events + return false; + } + + virtual bool selects_edges () const + { + // select_edge is not needed + return false; + } + + // implementation of EdgeSink + + virtual void start () + { + mp_receiver->start (); + } + + virtual void flush () + { + mp_receiver->finish (); + } + + virtual void put (const db::Edge &edge) + { + area_type partial_area = area_type (edge.p1 ().x () + edge.p2 ().x ()) * area_type (edge.dy ()) * 0.5; + mp_bsm->lookup (m_prev, tagged_area_inserter (partial_area, mp_receiver)); + mp_bsm->lookup (m_state, tagged_area_inserter (-partial_area, mp_receiver)); + } + +private: + area_type m_area_sum; + const tl::bit_set_map *mp_bsm; + tagged_area_receiver *mp_receiver; + tl::BitSet m_prev, m_state; + std::vector m_counts; +}; + +} + +#endif + diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 4f0534ae8a..a452cf4805 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -1694,6 +1694,7 @@ class EdgeProcessorState void next_coincident () { m_pn = m_ps = 0; + mp_op->begin_group (); } void end_coincident () diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index ff9f74c5f7..36a4501252 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -265,6 +265,7 @@ class DB_PUBLIC EdgeEvaluatorBase virtual void reset () { } virtual void reserve (size_t /*n*/) { } + virtual void begin_group () { } virtual int edge (bool /*north*/, bool /*enter*/, property_type /*p*/) { return 0; } virtual int select_edge (bool /*horizontal*/, property_type /*p*/) { return 0; } virtual int compare_ns () const { return 0; } diff --git a/src/db/unit_tests/dbAreaCollectorTests.cc b/src/db/unit_tests/dbAreaCollectorTests.cc new file mode 100644 index 0000000000..3ee5e591f3 --- /dev/null +++ b/src/db/unit_tests/dbAreaCollectorTests.cc @@ -0,0 +1,72 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbAreaCollector.h" +#include "dbEdgeProcessor.h" +#include "tlUnitTest.h" + + +namespace +{ + +class AreaReceiver + : public db::tagged_area_receiver +{ +public: + typedef db::coord_traits::area_type area_type; + + AreaReceiver () : m_sum (0.0) { } + virtual void add_area (area_type area, const double &value) { m_sum += value * area; } + + double get () const { return m_sum; } + +private: + double m_sum; +}; + +} + +TEST(1) +{ + db::EdgeProcessor ep; + + ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000)), 0); + ep.insert (db::SimplePolygon (db::Box (500, 1000, 1500, 3000)), 1); + + // set up an XOR mask where 1-vs-0 is counted twice + tl::bit_set_map bsm; + tl::BitSetMask bs0; + bs0.set (0, tl::BitSetMask::True); + bs0.set (1, tl::BitSetMask::False); + tl::BitSetMask bs1; + bs0.set (0, tl::BitSetMask::False); + bs0.set (1, tl::BitSetMask::True); + bsm.insert (bs0, 1.0); + bsm.insert (bs1, 2.0); + bsm.sort (); + + AreaReceiver rec; + db::tagged_area_collector coll (bsm, rec); + ep.process (coll, coll); + + EXPECT_EQ (rec.get (), 0.0); // @@@ +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index a5ef9690ec..5b611c148d 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -7,6 +7,7 @@ TARGET = db_tests include($$PWD/../../lib_ut.pri) SOURCES = \ + dbAreaCollectorTests.cc \ dbCompoundOperationTests.cc \ dbFillToolTests.cc \ dbLogTests.cc \ From 8c596fddc3195ecb2b5f7903865eccab2748236a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 Aug 2024 00:38:54 +0200 Subject: [PATCH 07/15] WIP: debugging --- src/db/db/dbAreaCollector.h | 10 ++++++++-- src/db/unit_tests/dbAreaCollectorTests.cc | 12 ++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbAreaCollector.h b/src/db/db/dbAreaCollector.h index 3bf038e784..a64c338e8c 100644 --- a/src/db/db/dbAreaCollector.h +++ b/src/db/db/dbAreaCollector.h @@ -147,7 +147,9 @@ class DB_PUBLIC tagged_area_collector { if (north) { - m_counts.resize (p + 1, 0); + while (m_counts.size () <= p) { + m_counts.push_back (0); + } int &count = m_counts [p]; if (enter) { @@ -172,7 +174,11 @@ class DB_PUBLIC tagged_area_collector virtual bool is_reset () const { - // that is a dummy + for (auto i = m_counts.begin (); i != m_counts.end (); ++i) { + if (*i) { + return false; + } + } return true; } diff --git a/src/db/unit_tests/dbAreaCollectorTests.cc b/src/db/unit_tests/dbAreaCollectorTests.cc index 3ee5e591f3..96cdcff11c 100644 --- a/src/db/unit_tests/dbAreaCollectorTests.cc +++ b/src/db/unit_tests/dbAreaCollectorTests.cc @@ -35,7 +35,11 @@ class AreaReceiver typedef db::coord_traits::area_type area_type; AreaReceiver () : m_sum (0.0) { } - virtual void add_area (area_type area, const double &value) { m_sum += value * area; } + + virtual void add_area (area_type area, const double &value) + { + m_sum += value * area; + } double get () const { return m_sum; } @@ -58,8 +62,8 @@ TEST(1) bs0.set (0, tl::BitSetMask::True); bs0.set (1, tl::BitSetMask::False); tl::BitSetMask bs1; - bs0.set (0, tl::BitSetMask::False); - bs0.set (1, tl::BitSetMask::True); + bs1.set (0, tl::BitSetMask::False); + bs1.set (1, tl::BitSetMask::True); bsm.insert (bs0, 1.0); bsm.insert (bs1, 2.0); bsm.sort (); @@ -68,5 +72,5 @@ TEST(1) db::tagged_area_collector coll (bsm, rec); ep.process (coll, coll); - EXPECT_EQ (rec.get (), 0.0); // @@@ + EXPECT_EQ (rec.get (), 4500000); } From 9d45a01abfe9457432b06ae487fb1cae5c659f39 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 Aug 2024 21:30:21 +0200 Subject: [PATCH 08/15] String representation of tl::BitSet and tl::BitSetMask --- src/tl/tl/tlBitSet.cc | 45 +++++++++++++++++ src/tl/tl/tlBitSet.h | 36 +++++++------- src/tl/tl/tlBitSetMask.cc | 67 ++++++++++++++++++++++++++ src/tl/tl/tlBitSetMask.h | 36 +++++++------- src/tl/unit_tests/tlBitSetMapTests.cc | 23 +-------- src/tl/unit_tests/tlBitSetMaskTests.cc | 33 +++---------- src/tl/unit_tests/tlBitSetTests.cc | 11 +++-- 7 files changed, 161 insertions(+), 90 deletions(-) diff --git a/src/tl/tl/tlBitSet.cc b/src/tl/tl/tlBitSet.cc index 669b5b749e..45928e0789 100644 --- a/src/tl/tl/tlBitSet.cc +++ b/src/tl/tl/tlBitSet.cc @@ -45,6 +45,51 @@ static inline unsigned int bit (BitSet::size_type index) return 31 - (index % (sizeof (BitSet::data_type) * 8)); } +BitSet::BitSet () + : mp_data (0), m_size (0) +{ + // .. nothing yet .. +} + +BitSet::BitSet (const std::string &s) + : mp_data (0), m_size (0) +{ + index_type bit = 0; + for (const char *cp = s.c_str (); *cp; ++cp, ++bit) { + set_value (bit, *cp == '1'); + } +} + +BitSet::BitSet (const BitSet &other) + : mp_data (0), m_size (0) +{ + operator= (other); +} + +BitSet::BitSet (BitSet &&other) + : mp_data (0), m_size (0) +{ + operator= (std::move (other)); +} + +std::string +BitSet::to_string () const +{ + std::string r; + r.reserve (m_size); + + for (index_type i = 0; i < m_size; ++i) { + r += operator[] (i) ? '1' : '0'; + } + + return r; +} + +BitSet::~BitSet () +{ + clear (); +} + BitSet & BitSet::operator= (const BitSet &other) { diff --git a/src/tl/tl/tlBitSet.h b/src/tl/tl/tlBitSet.h index b39c7d3e3f..f885303151 100644 --- a/src/tl/tl/tlBitSet.h +++ b/src/tl/tl/tlBitSet.h @@ -28,6 +28,7 @@ #include #include +#include namespace tl { @@ -52,11 +53,7 @@ class TL_PUBLIC BitSet /** * @brief Default constructor: creates an empty bit set */ - BitSet () - : mp_data (0), m_size (0) - { - // .. nothing yet .. - } + BitSet (); /** * @brief Creates and initializes a bit set from a range of indexes @@ -69,31 +66,32 @@ class TL_PUBLIC BitSet set (from, to); } + /** + * @brief Creates a bit set from a string + * + * In the string, a '0' character is for False, '1' for True. + */ + BitSet (const std::string &s); + /** * @brief Copy constructor */ - BitSet (const BitSet &other) - : mp_data (0), m_size (0) - { - operator= (other); - } + BitSet (const BitSet &other); /** * @brief Move constructor */ - BitSet (BitSet &&other) - : mp_data (0), m_size (0) - { - operator= (std::move (other)); - } + BitSet (BitSet &&other); + + /** + * @brief Converts the bit set to a string + */ + std::string to_string () const; /** * @brief Destructor */ - ~BitSet () - { - clear (); - } + ~BitSet (); /** * @brief Assignment diff --git a/src/tl/tl/tlBitSetMask.cc b/src/tl/tl/tlBitSetMask.cc index 1d749827c9..229e0a8b42 100644 --- a/src/tl/tl/tlBitSetMask.cc +++ b/src/tl/tl/tlBitSetMask.cc @@ -46,6 +46,73 @@ static inline unsigned int bit (BitSetMask::size_type index) return 31 - (index % (sizeof (BitSetMask::data_type) * 8)); } +BitSetMask::BitSetMask () + : mp_data0 (0), mp_data1 (0), m_size (0) +{ + // .. nothing yet .. +} + +BitSetMask::BitSetMask (const std::string &s) + : mp_data0 (0), mp_data1 (0), m_size (0) +{ + index_type bit = 0; + for (const char *cp = s.c_str (); *cp; ++cp, ++bit) { + mask_type m = Any; + if (*cp == '0') { + m = False; + } else if (*cp == '1') { + m = True; + } else if (*cp == '-') { + m = Never; + } + set (bit, m); + } +} + +BitSetMask::BitSetMask (const BitSetMask &other) + : mp_data0 (0), mp_data1 (0), m_size (0) +{ + operator= (other); +} + +BitSetMask::BitSetMask (BitSetMask &&other) + : mp_data0 (0), mp_data1 (0), m_size (0) +{ + operator= (std::move (other)); +} + +BitSetMask::~BitSetMask () +{ + clear (); +} + +std::string +BitSetMask::to_string () const +{ + std::string r; + r.reserve (m_size); + + for (index_type i = 0; i < m_size; ++i) { + switch (operator[] (i)) { + case False: + r += '0'; + break; + case True: + r += '1'; + break; + case Never: + r += '-'; + break; + case Any: + default: + r += 'X'; + break; + } + } + + return r; +} + BitSetMask & BitSetMask::operator= (const BitSetMask &other) { diff --git a/src/tl/tl/tlBitSetMask.h b/src/tl/tl/tlBitSetMask.h index 95195ff68b..14f75257d4 100644 --- a/src/tl/tl/tlBitSetMask.h +++ b/src/tl/tl/tlBitSetMask.h @@ -29,6 +29,7 @@ #include #include +#include namespace tl { @@ -59,37 +60,34 @@ class TL_PUBLIC BitSetMask /** * @brief Default constructor: creates an empty bit set */ - BitSetMask () - : mp_data0 (0), mp_data1 (0), m_size (0) - { - // .. nothing yet .. - } + BitSetMask (); + + /** + * @brief Creates a bit set mask from a string + * + * In the string, a '0' character is for False, '1' for True, 'X' for Any and '-' for Never. + */ + BitSetMask (const std::string &s); /** * @brief Copy constructor */ - BitSetMask (const BitSetMask &other) - : mp_data0 (0), mp_data1 (0), m_size (0) - { - operator= (other); - } + BitSetMask (const BitSetMask &other); /** * @brief Move constructor */ - BitSetMask (BitSetMask &&other) - : mp_data0 (0), mp_data1 (0), m_size (0) - { - operator= (std::move (other)); - } + BitSetMask (BitSetMask &&other); /** * @brief Destructor */ - ~BitSetMask () - { - clear (); - } + ~BitSetMask (); + + /** + * @brief Converts the mask to a string + */ + std::string to_string () const; /** * @brief Assignment diff --git a/src/tl/unit_tests/tlBitSetMapTests.cc b/src/tl/unit_tests/tlBitSetMapTests.cc index af5be63c8a..e0ca46a2a4 100644 --- a/src/tl/unit_tests/tlBitSetMapTests.cc +++ b/src/tl/unit_tests/tlBitSetMapTests.cc @@ -29,31 +29,12 @@ static tl::BitSet bs (const char *s) { - tl::BitSet res; - for (unsigned int i = 0; *s; ++i, ++s) { - res.set (i); - if (*s == '0') { - res.reset (i); - } - } - return res; + return tl::BitSet (s); } static tl::BitSetMask bsm (const char *s) { - tl::BitSetMask res; - for (unsigned int i = 0; *s; ++i, ++s) { - if (*s == '0') { - res.set (i, tl::BitSetMask::False); - } else if (*s == '1') { - res.set (i, tl::BitSetMask::True); - } else if (*s == 'X') { - res.set (i, tl::BitSetMask::Any); - } else if (*s == '-') { - res.set (i, tl::BitSetMask::Never); - } - } - return res; + return tl::BitSetMask (s); } struct SetInserter diff --git a/src/tl/unit_tests/tlBitSetMaskTests.cc b/src/tl/unit_tests/tlBitSetMaskTests.cc index 4c90995fd5..d2056e255a 100644 --- a/src/tl/unit_tests/tlBitSetMaskTests.cc +++ b/src/tl/unit_tests/tlBitSetMaskTests.cc @@ -29,36 +29,12 @@ namespace static std::string l2s (const tl::BitSetMask &s) { - std::string x; - for (tl::BitSetMask::index_type i = 0; i < s.size (); ++i) { - switch (s[i]) { - case tl::BitSetMask::Any: - x += "X"; - break; - case tl::BitSetMask::True: - x += "1"; - break; - case tl::BitSetMask::False: - x += "0"; - break; - default: - x += "-"; - break; - } - } - return x; + return s.to_string (); } static tl::BitSet bs (const char *s) { - tl::BitSet res; - for (unsigned int i = 0; *s; ++i, ++s) { - res.set (i); - if (*s == '0') { - res.reset (i); - } - } - return res; + return tl::BitSet (s); } TEST(1_Basic) @@ -67,22 +43,27 @@ TEST(1_Basic) EXPECT_EQ (bs.is_empty (), true); EXPECT_EQ (bs.size (), 0u); EXPECT_EQ (l2s (bs), ""); + EXPECT_EQ (l2s (tl::BitSetMask (l2s (bs))), ""); bs.set (1, tl::BitSetMask::True); EXPECT_EQ (bs.size (), 2u); EXPECT_EQ (l2s (bs), "X1"); + EXPECT_EQ (l2s (tl::BitSetMask (l2s (bs))), "X1"); bs.set (32, tl::BitSetMask::False); EXPECT_EQ (bs.size (), 33u); EXPECT_EQ (l2s (bs), "X1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + EXPECT_EQ (l2s (tl::BitSetMask (l2s (bs))), "X1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); bs.set (3, tl::BitSetMask::False); EXPECT_EQ (bs.size (), 33u); EXPECT_EQ (l2s (bs), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + EXPECT_EQ (l2s (tl::BitSetMask (l2s (bs))), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); bs.set (128, tl::BitSetMask::Any); EXPECT_EQ (bs.size (), 33u); EXPECT_EQ (l2s (bs), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); + EXPECT_EQ (l2s (tl::BitSetMask (l2s (bs))), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0"); bs.clear (); EXPECT_EQ (bs.size (), 0u); diff --git a/src/tl/unit_tests/tlBitSetTests.cc b/src/tl/unit_tests/tlBitSetTests.cc index 39587ae776..f5e84fa9c3 100644 --- a/src/tl/unit_tests/tlBitSetTests.cc +++ b/src/tl/unit_tests/tlBitSetTests.cc @@ -29,11 +29,7 @@ namespace static std::string l2s (const tl::BitSet &s) { - std::string x; - for (tl::BitSet::index_type i = 0; i < s.size (); ++i) { - x += s[i] ? "1" : "0"; - } - return x; + return s.to_string (); } TEST(1_Basic) @@ -42,23 +38,28 @@ TEST(1_Basic) EXPECT_EQ (bs.is_empty (), true); EXPECT_EQ (bs.size (), 0u); EXPECT_EQ (l2s (bs), ""); + EXPECT_EQ (l2s (tl::BitSet (l2s (bs))), ""); bs.set (1); EXPECT_EQ (bs.size (), 2u); EXPECT_EQ (l2s (bs), "01"); + EXPECT_EQ (l2s (tl::BitSet (l2s (bs))), "01"); bs.set (32); EXPECT_EQ (bs.size (), 33u); EXPECT_EQ (l2s (bs), "010000000000000000000000000000001"); + EXPECT_EQ (l2s (tl::BitSet (l2s (bs))), "010000000000000000000000000000001"); bs.set (3); EXPECT_EQ (bs.size (), 33u); EXPECT_EQ (l2s (bs), "010100000000000000000000000000001"); + EXPECT_EQ (l2s (tl::BitSet (l2s (bs))), "010100000000000000000000000000001"); unsigned int indexes[] = { 5, 6, 7 }; bs.set (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0])); EXPECT_EQ (bs.size (), 33u); EXPECT_EQ (l2s (bs), "010101110000000000000000000000001"); + EXPECT_EQ (l2s (tl::BitSet (l2s (bs))), "010101110000000000000000000000001"); bs.reset (128); EXPECT_EQ (bs.size (), 33u); From b7a2926f1fb2c1f6a21150e52dc429c8e6db8d90 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 Aug 2024 21:48:56 +0200 Subject: [PATCH 09/15] More tests for area collector, debugging --- src/db/db/dbAreaCollector.h | 23 ++++--- src/db/unit_tests/dbAreaCollectorTests.cc | 82 ++++++++++++++++++++++- 2 files changed, 95 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbAreaCollector.h b/src/db/db/dbAreaCollector.h index a64c338e8c..d650a33783 100644 --- a/src/db/db/dbAreaCollector.h +++ b/src/db/db/dbAreaCollector.h @@ -126,7 +126,7 @@ class DB_PUBLIC tagged_area_collector * @brief Constructor */ tagged_area_collector (const tl::bit_set_map &bsm, tagged_area_receiver &receiver) - : mp_bsm (&bsm), mp_receiver (&receiver) + : mp_bsm (&bsm), mp_receiver (&receiver), m_state_one_bits (0), m_prev_one_bits (0) { // .. nothing yet .. } @@ -136,11 +136,14 @@ class DB_PUBLIC tagged_area_collector { m_prev = tl::BitSet (); m_state = tl::BitSet (); + m_state_one_bits = 0; + m_prev_one_bits = 0; } virtual void begin_group () { m_prev = m_state; + m_prev_one_bits = m_state_one_bits; } virtual int edge (bool north, bool enter, property_type p) @@ -155,12 +158,14 @@ class DB_PUBLIC tagged_area_collector if (enter) { if (count == 0) { m_state.set (p); + ++m_state_one_bits; } ++count; } else { --count; if (count == 0) { m_state.reset (p); + --m_state_one_bits; } } @@ -174,12 +179,7 @@ class DB_PUBLIC tagged_area_collector virtual bool is_reset () const { - for (auto i = m_counts.begin (); i != m_counts.end (); ++i) { - if (*i) { - return false; - } - } - return true; + return m_state_one_bits == 0; } virtual bool prefer_touch () const @@ -209,8 +209,12 @@ class DB_PUBLIC tagged_area_collector virtual void put (const db::Edge &edge) { area_type partial_area = area_type (edge.p1 ().x () + edge.p2 ().x ()) * area_type (edge.dy ()) * 0.5; - mp_bsm->lookup (m_prev, tagged_area_inserter (partial_area, mp_receiver)); - mp_bsm->lookup (m_state, tagged_area_inserter (-partial_area, mp_receiver)); + if (m_prev_one_bits > 0) { + mp_bsm->lookup (m_prev, tagged_area_inserter (partial_area, mp_receiver)); + } + if (m_state_one_bits > 0) { + mp_bsm->lookup (m_state, tagged_area_inserter (-partial_area, mp_receiver)); + } } private: @@ -219,6 +223,7 @@ class DB_PUBLIC tagged_area_collector tagged_area_receiver *mp_receiver; tl::BitSet m_prev, m_state; std::vector m_counts; + unsigned int m_state_one_bits, m_prev_one_bits; }; } diff --git a/src/db/unit_tests/dbAreaCollectorTests.cc b/src/db/unit_tests/dbAreaCollectorTests.cc index 96cdcff11c..dd181912d6 100644 --- a/src/db/unit_tests/dbAreaCollectorTests.cc +++ b/src/db/unit_tests/dbAreaCollectorTests.cc @@ -49,7 +49,7 @@ class AreaReceiver } -TEST(1) +TEST(1_Basic) { db::EdgeProcessor ep; @@ -74,3 +74,83 @@ TEST(1) EXPECT_EQ (rec.get (), 4500000); } + +TEST(2_ShapesGetMerged) +{ + db::EdgeProcessor ep; + + ep.insert (db::SimplePolygon (db::Box (0, -1000, 1000, 1000)), 0); + ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000)), 0); + ep.insert (db::SimplePolygon (db::Box (500, 1000, 1500, 3000)), 1); + ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000)), 0); + ep.insert (db::SimplePolygon (db::Box (1000, 1000, 1500, 3000)), 1); + + // set up an XOR mask where 1-vs-0 is counted twice + tl::bit_set_map bsm; + tl::BitSetMask bs0; + bs0.set (0, tl::BitSetMask::True); + bs0.set (1, tl::BitSetMask::False); + tl::BitSetMask bs1; + bs1.set (0, tl::BitSetMask::False); + bs1.set (1, tl::BitSetMask::True); + bsm.insert (bs0, 1.0); + bsm.insert (bs1, 2.0); + bsm.sort (); + + AreaReceiver rec; + db::tagged_area_collector coll (bsm, rec); + ep.process (coll, coll); + + EXPECT_EQ (rec.get (), 5500000); +} + +TEST(3_TouchingOnly) +{ + db::EdgeProcessor ep; + + ep.insert (db::SimplePolygon (db::Box (0, -1000, 1000, 1000)), 0); + ep.insert (db::SimplePolygon (db::Box (1000, 0, 2000, 2000)), 1); + ep.insert (db::SimplePolygon (db::Box (1000, 500, 1500, 1500)), 1); + ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 1000)), 0); + ep.insert (db::SimplePolygon (db::Box (1500, 500, 2000, 2000)), 1); + + // set up an XOR mask where 1-vs-0 is counted twice + tl::bit_set_map bsm; + tl::BitSetMask bs0; + bs0.set (0, tl::BitSetMask::True); + bs0.set (1, tl::BitSetMask::False); + tl::BitSetMask bs1; + bs1.set (0, tl::BitSetMask::False); + bs1.set (1, tl::BitSetMask::True); + bsm.insert (bs0, 1.0); + bsm.insert (bs1, 2.0); + bsm.sort (); + + AreaReceiver rec; + db::tagged_area_collector coll (bsm, rec); + ep.process (coll, coll); + + EXPECT_EQ (rec.get (), 6000000); +} + +TEST(4_PlainAreaApproximation) +{ + db::EdgeProcessor ep; + + ep.insert (db::SimplePolygon (db::Box (0, -1000, 1000, 1000)), 0); + ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000)), 0); + ep.insert (db::SimplePolygon (db::Box (500, 1000, 1500, 3000)), 1); + ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000)), 0); + ep.insert (db::SimplePolygon (db::Box (1000, 1000, 1500, 3000)), 1); + + tl::bit_set_map bsm; + bsm.insert (tl::BitSetMask (), 1.0); + bsm.sort (); + + AreaReceiver rec; + db::tagged_area_collector coll (bsm, rec); + ep.process (coll, coll); + + EXPECT_EQ (rec.get (), 4500000); +} + From 8fa308b74c0779a313e0d33264ae4a7d32a48b3e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 Aug 2024 22:17:17 +0200 Subject: [PATCH 10/15] Renaming --- src/db/db/db.pro | 4 +- ...aCollector.cc => dbBinnedAreaCollector.cc} | 2 +- ...reaCollector.h => dbBinnedAreaCollector.h} | 42 +++++++++---------- ...Tests.cc => dbBinnedAreaCollectorTests.cc} | 12 +++--- src/db/unit_tests/unit_tests.pro | 2 +- 5 files changed, 31 insertions(+), 31 deletions(-) rename src/db/db/{dbAreaCollector.cc => dbBinnedAreaCollector.cc} (95%) rename src/db/db/{dbAreaCollector.h => dbBinnedAreaCollector.h} (81%) rename src/db/unit_tests/{dbAreaCollectorTests.cc => dbBinnedAreaCollectorTests.cc} (93%) diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 83991f2c2c..48e0d9332c 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -7,8 +7,8 @@ include($$PWD/../../lib.pri) DEFINES += MAKE_DB_LIBRARY SOURCES = \ - dbAreaCollector.cc \ dbArray.cc \ + dbBinnedAreaCollector.cc \ dbBox.cc \ dbBoxConvert.cc \ dbBoxScanner.cc \ @@ -233,8 +233,8 @@ SOURCES = \ dbShapeCollectionUtils.cc HEADERS = \ - dbAreaCollector.h \ dbArray.h \ + dbBinnedAreaCollector.h \ dbBoxConvert.h \ dbBox.h \ dbBoxScanner.h \ diff --git a/src/db/db/dbAreaCollector.cc b/src/db/db/dbBinnedAreaCollector.cc similarity index 95% rename from src/db/db/dbAreaCollector.cc rename to src/db/db/dbBinnedAreaCollector.cc index 545d261998..abf9a8a7e2 100644 --- a/src/db/db/dbAreaCollector.cc +++ b/src/db/db/dbBinnedAreaCollector.cc @@ -20,7 +20,7 @@ */ -#include "dbAreaCollector.h" +#include "dbBinnedAreaCollector.h" namespace db { diff --git a/src/db/db/dbAreaCollector.h b/src/db/db/dbBinnedAreaCollector.h similarity index 81% rename from src/db/db/dbAreaCollector.h rename to src/db/db/dbBinnedAreaCollector.h index d650a33783..2de1f2c169 100644 --- a/src/db/db/dbAreaCollector.h +++ b/src/db/db/dbBinnedAreaCollector.h @@ -20,8 +20,8 @@ */ -#ifndef HDR_dbAreaCollector -#define HDR_dbAreaCollector +#ifndef HDR_dbBinnedAreaCollector +#define HDR_dbBinnedAreaCollector #include "dbCommon.h" @@ -31,12 +31,12 @@ namespace db { /** - * @brief The receiver for the tagged partial areas + * @brief The receiver for the binned partial areas * - * See description of tagged_area_collector for details. + * See description of binned_area_collector for details. */ template -class DB_PUBLIC tagged_area_receiver +class DB_PUBLIC binned_area_receiver { public: typedef db::coord_traits::area_type area_type; @@ -44,12 +44,12 @@ class DB_PUBLIC tagged_area_receiver /** * @brief Constructor */ - tagged_area_receiver () { } + binned_area_receiver () { } /** * @brief Destructor */ - virtual ~tagged_area_receiver () { } + virtual ~binned_area_receiver () { } /** * @brief This method gets called when the scanline process starts @@ -71,22 +71,22 @@ class DB_PUBLIC tagged_area_receiver * @brief A helper class providing an inserter that is the connection between the receiver and the provider */ template -class DB_PUBLIC tagged_area_inserter +class DB_PUBLIC binned_area_inserter { public: typedef db::coord_traits::area_type area_type; - tagged_area_inserter (area_type area, tagged_area_receiver *receiver) + binned_area_inserter (area_type area, binned_area_receiver *receiver) : m_area (area), mp_receiver (receiver) { // .. nothing yet .. } // methods necessary, so this object can act as an inserter - tagged_area_inserter &operator* () { return *this; } - tagged_area_inserter &operator++ (int) { return *this; } + binned_area_inserter &operator* () { return *this; } + binned_area_inserter &operator++ (int) { return *this; } - tagged_area_inserter &operator= (const Value &value) + binned_area_inserter &operator= (const Value &value) { mp_receiver->add_area (m_area, value); return *this; @@ -94,16 +94,16 @@ class DB_PUBLIC tagged_area_inserter private: area_type m_area; - tagged_area_receiver *mp_receiver; + binned_area_receiver *mp_receiver; }; /** - * @brief Provides the operation and edge receiver part of the tagged area collector + * @brief Provides the operation and edge receiver part of the binned area collector * * Use this object both as the edge operator and as an edge collector. * After running the edge processor, use "area" to obtain the area. * - * This method collects "tagged areas". That is, each field of the area divided by + * This method collects "binned areas". That is, each field of the area divided by * the edges carries a bit set which is made from the combinations of overlapping * layers. The layers are given by the property number where the number is the * bit set in the bit field. Hence, every field is associated with a bit set. @@ -111,11 +111,11 @@ class DB_PUBLIC tagged_area_inserter * The Area collector will now report the field's areas for accumulation together with * a field value that is obtained from the bit set map. As the bit set map * may deliver multiple fields, multiple such values can be present for each field. - * The areas are reported through the tagged_area_receiver object. This object + * The areas are reported through the binned_area_receiver object. This object * is supposed to add up the areas in an application specific fashion. */ template -class DB_PUBLIC tagged_area_collector +class DB_PUBLIC binned_area_collector : public EdgeEvaluatorBase, public EdgeSink { @@ -125,7 +125,7 @@ class DB_PUBLIC tagged_area_collector /** * @brief Constructor */ - tagged_area_collector (const tl::bit_set_map &bsm, tagged_area_receiver &receiver) + binned_area_collector (const tl::bit_set_map &bsm, binned_area_receiver &receiver) : mp_bsm (&bsm), mp_receiver (&receiver), m_state_one_bits (0), m_prev_one_bits (0) { // .. nothing yet .. @@ -210,17 +210,17 @@ class DB_PUBLIC tagged_area_collector { area_type partial_area = area_type (edge.p1 ().x () + edge.p2 ().x ()) * area_type (edge.dy ()) * 0.5; if (m_prev_one_bits > 0) { - mp_bsm->lookup (m_prev, tagged_area_inserter (partial_area, mp_receiver)); + mp_bsm->lookup (m_prev, binned_area_inserter (partial_area, mp_receiver)); } if (m_state_one_bits > 0) { - mp_bsm->lookup (m_state, tagged_area_inserter (-partial_area, mp_receiver)); + mp_bsm->lookup (m_state, binned_area_inserter (-partial_area, mp_receiver)); } } private: area_type m_area_sum; const tl::bit_set_map *mp_bsm; - tagged_area_receiver *mp_receiver; + binned_area_receiver *mp_receiver; tl::BitSet m_prev, m_state; std::vector m_counts; unsigned int m_state_one_bits, m_prev_one_bits; diff --git a/src/db/unit_tests/dbAreaCollectorTests.cc b/src/db/unit_tests/dbBinnedAreaCollectorTests.cc similarity index 93% rename from src/db/unit_tests/dbAreaCollectorTests.cc rename to src/db/unit_tests/dbBinnedAreaCollectorTests.cc index dd181912d6..4b73341047 100644 --- a/src/db/unit_tests/dbAreaCollectorTests.cc +++ b/src/db/unit_tests/dbBinnedAreaCollectorTests.cc @@ -20,7 +20,7 @@ */ -#include "dbAreaCollector.h" +#include "dbBinnedAreaCollector.h" #include "dbEdgeProcessor.h" #include "tlUnitTest.h" @@ -29,7 +29,7 @@ namespace { class AreaReceiver - : public db::tagged_area_receiver + : public db::binned_area_receiver { public: typedef db::coord_traits::area_type area_type; @@ -69,7 +69,7 @@ TEST(1_Basic) bsm.sort (); AreaReceiver rec; - db::tagged_area_collector coll (bsm, rec); + db::binned_area_collector coll (bsm, rec); ep.process (coll, coll); EXPECT_EQ (rec.get (), 4500000); @@ -98,7 +98,7 @@ TEST(2_ShapesGetMerged) bsm.sort (); AreaReceiver rec; - db::tagged_area_collector coll (bsm, rec); + db::binned_area_collector coll (bsm, rec); ep.process (coll, coll); EXPECT_EQ (rec.get (), 5500000); @@ -127,7 +127,7 @@ TEST(3_TouchingOnly) bsm.sort (); AreaReceiver rec; - db::tagged_area_collector coll (bsm, rec); + db::binned_area_collector coll (bsm, rec); ep.process (coll, coll); EXPECT_EQ (rec.get (), 6000000); @@ -148,7 +148,7 @@ TEST(4_PlainAreaApproximation) bsm.sort (); AreaReceiver rec; - db::tagged_area_collector coll (bsm, rec); + db::binned_area_collector coll (bsm, rec); ep.process (coll, coll); EXPECT_EQ (rec.get (), 4500000); diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 5b611c148d..74327e84f4 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -7,7 +7,7 @@ TARGET = db_tests include($$PWD/../../lib_ut.pri) SOURCES = \ - dbAreaCollectorTests.cc \ + dbBinnedAreaCollectorTests.cc \ dbCompoundOperationTests.cc \ dbFillToolTests.cc \ dbLogTests.cc \ From a2e449989eaa2c00dbb9ec9d521f558db91fd2d2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 Aug 2024 22:53:19 +0200 Subject: [PATCH 11/15] GSI binding of Region#binned_area - experimental! --- src/db/db/dbBinnedAreaCollector.cc | 99 +++++++++++++++++++++++++++++- src/db/db/dbBinnedAreaCollector.h | 5 ++ 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbBinnedAreaCollector.cc b/src/db/db/dbBinnedAreaCollector.cc index abf9a8a7e2..96599ef1b9 100644 --- a/src/db/db/dbBinnedAreaCollector.cc +++ b/src/db/db/dbBinnedAreaCollector.cc @@ -21,10 +21,107 @@ */ #include "dbBinnedAreaCollector.h" +#include "dbTypes.h" +#include "dbRegion.h" + +#include "gsiDecl.h" + +#include namespace db { - // .. nothing yet .. +namespace +{ + +class AreaReceiver + : public db::binned_area_receiver +{ +public: + typedef db::coord_traits::area_type area_type; + + AreaReceiver (unsigned int count) + { + m_areas.resize (count, 0.0); + } + + virtual void add_area (area_type area, const unsigned int &index) + { + m_areas [index] += area; + } + + const std::vector &get () const + { + return m_areas; + } + +private: + std::vector m_areas; +}; + +} + +// NOTE: this does not belong here. It is an experimental feature + +static std::vector +binned_area (const std::vector &inputs, const std::vector &vectors) +{ + db::EdgeProcessor ep; + + unsigned int index = 0; + for (auto r = inputs.begin (); r != inputs.end (); ++r, ++index) { + for (auto p = (*r)->begin (); ! p.at_end (); ++p) { + ep.insert (*p, index); + } + } + + tl::bit_set_map bsm; + index = 0; + for (auto i = vectors.begin (); i != vectors.end (); ++i, ++index) { + bsm.insert (tl::BitSetMask (*i), index); + } + bsm.sort (); + + AreaReceiver rec (index); + db::binned_area_collector coll (bsm, rec); + ep.process (coll, coll); + + return rec.get (); +} + +gsi::ClassExt extend_region_by_binned_area ( + gsi::method ("binned_area", &binned_area, gsi::arg ("inputs"), gsi::arg ("masks"), + "@brief Computes the areas of a binned decomposition of the overall region.\n" + "In this function, the overall region is decomposed into subregions with different overlap situations. " + "Each overlap case is assigned a bin using a bit mask from the 'masks' argument. " + "Each bit corresponds to one input from 'inputs' - bit 0 is the first one etc.\n" + "The masks are strings of characters 0, 1 or 'X', representing 'inside', 'outside' and " + "'any' for the respective input. The first character represents the first input, the second the second input etc.\n" + "Missing characters are treated as 'any', so the empty string matches every situation.\n" + "\n" + "The result is a vector of accumulated areas for each bin identified by one mask. " + "Bins may overlay if multiple masks match, so the total sum of areas is not necessarily " + "identical to the total area. A bin with an empty string mask will deliver the total area.\n" + "\n" + "Merge semantics always applies - i.e. all shapes inside the regions are conceptually " + "merged in 'positive wrap count' mode before computing the area. Hence overlapping shapes "" + "per input region just count once.\n" + "\n" + "Example:\n" + "\n" + "@code\n" + "r1 = RBA::Region::new\n" + "r1.insert(RBA::Box::new(0, 0, 1000, 2000))\n" + "\n" + "r2 = RBA::Region::new\n" + "r2.insert(RBA::Box::new(500, 1000, 1500, 3000))\n" + "\n" + "areas = RBA::Region::binned_area([ r1, r2 ], [ \"10\", \"01\", \"\" ])\n" + "r1_not_r2, r2_not_r1, all = areas\n" + "@/code\n" + "\n" + "This feature is highly experimental." + ) +); } diff --git a/src/db/db/dbBinnedAreaCollector.h b/src/db/db/dbBinnedAreaCollector.h index 2de1f2c169..2d3756ccc2 100644 --- a/src/db/db/dbBinnedAreaCollector.h +++ b/src/db/db/dbBinnedAreaCollector.h @@ -206,6 +206,11 @@ class DB_PUBLIC binned_area_collector mp_receiver->finish (); } + virtual void put (const db::Edge &, int) + { + // not used. + } + virtual void put (const db::Edge &edge) { area_type partial_area = area_type (edge.p1 ().x () + edge.p2 ().x ()) * area_type (edge.dy ()) * 0.5; From ccf5314fdb539bf3fd86328bb26a63a444e287a0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 Aug 2024 22:54:37 +0200 Subject: [PATCH 12/15] Typo fixed --- src/db/db/dbBinnedAreaCollector.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/dbBinnedAreaCollector.cc b/src/db/db/dbBinnedAreaCollector.cc index 96599ef1b9..590ea72fb2 100644 --- a/src/db/db/dbBinnedAreaCollector.cc +++ b/src/db/db/dbBinnedAreaCollector.cc @@ -104,7 +104,7 @@ gsi::ClassExt extend_region_by_binned_area ( "identical to the total area. A bin with an empty string mask will deliver the total area.\n" "\n" "Merge semantics always applies - i.e. all shapes inside the regions are conceptually " - "merged in 'positive wrap count' mode before computing the area. Hence overlapping shapes "" + "merged in 'positive wrap count' mode before computing the area. Hence overlapping shapes " "per input region just count once.\n" "\n" "Example:\n" From 36002c38b81e89d074747c7b68cee813c8ddcdee Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 7 Sep 2024 17:52:24 +0200 Subject: [PATCH 13/15] Adding a header that was missing --- src/tl/tl/tlBitSetMap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tl/tl/tlBitSetMap.h b/src/tl/tl/tlBitSetMap.h index 3928bfa7ef..17f08dc8bc 100644 --- a/src/tl/tl/tlBitSetMap.h +++ b/src/tl/tl/tlBitSetMap.h @@ -30,6 +30,7 @@ #include "tlAssert.h" #include +#include namespace tl { From 66ee87b725a069a374c25e872c1ef8654daa9da1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 8 Sep 2024 10:08:43 +0200 Subject: [PATCH 14/15] Fixing Windows builds --- src/tl/tl/tlBitSetMap.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tl/tl/tlBitSetMap.h b/src/tl/tl/tlBitSetMap.h index 17f08dc8bc..fbb94f88a8 100644 --- a/src/tl/tl/tlBitSetMap.h +++ b/src/tl/tl/tlBitSetMap.h @@ -36,7 +36,7 @@ namespace tl { template -struct TL_PUBLIC bit_set_mask_node +struct TL_PUBLIC_TEMPLATE bit_set_mask_node { bit_set_mask_node () : mask (), next (0), value () @@ -56,7 +56,7 @@ struct TL_PUBLIC bit_set_mask_node }; template -class TL_PUBLIC bit_set_mask_compare +class TL_PUBLIC_TEMPLATE bit_set_mask_compare { public: bit_set_mask_compare (tl::BitSetMask::index_type bit, tl::BitSetMask::mask_type mask) @@ -103,7 +103,7 @@ namespace tl * "insert" and do a "sort" before using "lookup". */ template -class TL_PUBLIC bit_set_map +class TL_PUBLIC_TEMPLATE bit_set_map { public: typedef std::vector > node_list; From 4eaae15ce0b3c2eff544cf7b735dcc41ed9c17fa Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 8 Sep 2024 17:20:43 +0200 Subject: [PATCH 15/15] Fixing Windows builds, another step --- src/db/db/dbBinnedAreaCollector.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbBinnedAreaCollector.h b/src/db/db/dbBinnedAreaCollector.h index 2d3756ccc2..0d742b7df1 100644 --- a/src/db/db/dbBinnedAreaCollector.h +++ b/src/db/db/dbBinnedAreaCollector.h @@ -36,7 +36,7 @@ namespace db { * See description of binned_area_collector for details. */ template -class DB_PUBLIC binned_area_receiver +class DB_PUBLIC_TEMPLATE binned_area_receiver { public: typedef db::coord_traits::area_type area_type; @@ -71,7 +71,7 @@ class DB_PUBLIC binned_area_receiver * @brief A helper class providing an inserter that is the connection between the receiver and the provider */ template -class DB_PUBLIC binned_area_inserter +class DB_PUBLIC_TEMPLATE binned_area_inserter { public: typedef db::coord_traits::area_type area_type; @@ -115,7 +115,7 @@ class DB_PUBLIC binned_area_inserter * is supposed to add up the areas in an application specific fashion. */ template -class DB_PUBLIC binned_area_collector +class DB_PUBLIC_TEMPLATE binned_area_collector : public EdgeEvaluatorBase, public EdgeSink {