[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