From 1890a17544b1292bd488cd0c355a9bc64224cbf2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 Aug 2024 23:12:53 +0200 Subject: [PATCH] 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 db1ddc47e..3928bfa7e 100644 --- a/src/tl/tl/tlBitSetMap.h +++ b/src/tl/tl/tlBitSetMap.h @@ -208,7 +208,10 @@ public: 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 @@ public: * 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 @@ private: // 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 @@ private: // 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 @@ private: } 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 @@ private: 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 000000000..272a31428 --- /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 a65a773e7..7ec4203dd 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 \