diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 299f029e3..48e0d9332 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -8,6 +8,7 @@ DEFINES += MAKE_DB_LIBRARY SOURCES = \ dbArray.cc \ + dbBinnedAreaCollector.cc \ dbBox.cc \ dbBoxConvert.cc \ dbBoxScanner.cc \ @@ -233,6 +234,7 @@ SOURCES = \ HEADERS = \ dbArray.h \ + dbBinnedAreaCollector.h \ dbBoxConvert.h \ dbBox.h \ dbBoxScanner.h \ diff --git a/src/db/db/dbBinnedAreaCollector.cc b/src/db/db/dbBinnedAreaCollector.cc new file mode 100644 index 000000000..590ea72fb --- /dev/null +++ b/src/db/db/dbBinnedAreaCollector.cc @@ -0,0 +1,127 @@ + +/* + + 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 "dbBinnedAreaCollector.h" +#include "dbTypes.h" +#include "dbRegion.h" + +#include "gsiDecl.h" + +#include + +namespace db +{ + +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 new file mode 100644 index 000000000..0d742b7df --- /dev/null +++ b/src/db/db/dbBinnedAreaCollector.h @@ -0,0 +1,237 @@ + +/* + + 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_dbBinnedAreaCollector +#define HDR_dbBinnedAreaCollector + +#include "dbCommon.h" + +#include "dbEdgeProcessor.h" +#include "tlBitSetMap.h" + +namespace db { + +/** + * @brief The receiver for the binned partial areas + * + * See description of binned_area_collector for details. + */ +template +class DB_PUBLIC_TEMPLATE binned_area_receiver +{ +public: + typedef db::coord_traits::area_type area_type; + + /** + * @brief Constructor + */ + binned_area_receiver () { } + + /** + * @brief Destructor + */ + virtual ~binned_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_TEMPLATE binned_area_inserter +{ +public: + typedef db::coord_traits::area_type area_type; + + 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 + binned_area_inserter &operator* () { return *this; } + binned_area_inserter &operator++ (int) { return *this; } + + binned_area_inserter &operator= (const Value &value) + { + mp_receiver->add_area (m_area, value); + return *this; + } + +private: + area_type m_area; + binned_area_receiver *mp_receiver; +}; + +/** + * @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 "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. + * + * 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 binned_area_receiver object. This object + * is supposed to add up the areas in an application specific fashion. + */ +template +class DB_PUBLIC_TEMPLATE binned_area_collector + : public EdgeEvaluatorBase, + public EdgeSink +{ +public: + typedef db::coord_traits::area_type area_type; + + /** + * @brief Constructor + */ + 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 .. + } + + // implementation of EdgeEvaluatorBase + virtual void reset () + { + 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) + { + if (north) { + + while (m_counts.size () <= p) { + m_counts.push_back (0); + } + + int &count = m_counts [p]; + 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; + } + } + + // this will call "put" when the group is finished + return 1; + + } else { + return 0; + } + } + + virtual bool is_reset () const + { + return m_state_one_bits == 0; + } + + 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 &, 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; + if (m_prev_one_bits > 0) { + mp_bsm->lookup (m_prev, binned_area_inserter (partial_area, mp_receiver)); + } + if (m_state_one_bits > 0) { + 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; + 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; +}; + +} + +#endif + diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 4f0534ae8..a452cf480 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 ff9f74c5f..36a450125 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/dbBinnedAreaCollectorTests.cc b/src/db/unit_tests/dbBinnedAreaCollectorTests.cc new file mode 100644 index 000000000..4b7334104 --- /dev/null +++ b/src/db/unit_tests/dbBinnedAreaCollectorTests.cc @@ -0,0 +1,156 @@ + +/* + + 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 "dbBinnedAreaCollector.h" +#include "dbEdgeProcessor.h" +#include "tlUnitTest.h" + + +namespace +{ + +class AreaReceiver + : public db::binned_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_Basic) +{ + 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; + 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::binned_area_collector coll (bsm, rec); + ep.process (coll, coll); + + 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::binned_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::binned_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::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 a5ef9690e..74327e84f 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 = \ + dbBinnedAreaCollectorTests.cc \ dbCompoundOperationTests.cc \ dbFillToolTests.cc \ dbLogTests.cc \ diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index 7d1cf0c15..c7d4207ed 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 \ + tlBitSetMask.cc \ tlColor.cc \ tlClassRegistry.cc \ tlCopyOnWrite.cc \ @@ -62,6 +65,9 @@ HEADERS = \ tlAlgorithm.h \ tlAssert.h \ tlBase64.h \ + tlBitSet.h \ + tlBitSetMap.h \ + tlBitSetMask.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 000000000..45928e078 --- /dev/null +++ b/src/tl/tl/tlBitSet.cc @@ -0,0 +1,244 @@ + +/* + + 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 () + : 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) +{ + 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 000000000..f88530315 --- /dev/null +++ b/src/tl/tl/tlBitSet.h @@ -0,0 +1,251 @@ + +/* + + 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 +#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 + * allocated size are treated as "false" or zero. + */ +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 (); + + /** + * @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 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); + + /** + * @brief Move constructor + */ + BitSet (BitSet &&other); + + /** + * @brief Converts the bit set to a string + */ + std::string to_string () const; + + /** + * @brief Destructor + */ + ~BitSet (); + + /** + * @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: + friend class BitSetMask; + + data_type *mp_data; + size_type m_size; +}; + +} + +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 new file mode 100644 index 000000000..8c759eb93 --- /dev/null +++ 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 new file mode 100644 index 000000000..fbb94f88a --- /dev/null +++ b/src/tl/tl/tlBitSetMap.h @@ -0,0 +1,374 @@ + +/* + + 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 +#include + +namespace tl +{ + +template +struct TL_PUBLIC_TEMPLATE 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_TEMPLATE 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_TEMPLATE 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) { + if (! m_nodes.empty ()) { + m_nodes.front ().next = m_nodes.size (); + 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) 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; + + 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->mask != from->mask) { + 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) const + { + 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 == 0) { + break; + } + i += i->next; + } + + return any; + } +}; + +} + +#endif diff --git a/src/tl/tl/tlBitSetMask.cc b/src/tl/tl/tlBitSetMask.cc new file mode 100644 index 000000000..229e0a8b4 --- /dev/null +++ b/src/tl/tl/tlBitSetMask.cc @@ -0,0 +1,373 @@ + +/* + + 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 () + : 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) +{ + 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 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; ++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 & ~*d1 & d) != 0) { + return false; + } + // A "false" in place of "true expected" gives "no match" + if ((*d1 & ~*d0 & ~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 000000000..14f75257d --- /dev/null +++ b/src/tl/tl/tlBitSetMask.h @@ -0,0 +1,198 @@ + +/* + + 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 +#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 + * allocated 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 (); + + /** + * @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); + + /** + * @brief Move constructor + */ + BitSetMask (BitSetMask &&other); + + /** + * @brief Destructor + */ + ~BitSetMask (); + + /** + * @brief Converts the mask to a string + */ + std::string to_string () const; + + /** + * @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; +}; + +} + +namespace std +{ + +inline void +swap (tl::BitSetMask &a, tl::BitSetMask &b) +{ + a.swap (b); +} + +} + +#endif diff --git a/src/tl/unit_tests/tlBitSetMapTests.cc b/src/tl/unit_tests/tlBitSetMapTests.cc new file mode 100644 index 000000000..e0ca46a2a --- /dev/null +++ b/src/tl/unit_tests/tlBitSetMapTests.cc @@ -0,0 +1,218 @@ + +/* + + 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" + +#include + +static tl::BitSet bs (const char *s) +{ + return tl::BitSet (s); +} + +static tl::BitSetMask bsm (const char *s) +{ + return tl::BitSetMask (s); +} + +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 s2s (const std::set &values) +{ + std::string res; + for (auto i = values.begin (); i != values.end (); ++i) { + if (!res.empty ()) { + res += ","; + } + res += tl::to_string (*i); + } + 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 +{ + +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)); + } + } +} + +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); +} + +} diff --git a/src/tl/unit_tests/tlBitSetMaskTests.cc b/src/tl/unit_tests/tlBitSetMaskTests.cc new file mode 100644 index 000000000..d2056e255 --- /dev/null +++ b/src/tl/unit_tests/tlBitSetMaskTests.cc @@ -0,0 +1,304 @@ + +/* + + 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) +{ + return s.to_string (); +} + +static tl::BitSet bs (const char *s) +{ + return tl::BitSet (s); +} + +TEST(1_Basic) +{ + tl::BitSetMask bs; + 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); + 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"); +} + +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 +} + +} diff --git a/src/tl/unit_tests/tlBitSetTests.cc b/src/tl/unit_tests/tlBitSetTests.cc new file mode 100644 index 000000000..f5e84fa9c --- /dev/null +++ b/src/tl/unit_tests/tlBitSetTests.cc @@ -0,0 +1,209 @@ + +/* + + 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) +{ + return s.to_string (); +} + +TEST(1_Basic) +{ + tl::BitSet bs; + 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); + 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 3d8b06676..7ec4203dd 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -9,6 +9,9 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ tlAlgorithmTests.cc \ tlBase64Tests.cc \ + tlBitSetMapTests.cc \ + tlBitSetMaskTests.cc \ + tlBitSetTests.cc \ tlClassRegistryTests.cc \ tlCommandLineParserTests.cc \ tlColorTests.cc \