[mathic] 14/62: Added PairQueue to Mathic along with tests. Useful for ordering S-pairs.
Doug Torrance
dtorrance-guest at moszumanska.debian.org
Wed Apr 1 11:36:18 UTC 2015
This is an automated email from the git hooks/post-receive script.
dtorrance-guest pushed a commit to branch master
in repository mathic.
commit a719044a79ab9731e98c67c2e596895f1132dad1
Author: Bjarke Hammersholt Roune <bjarkehr.code at gmail.com>
Date: Sat Jul 14 17:38:12 2012 -0400
Added PairQueue to Mathic along with tests. Useful for ordering S-pairs.
---
Makefile.am | 6 +-
src/mathic/PairQueue.cpp | 8 +
src/mathic/PairQueue.h | 402 +++++++++++++++++++++++++++++++++++++++++++++++
src/mathic/TourTree.h | 12 +-
src/mathic/stdinc.h | 11 +-
src/test/PairQueue.cpp | 307 ++++++++++++++++++++++++++++++++++++
6 files changed, 736 insertions(+), 10 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 5707da7..ac64edb 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -26,7 +26,8 @@ libmathic_ at MATHIC_API_VERSION@_la_SOURCES = src/mathic/Timer.cpp \
src/mathic/ElementDeleter.cpp src/mathic/NameFactory.cpp \
src/mathic/error.cpp src/mathic/HelpAction.cpp \
src/mathic/IntegerParameter.cpp src/mathic/StringParameter.cpp \
- src/mathic/display.cpp src/mathic/BitTriangle.cpp
+ src/mathic/display.cpp src/mathic/BitTriangle.cpp \
+ src/mathic/PairQueue.cpp
# The headers that libmathic installs.
# Normally, automake strips the path from the files when installing them,
@@ -95,4 +96,5 @@ unittest_LDFLAGS= $(top_builddir)/libmathic-$(MATHIC_API_VERSION).la
test_LIBS=
unittest_SOURCES=src/test/DivFinder.cpp src/test/gtestInclude.cpp \
- src/test/testMain.cpp src/test/BitTriangle.cpp
+ src/test/testMain.cpp src/test/BitTriangle.cpp \
+ src/test/PairQueue.cpp
diff --git a/src/mathic/PairQueue.cpp b/src/mathic/PairQueue.cpp
new file mode 100755
index 0000000..71ad1bf
--- /dev/null
+++ b/src/mathic/PairQueue.cpp
@@ -0,0 +1,8 @@
+#include "stdinc.h"
+#include "PairQueue.h"
+
+#include <stdexcept>
+
+namespace mathic {
+
+}
diff --git a/src/mathic/PairQueue.h b/src/mathic/PairQueue.h
new file mode 100755
index 0000000..4cde729
--- /dev/null
+++ b/src/mathic/PairQueue.h
@@ -0,0 +1,402 @@
+#ifndef MATHIC_PAIR_QUEUE_GUARD
+#define MATHIC_PAIR_QUEUE_GUARD
+
+#include "stdinc.h"
+
+#include "TourTree.h"
+
+#include <memtailor.h>
+#include <limits>
+#include <iterator>
+#include <vector>
+#include <algorithm>
+#include <stdexcept>
+
+namespace mathic {
+ // A priority queue of integer pairs (col, row) with custom
+ // comparison function via template. Pairs must be added to the data
+ // structure one column index at a time, such as will be the case
+ // for S-pairs in Buchberger's algorithm.
+ //
+ // Think of the data structure as storing a subset of the following
+ // triangle
+ //
+ // row
+ // 3| x
+ // 2| x x
+ // 1| x x x
+ // 0| x x x x
+ // ---------
+ // 0 1 2 3 4 col
+ //
+ // As you can see in this triangle, any pair (col, row) must have
+ // col > row.
+ //
+ // The custom comparison is not directly between pairs but between
+ // triples (col, row, pd) where pd is extra information that is
+ // computed as a function pairData of the pair (col, row). The
+ // purpose of this is that pd=pairData(col, row) can contain
+ // information that is necessary to compare two pairs but that is
+ // expensive to compute. Storing pd then avoids the need to
+ // recompute pd on each pair when comparing two pairs. If you do not
+ // need pd then just let pairData compute an empty struct.
+ //
+ // The data structure stores only one pd value per column to cut
+ // down on the memory cost associated to storing many pd's. The pd
+ // value of a pair is computed exactly twice. This is a speed-memory
+ // trade-off between the extremes of storing no pd's (slow due to
+ // frequent recomputations) and storing a pd for every pair (can
+ // take a lot of memory).
+ //
+ // The data structure is the S-pair queue that is described from a
+ // high level in the paper "Practical Grobner Basis Computation"
+ // that is available at http://arxiv.org/abs/1206.6940
+ template<class Configuration>
+ class PairQueue;
+
+ template<class C>
+ class PairQueue {
+ public:
+ typedef C Configuration;
+ typedef typename C::PairData PairData;
+ typedef unsigned int Index;
+ struct ColumnEntry {
+ ColumnEntry(const PairData& pd, Index r): pairData(pd), row(r) {}
+ PairData pairData;
+ Index row;
+ };
+
+ // PairQueue stores a copy of the passed in configuration.
+ PairQueue(const Configuration& conf);
+ ~PairQueue();
+
+ // Returns the stored configuration.
+ Configuration& configuration() {return mConf;}
+
+ // Returns the stored configuration.
+ Configuration const& configuration() const {return mConf;}
+
+ // Returns how many columns the triangle has. O(1) time.
+ size_t columnCount() const {return mColumnCount;}
+
+ // Returns how many pairs are in the triangle. O(columnCount())
+ // time.
+ size_t size() const;
+
+ // Returns true if there are no pairs in the triangle. O(1) time.
+ bool empty() const;
+
+ // As addColumn, but the range [sortedRowsBegin, sortedRowsEnd)
+ // must be sorted in weakly descending order according to the
+ // custom sorting function.
+ template<class Iter>
+ void addColumnDescending(Iter rowsBegin, Iter rowsEnd);
+
+ // Returns the maximal pair according to the custom ordering on
+ // pairs.
+ std::pair<size_t, size_t> topPair() const;
+
+ // Returns the PairData of topPair().
+ const PairData& topPairData() const;
+
+ // Removes topPair() from the data structure.
+ void pop();
+
+ // Returns how many bytes of memory this data structure consumes
+ // not including sizeof(*this).
+ size_t getMemoryUse() const;
+
+ // Returns a string that describes how this data structure was
+ // configured.
+ std::string name() const;
+
+ private:
+ typedef unsigned short SmallIndex;
+
+ class Column {
+ public:
+ template<class Iter>
+ Column
+ (Index col, Iter rowsBegin, Iter rowsEnd, C& conf, memt::Arena& arena);
+
+ const PairData& pairData() {MATHIC_ASSERT(!empty()); return mPairData;}
+ Index columnIndex() const {return mColumnIndex;}
+ Index rowIndex() const;
+
+ void incrementRowIndex(C& conf); // recomputes pairData if not empty
+ bool empty() const;
+ size_t size() const; // number of pairs remaining in this column
+
+ private:
+ PairData mPairData; // pairData of (columnIndex(), rowIndex())
+ Index const mColumnIndex; // all pairs here have this column index
+
+ bool big() const; // returns true if we need to use big part of union
+ union { // the current row index is *begin
+ Index* bigBegin;
+ SmallIndex* smallBegin;
+ };
+ union { // the row indices lie in [begin, end)
+ Index* bigEnd;
+ SmallIndex* smallEnd;
+ };
+ };
+
+ class ColumnSizeSummer {
+ public:
+ ColumnSizeSummer(): mSizeSum(0) {}
+ size_t sizeSum() const {return mSizeSum;}
+ bool proceed(Column const* const column) {
+ mSizeSum += column->size();
+ return true;
+ }
+ private:
+ size_t mSizeSum;
+ };
+
+ class ColumnDestructor {
+ public:
+ bool proceed(Column* const column) {
+ column->~Column();
+ return true;
+ }
+ };
+
+ class QueueConfiguration : TourTreeSuggestedOptions {
+ public:
+ QueueConfiguration(Configuration& conf): mConf(conf) {}
+
+ typedef Column* Entry;
+
+ typedef typename C::CompareResult CompareResult;
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ return mConf.compare(a->columnIndex(), a->rowIndex(), a->pairData(),
+ b->columnIndex(), b->rowIndex(), b->pairData());
+ }
+ bool cmpLessThan(CompareResult cr) const {
+ return mConf.cmpLessThan(cr);
+ }
+
+ private:
+ Configuration& mConf;
+ };
+ typedef TourTree<QueueConfiguration> ColumnQueue;
+
+ ColumnQueue mColumnQueue;
+ size_t mColumnCount;
+ std::vector<Column*> mColumns;
+ memt::Arena mArena;
+ memt::Arena mScratchArena;
+ Configuration mConf;
+ std::vector<ColumnEntry> mColumnEntries;
+ };
+
+ //// Implementation
+ template<class C>
+ template<class Iter>
+ PairQueue<C>::Column::Column
+ (Index const col,
+ Iter const rowsBegin, Iter const rowsEnd,
+ C& conf,
+ memt::Arena& arena):
+ mColumnIndex(col)
+ {
+#ifdef MATHIC_DEBUG
+ // check that the passed in range is weakly descending according
+ // to the custom order.
+ if (rowsBegin != rowsEnd) {
+ Iter prevIt = rowsBegin;
+ Iter it = rowsBegin;
+ PairData prevPd;
+ conf.computePairData(columnIndex(), *rowsBegin, prevPd);
+ PairData currentPd;
+ for (++it; it != rowsEnd; ++it, ++prevIt) {
+ conf.computePairData(columnIndex(), *it, currentPd);
+ // check prev >= current, which is equivalent to !(prev < current)
+ MATHIC_ASSERT
+ (!conf.cmpLessThan(conf.compare(columnIndex(), *prevIt, prevPd,
+ columnIndex(), *it, currentPd)));
+ // !(currentPd < prevPd)
+ prevPd = currentPd;
+ }
+ }
+#endif
+
+ size_t const entryCount = std::distance(rowsBegin, rowsEnd);
+ if (big()) {
+ std::pair<Index*, Index*> const range =
+ arena.allocArrayNoCon<Index>(entryCount);
+ bigBegin = range.first;
+ bigEnd = range.second;
+ Index* rangeIt = range.first;
+ Iter rowsIt = rowsBegin;
+ for (; rangeIt != range.second; ++rangeIt, ++rowsIt) {
+ MATHIC_ASSERT(rowsIt != rowsEnd);
+ MATHIC_ASSERT(*rowsIt < mColumnIndex);
+ MATHIC_ASSERT(*rowsIt < std::numeric_limits<Index>::max());
+ *rangeIt = static_cast<Index>(*rowsIt);
+ }
+ MATHIC_ASSERT(rowsIt == rowsEnd);
+ bigBegin = range.first;
+ bigEnd = range.second;
+ } else {
+ std::pair<SmallIndex*, SmallIndex*> range =
+ arena.allocArrayNoCon<SmallIndex>(entryCount);
+ smallBegin = range.first;
+ smallEnd = range.second;
+ SmallIndex* rangeIt = range.first;
+ Iter rowsIt = rowsBegin;
+ for (; rangeIt != range.second; ++rangeIt, ++rowsIt) {
+ MATHIC_ASSERT(rowsIt != rowsEnd);
+ MATHIC_ASSERT(*rowsIt < mColumnIndex);
+ MATHIC_ASSERT(*rowsIt < std::numeric_limits<SmallIndex>::max());
+ *rangeIt = static_cast<SmallIndex>(*rowsIt);
+ }
+ }
+ MATHIC_ASSERT(size() == entryCount);
+ MATHIC_ASSERT(empty() == (entryCount == 0));
+ conf.computePairData(columnIndex(), rowIndex(), mPairData);
+ }
+
+ template<class C>
+ typename PairQueue<C>::Index PairQueue<C>::Column::rowIndex() const {
+ MATHIC_ASSERT(!empty());
+ if (big())
+ return *bigBegin;
+ else
+ return *smallBegin;
+ }
+
+ template<class C>
+ void PairQueue<C>::Column::incrementRowIndex(C& conf) {
+ MATHIC_ASSERT(!empty());
+ if (big())
+ ++bigBegin;
+ else
+ ++smallBegin;
+ if (!empty())
+ conf.computePairData(columnIndex(), rowIndex(), mPairData);
+ }
+
+ template<class C>
+ bool PairQueue<C>::Column::empty() const {
+ if (big())
+ return bigBegin == bigEnd;
+ else
+ return smallBegin == smallEnd;
+ }
+
+ template<class C>
+ size_t PairQueue<C>::Column::size() const {
+ if (big())
+ return bigEnd - bigBegin;
+ else
+ return smallEnd - smallBegin;
+ }
+
+ template<class C>
+ bool PairQueue<C>::Column::big() const {
+ return columnIndex() >
+ static_cast<size_t>(std::numeric_limits<SmallIndex>::max());
+ }
+
+ template<class C>
+ PairQueue<C>::PairQueue(const Configuration& conf):
+ mConf(conf),
+ mColumnQueue(QueueConfiguration(mConf)),
+ mColumnCount(0) {
+ }
+
+ template<class C>
+ PairQueue<C>::~PairQueue() {
+ ColumnDestructor destructor;
+ mColumnQueue.forAll(destructor);
+ }
+
+ template<class C>
+ bool PairQueue<C>::empty() const {
+ MATHIC_ASSERT(mColumnQueue.empty() || !mColumnQueue.top()->empty());
+ return mColumnQueue.empty();
+ }
+
+ template<class C>
+ size_t PairQueue<C>::size() const {
+ ColumnSizeSummer summer;
+ mColumnQueue.forAll(summer);
+ return summer.sizeSum();
+ }
+
+ template<class C>
+ template<class Iter>
+ void PairQueue<C>::addColumnDescending
+ (Iter const sortedRowsBegin, Iter const sortedRowsEnd) {
+ if (mColumnCount >= std::numeric_limits<Index>::max())
+ throw std::overflow_error("Too large column index in PairQueue.");
+ Index const newColumnIndex = static_cast<Index>(mColumnCount);
+ ++mColumnCount;
+ if (sortedRowsBegin == sortedRowsEnd)
+ return;
+ mColumns.reserve(mColumns.size() + 1);
+ memt::Arena::Guard guard(mArena);
+
+ Column* column = new (mArena.allocObjectNoCon<Column>())
+ Column(newColumnIndex, sortedRowsBegin, sortedRowsEnd, mConf, mArena);
+
+ try {
+ mColumnQueue.push(column);
+ } catch (...) {
+ column->~Column();
+ throw;
+ }
+ mColumns.push_back(column); // does not throw due to reserve above
+ guard.release();
+ }
+
+ template<class C>
+ void PairQueue<C>::pop() {
+ MATHIC_ASSERT(!empty());
+
+ Column* const topColumn = mColumnQueue.top();
+ MATHIC_ASSERT(topColumn != 0);
+ MATHIC_ASSERT(!topColumn->empty());
+
+ // Note that all mathic queues allow doing this sequence of
+ // actions: top(), change top element in-place, do decreaseTop/pop.
+ topColumn->incrementRowIndex(mConf);
+ if (topColumn->empty()) {
+ topColumn->~Column();
+ mColumnQueue.pop();
+ } else
+ mColumnQueue.decreaseTop(topColumn);
+ }
+
+ template<class C>
+ size_t PairQueue<C>::getMemoryUse() const {
+ return mArena.getMemoryUse() + mColumnQueue.getMemoryUse();
+ }
+
+ template<class C>
+ std::string PairQueue<C>::name() const {
+ return std::string("PairQueue-") + mColumnQueue.getName();
+ }
+
+ template<class C>
+ std::pair<size_t, size_t> PairQueue<C>::topPair() const {
+ MATHIC_ASSERT(!mColumnQueue.empty());
+ Column* topColumn = mColumnQueue.top();
+ MATHIC_ASSERT(topColumn != 0);
+ MATHIC_ASSERT(!topColumn->empty());
+ return std::make_pair(topColumn->columnIndex(), topColumn->rowIndex());
+ }
+
+ template<class C>
+ const typename PairQueue<C>::PairData& PairQueue<C>::topPairData() const {
+ MATHIC_ASSERT(!mColumnQueue.empty());
+ Column* topColumn = mColumnQueue.top();
+ MATHIC_ASSERT(topColumn != 0);
+ MATHIC_ASSERT(!topColumn->empty());
+ return topColumn->pairData();
+ }
+}
+
+#endif
diff --git a/src/mathic/TourTree.h b/src/mathic/TourTree.h
index 9256c12..e2459e9 100755
--- a/src/mathic/TourTree.h
+++ b/src/mathic/TourTree.h
@@ -7,8 +7,13 @@
#include <vector>
namespace mathic {
+ class TourTreeSuggestedOptions {
+ public:
+ // there are no tournament tree options so far.
+ };
+
template<class C>
- class TourTree {
+ class TourTree {
public:
typedef C Configuration;
typedef typename Configuration::Entry Entry;
@@ -52,7 +57,10 @@ namespace mathic {
private:
class Player;
- typedef ComTree<Player*, C::fastIndex> Tree;
+ // Setting fastIndex to true speeds up left/right child
+ // computations. We only compute parents, so there is no reason
+ // to enable fastIndex.
+ typedef ComTree<Player*, false> Tree;
typedef typename Tree::Node Node;
struct Player {
Player(const Entry& entry, Node leaf): entry(entry), leaf(leaf) {}
diff --git a/src/mathic/stdinc.h b/src/mathic/stdinc.h
index 3311b15..b4368bf 100755
--- a/src/mathic/stdinc.h
+++ b/src/mathic/stdinc.h
@@ -1,15 +1,14 @@
#ifndef MATHIC_STDINC_GUARD
#define MATHIC_STDINC_GUARD
-#if (defined DEBUG || defined _DEBUG) && (!(defined MATCHIC_NDEBUG))
-#ifndef MATHIC_DEBUG
-#define MATHIC_DEBUG
+#if (defined MATHIC_DEBUG) && (defined MATHIC_NDEBUG)
+#error Both MATHIC_DEBUG and MATHIC_NDEBUG defined
#endif
+
+#ifdef MATHIC_DEBUG
#include <cassert>
#define MATHIC_ASSERT(X) assert(X)
-#endif
-
-#ifndef MATHIC_ASSERT
+#else
#define MATHIC_ASSERT(X)
#endif
diff --git a/src/test/PairQueue.cpp b/src/test/PairQueue.cpp
new file mode 100755
index 0000000..64b5f76
--- /dev/null
+++ b/src/test/PairQueue.cpp
@@ -0,0 +1,307 @@
+#include "mathic/PairQueue.h"
+#include <gtest/gtest.h>
+
+#include <string>
+#include <sstream>
+#include <set>
+
+namespace {
+ class PQConf {
+ public:
+ PQConf(int id): mId(id) {}
+ int id() const {return mId;}
+
+ typedef std::string PairData;
+ void computePairData(size_t col, size_t row, std::string& str) {
+ str = pairString(col, row);
+ }
+
+ typedef bool CompareResult;
+ bool compare(int colA, int rowA, const std::string& strA,
+ int colB, int rowB, const std::string& strB) const {
+ MATHIC_ASSERT(pairString(colA, rowA) == strA);
+ MATHIC_ASSERT(pairString(colB, rowB) == strB);
+ return strA > strB;
+ }
+ bool cmpLessThan(bool v) const {return v;}
+
+ std::string pairString(int col, int row) const {
+ std::ostringstream out;
+ out << col << row << mId;
+ return out.str();
+ }
+
+ private:
+ int mId;
+ };
+}
+
+TEST(PairQueue, NoOp) {
+ mathic::PairQueue<PQConf> pq(PQConf(421));
+ ASSERT_EQ(421, pq.configuration().id());
+};
+
+TEST(PairQueue, emptyAndSizeAndColumnCount) {
+ mathic::PairQueue<PQConf> pq(PQConf(1));
+
+ ASSERT_TRUE(pq.empty());
+ ASSERT_EQ(0, pq.size());
+ ASSERT_EQ(0, pq.columnCount());
+ size_t const columnCount = 7;
+ mathic::PairQueue<PQConf>::Index rows[columnCount - 1] = {0,1,2,3,4,5};
+ // add full columns
+ for (size_t i = 0; i < columnCount; ++i) {
+ // single-digit indicies are sorted just by their numerical value
+ pq.addColumnDescending(rows, rows + i);
+ ASSERT_EQ(i == 0, pq.empty());
+ ASSERT_EQ((i * (i + 1)) / 2, pq.size()) << "i = " << i;
+ ASSERT_EQ(i + 1, pq.columnCount());
+ }
+ size_t pairCount = (columnCount * (columnCount - 1)) / 2;
+ ASSERT_FALSE(pq.empty());
+ ASSERT_EQ(pairCount, pq.size());
+ ASSERT_EQ(columnCount, pq.columnCount());
+
+ // add empty column
+ pq.addColumnDescending(rows, rows);
+ ASSERT_FALSE(pq.empty());
+ ASSERT_EQ(pairCount, pq.size());
+ ASSERT_EQ(columnCount + 1, pq.columnCount());
+
+ // add a 2-element column
+ pq.addColumnDescending(rows, rows + 2);
+ pairCount += 2;
+ ASSERT_FALSE(pq.empty());
+ ASSERT_EQ(pairCount, pq.size());
+ ASSERT_EQ(columnCount + 2, pq.columnCount());
+
+ for (size_t i = 0; i < pairCount; ++i) {
+ ASSERT_EQ(pairCount - i, pq.size());
+ ASSERT_FALSE(pq.empty());
+ pq.pop();
+ }
+ ASSERT_TRUE(pq.empty());
+ ASSERT_EQ(0, pq.size());
+ ASSERT_EQ(columnCount + 2, pq.columnCount());
+
+ pq.addColumnDescending(rows, rows); // empty column
+ ASSERT_TRUE(pq.empty()); // still empty
+}
+
+// test that the queue orders the pairs correctly
+TEST(PairQueue, Ordering) {
+ mathic::PairQueue<PQConf> pq(PQConf(9));
+ typedef mathic::PairQueue<PQConf>::Index Index;
+
+ // Put some pairs in. Note that the pairdata string does not
+ // distinguish all pairs and that, according to the pairdata,
+ // (11,0) < (11,10) = (111,0) < (11,5)
+ // so the order that pairs are extracted mix up columns 11 and 111.
+ for (size_t col = 0; col < 112; ++col) {
+ Index const* begin = 0;
+ Index const* end = 0;
+ if (col == 1) {
+ Index const rows[] = {0};
+ begin = rows;
+ end = rows + sizeof(rows) / sizeof(rows[0]);
+ } else if (col == 11) {
+ Index const rows[] = {0, 10, 5};
+ begin = rows;
+ end = rows + sizeof(rows) / sizeof(rows[0]);
+ } else if (col == 13) {
+ Index const rows[] = {12, 3, 7};
+ begin = rows;
+ end = rows + sizeof(rows) / sizeof(rows[0]);
+ } else if (col == 111) {
+ Index const rows[] = {0, 100};
+ begin = rows;
+ end = rows + sizeof(rows) / sizeof(rows[0]);
+ }
+ pq.addColumnDescending(begin, end);
+ }
+ std::ostringstream out;
+ std::string lastPd;
+ while (!pq.empty()) {
+ // Extract top pair and top pairdata and check that doing that in
+ // either order works.
+ std::pair<Index, Index> p;
+ std::string pd;
+ if ((pq.size() % 2) == 0) {
+ pd = pq.topPairData();
+ p = pq.topPair();
+ } else {
+ p = pq.topPair();
+ pd = pq.topPairData();
+ }
+ if (p.first == 11 && p.second == 5) {
+ // test adding a column after top() but before pop()
+ MATHIC_ASSERT(pq.columnCount() == 112);
+ Index const rows[] = {0, 111};
+ Index const* begin = rows;
+ Index const* end = rows + sizeof(rows) / sizeof(rows[0]);
+ pq.addColumnDescending(begin, end);
+ ASSERT_EQ((std::make_pair<Index, Index>(112, 0)), pq.topPair());
+ ASSERT_EQ("11209", pq.topPairData());
+ pq.pop();
+ ASSERT_EQ("1121119", pq.topPairData());
+ ASSERT_EQ((std::make_pair<Index, Index>(112, 111)), pq.topPair());
+ pq.pop();
+ // should be back at same place now
+ ASSERT_EQ(pd, pq.topPairData());
+ ASSERT_EQ(p, pq.topPair());
+
+ // test adding a column that only becomes the top later
+ while (pq.columnCount() < 200)
+ pq.addColumnDescending(rows, rows);
+ pq.addColumnDescending(begin, end);
+
+ // still at same place
+ ASSERT_EQ(pd, pq.topPairData());
+ ASSERT_EQ(p, pq.topPair());
+ }
+
+
+ // ASSERT_TRUE(lastPd <= pd);
+ ASSERT_EQ(pq.configuration().pairString(p.first, p.second), pd);
+ out << ' ' << pd;
+ pq.pop();
+ lastPd = pd;
+ // test adding a column that has already been
+ if (p.first == 11 && p.second == 5) {
+ Index rows[] = {0, 111};
+ Index const* begin = rows;
+ Index const* end = rows + sizeof(rows) / sizeof(rows[0]);
+ }
+ }
+ ASSERT_EQ(" 109 1109 11109 11109 1111009 1159 13129 1339 1379 20009 2001119",
+ out.str());
+}
+
+// test that the change to large indices works correctly
+TEST(PairQueue, LargeIndices) {
+ mathic::PairQueue<PQConf> pq(PQConf(7));
+ typedef mathic::PairQueue<PQConf>::Index Index;
+ Index const rows[] = {0, 100000}; // 100000 is more than fits in 16 bits
+ MATHIC_ASSERT(rows[1] > 64000); // this test assumes that index has >=32 bits
+ pq.addColumnDescending(rows, rows);
+ pq.addColumnDescending(rows, rows + 1);
+ for (size_t i = 2; i < 100100; ++i)
+ pq.addColumnDescending(rows, rows);
+ ASSERT_EQ(100100, pq.columnCount());
+ pq.addColumnDescending(rows, rows + sizeof(rows) / sizeof(rows[0]));
+ ASSERT_EQ((std::make_pair<Index, Index>(100100, 0)), pq.topPair());
+ ASSERT_EQ("10010007", pq.topPairData());
+ pq.pop();
+ ASSERT_EQ((std::make_pair<Index, Index>(100100, 100000)), pq.topPair());
+ ASSERT_EQ("1001001000007", pq.topPairData());
+ pq.pop();
+ ASSERT_EQ((std::make_pair<Index, Index>(1, 0)), pq.topPair());
+ ASSERT_EQ("107", pq.topPairData());
+ pq.pop();
+}
+
+namespace {
+ // This configuration uses static variables to track construction
+ // and deconstruction of PairData. Therefore this configuration
+ // should only be used in one test so that tests can still run in
+ // parallel.
+ class PQConDeconCounterConf {
+ public:
+ // better not have two of these objects around at the same time!
+ PQConDeconCounterConf() {mLive.clear();}
+
+ struct PairData {
+ PairData() {makeLive();}
+ ~PairData() {makeDead();}
+
+ bool live() const {
+ return mLive.find(this) != mLive.end();
+ }
+
+ void makeLive() {
+ // Could not do this directly in the constructor as gcc 4.5.3
+ // would complain "error: returning a value from a
+ // constructor", even though that should not be happening.
+
+ // assert this not already live
+ ASSERT_TRUE(mLive.insert(this).second);
+ }
+
+ void makeDead() {
+ // Could not do this directly in the destructor as gcc 4.5.3
+ // would complain "error: returning a value from a
+ // destructor", even though that should not be happening.
+
+ // assert this live
+ ASSERT_EQ(1, mLive.erase(this));
+ }
+
+ size_t row;
+ };
+
+ void computePairData(size_t col, size_t row, PairData& pd) {
+ MATHIC_ASSERT(pd.live());
+ pd.row = row;
+ }
+
+ typedef bool CompareResult;
+ bool compare(int colA, int rowA, const PairData& pdA,
+ int colB, int rowB, const PairData& pdB) const {
+ MATHIC_ASSERT(pdA.live());
+ MATHIC_ASSERT(pdB.live());
+ return pdA.row > pdB.row;
+ }
+ bool cmpLessThan(bool v) const {return v;}
+
+ static size_t liveCount() {return mLive.size();}
+
+ private:
+ static std::set<PairData const*> mLive;
+ };
+ std::set<PQConDeconCounterConf::PairData const*> PQConDeconCounterConf::mLive;
+}
+
+// check that all PairQueue properly constructs and deconstructs
+// all PairData objects.
+TEST(PairQueue, ConDeconOfPairData) {
+ size_t const columnCount = 7;
+ mathic::PairQueue<PQConf>::Index rows[columnCount - 1] = {0,1,2,3,4,5};
+
+ // check that PairData get cleaned up for a PairQueue that ends up empty.
+ {
+ mathic::PairQueue<PQConDeconCounterConf> pq((PQConDeconCounterConf()));
+ for (size_t i = 0; i < columnCount; ++i)
+ pq.addColumnDescending(rows, rows + i);
+ pq.addColumnDescending(rows, rows);
+ pq.addColumnDescending(rows, rows + 2);
+ while (!pq.empty()) {
+ pq.topPairData(); // just to cause more constuctions/deconstructions
+ pq.pop();
+ }
+ }
+ ASSERT_EQ(0, PQConDeconCounterConf::liveCount());
+
+ // check that PairData get cleaned up for a PairQueue that has not
+ // been pop'ed at all.
+ {
+ mathic::PairQueue<PQConDeconCounterConf> pq((PQConDeconCounterConf()));
+ for (size_t i = 0; i < columnCount; ++i)
+ pq.addColumnDescending(rows, rows + i);
+ pq.addColumnDescending(rows, rows);
+ pq.addColumnDescending(rows, rows + 2);
+ }
+ ASSERT_EQ(0, PQConDeconCounterConf::liveCount());
+
+ // check that PairData get cleaned up for a PairQueue that has been
+ // pop'ed but is not full.
+ {
+ mathic::PairQueue<PQConDeconCounterConf> pq((PQConDeconCounterConf()));
+ for (size_t i = 0; i < columnCount; ++i)
+ pq.addColumnDescending(rows, rows + i);
+ pq.addColumnDescending(rows, rows);
+ pq.addColumnDescending(rows, rows + 2);
+ for (size_t i = 0; i < 3u; ++i)
+ pq.pop();
+ }
+ ASSERT_EQ(0, PQConDeconCounterConf::liveCount());
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/mathic.git
More information about the debian-science-commits
mailing list