mirror of https://github.com/KLayout/klayout.git
Merge 4eaae15ce0 into 564111af77
This commit is contained in:
commit
07fcd4f652
|
|
@ -8,6 +8,7 @@ DEFINES += MAKE_DB_LIBRARY
|
|||
|
||||
SOURCES = \
|
||||
dbArray.cc \
|
||||
dbBinnedAreaCollector.cc \
|
||||
dbBox.cc \
|
||||
dbBoxConvert.cc \
|
||||
dbBoxScanner.cc \
|
||||
|
|
@ -245,6 +246,7 @@ SOURCES = \
|
|||
|
||||
HEADERS = \
|
||||
dbArray.h \
|
||||
dbBinnedAreaCollector.h \
|
||||
dbBoxConvert.h \
|
||||
dbBox.h \
|
||||
dbBoxScanner.h \
|
||||
|
|
|
|||
|
|
@ -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 <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class AreaReceiver
|
||||
: public db::binned_area_receiver<unsigned int>
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<db::Coord>::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<area_type> &get () const
|
||||
{
|
||||
return m_areas;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<area_type> m_areas;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// NOTE: this does not belong here. It is an experimental feature
|
||||
|
||||
static std::vector<AreaReceiver::area_type>
|
||||
binned_area (const std::vector<const db::Region *> &inputs, const std::vector<std::string> &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<unsigned int> 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<unsigned int> coll (bsm, rec);
|
||||
ep.process (coll, coll);
|
||||
|
||||
return rec.get ();
|
||||
}
|
||||
|
||||
gsi::ClassExt<db::Region> 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."
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -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 Value>
|
||||
class DB_PUBLIC_TEMPLATE binned_area_receiver
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<db::Coord>::area_type area_type;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
binned_area_receiver<Value> () { }
|
||||
|
||||
/**
|
||||
* @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 Value>
|
||||
class DB_PUBLIC_TEMPLATE binned_area_inserter
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<db::Coord>::area_type area_type;
|
||||
|
||||
binned_area_inserter<Value> (area_type area, binned_area_receiver<Value> *receiver)
|
||||
: m_area (area), mp_receiver (receiver)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
// methods necessary, so this object can act as an inserter
|
||||
binned_area_inserter<Value> &operator* () { return *this; }
|
||||
binned_area_inserter<Value> &operator++ (int) { return *this; }
|
||||
|
||||
binned_area_inserter<Value> &operator= (const Value &value)
|
||||
{
|
||||
mp_receiver->add_area (m_area, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
area_type m_area;
|
||||
binned_area_receiver<Value> *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 Value>
|
||||
class DB_PUBLIC_TEMPLATE binned_area_collector
|
||||
: public EdgeEvaluatorBase,
|
||||
public EdgeSink
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<db::Coord>::area_type area_type;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
binned_area_collector<Value> (const tl::bit_set_map<Value> &bsm, binned_area_receiver<Value> &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<Value> (partial_area, mp_receiver));
|
||||
}
|
||||
if (m_state_one_bits > 0) {
|
||||
mp_bsm->lookup (m_state, binned_area_inserter<Value> (-partial_area, mp_receiver));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
area_type m_area_sum;
|
||||
const tl::bit_set_map<Value> *mp_bsm;
|
||||
binned_area_receiver<Value> *mp_receiver;
|
||||
tl::BitSet m_prev, m_state;
|
||||
std::vector<int> m_counts;
|
||||
unsigned int m_state_one_bits, m_prev_one_bits;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1670,6 +1670,7 @@ public:
|
|||
void next_coincident ()
|
||||
{
|
||||
m_pn = m_ps = 0;
|
||||
mp_op->begin_group ();
|
||||
}
|
||||
|
||||
void end_coincident ()
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@ public:
|
|||
|
||||
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; }
|
||||
|
|
|
|||
|
|
@ -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<double>
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<db::Coord>::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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> bsm;
|
||||
bsm.insert (tl::BitSetMask (), 1.0);
|
||||
bsm.sort ();
|
||||
|
||||
AreaReceiver rec;
|
||||
db::binned_area_collector<double> coll (bsm, rec);
|
||||
ep.process (coll, coll);
|
||||
|
||||
EXPECT_EQ (rec.get (), 4500000);
|
||||
}
|
||||
|
||||
|
|
@ -7,6 +7,7 @@ TARGET = db_tests
|
|||
include($$PWD/../../lib_ut.pri)
|
||||
|
||||
SOURCES = \
|
||||
dbBinnedAreaCollectorTests.cc \
|
||||
dbCompoundOperationTests.cc \
|
||||
dbEdgeNeighborhoodTests.cc \
|
||||
dbFillToolTests.cc \
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <cstdint>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
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<bool>, 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 <class Iter>
|
||||
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 <class Iter>
|
||||
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 <class Iter>
|
||||
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 <class Iter>
|
||||
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
|
||||
|
|
@ -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 ..
|
||||
}
|
||||
|
|
@ -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 <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template <class Value>
|
||||
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<Value> &other)
|
||||
{
|
||||
std::swap (mask, other.mask);
|
||||
std::swap (next, other.next);
|
||||
std::swap (value, other.value);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Value>
|
||||
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<Value> &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 <class Value>
|
||||
inline void
|
||||
swap (tl::bit_set_mask_node<Value> &a, tl::bit_set_mask_node<Value> &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 Value>
|
||||
class TL_PUBLIC_TEMPLATE bit_set_map
|
||||
{
|
||||
public:
|
||||
typedef std::vector<bit_set_mask_node<Value> > 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<Value> ());
|
||||
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 <class 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;
|
||||
|
||||
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<Value> (bit, tl::BitSetMask::False));
|
||||
auto middle_true = std::partition (middle_false, to, tl::bit_set_mask_compare<Value> (bit, tl::BitSetMask::True));
|
||||
auto middle_never = std::partition (middle_true, to, tl::bit_set_mask_compare<Value> (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 <class 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;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <cstdint>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
|
|
@ -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 <iostream>
|
||||
|
||||
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<int> &s) : ps (&s) { }
|
||||
|
||||
SetInserter &operator++(int) { return *this; }
|
||||
SetInserter &operator* () { return *this; }
|
||||
|
||||
SetInserter &operator= (int v) { ps->insert (v); return *this; }
|
||||
|
||||
std::set<int> *ps;
|
||||
};
|
||||
|
||||
static std::string s2s (const std::set<int> &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<int> &bsm, const tl::BitSet &bs)
|
||||
{
|
||||
std::set<int> values;
|
||||
bsm.lookup (bs, SetInserter (values));
|
||||
return s2s (values);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
TEST(1_Basic)
|
||||
{
|
||||
tl::bit_set_map<int> 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<int> 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<int> 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<tl::BitSet> 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<std::string> 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<int> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 \
|
||||
|
|
|
|||
Loading…
Reference in New Issue