[mathic] 34/62: Added support for retirement of indexes to PairQueue.

Doug Torrance dtorrance-guest at moszumanska.debian.org
Wed Apr 1 11:36:21 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 6ed3a6ea8cbae6609e281ecb3716373fbf727b0b
Author: Bjarke Hammersholt Roune <bjarkehr.code at gmail.com>
Date:   Thu Aug 9 04:13:49 2012 -0400

    Added support for retirement of indexes to PairQueue.
---
 src/mathic/PairQueue.h | 289 ++++++++++++++++++++++++++++++++++++++-----------
 src/test/PairQueue.cpp | 145 ++++++++++++++++++++++---
 2 files changed, 355 insertions(+), 79 deletions(-)

diff --git a/src/mathic/PairQueue.h b/src/mathic/PairQueue.h
index 5db9064..b9cae3f 100755
--- a/src/mathic/PairQueue.h
+++ b/src/mathic/PairQueue.h
@@ -62,18 +62,32 @@ namespace mathic {
 	// other way than to call this function.
 	//
 	// The default implementation default-constructs the PairData and
-	// then calls Configuration::computePairData. Specialize (do not
-	// overload) this template function for your particular
-	// configuration type if you want something else to happen -- for
-	// example you might not want default construction to occur.
+	// then calls Configuration::computePairData. Specialize
+	// ConstructPairDataFunction for your particular configuration
+	// type if you want something else to happen -- for example you
+	// might not want default construction to occur.
+	//
+	// You could also specialize constructPairData directly, but this
+	// is not recommended since C++ does not allow partial
+	// specialization of function templates. Hence fully or partially
+	// specializing ConstructPairDataFunction is a more general
+	// mechanism that you might as well use.
+	template<class Configuration>
+	struct ConstructPairDataFunction {
+	  typedef typename Configuration::PairData PairData;
+	  static void function
+	  (void* memory, Index col, Index row, Configuration& conf) {
+		MATHIC_ASSERT(memory != 0);
+		MATHIC_ASSERT(col > row);
+		PairData* pd = new (memory) PairData();
+		conf.computePairData(col, row, *pd);
+	  }
+	};
 	template<class Configuration>
 	void constructPairData
 	(void* memory, Index col, Index row, Configuration& conf) {
-	  MATHIC_ASSERT(memory != 0);
-	  MATHIC_ASSERT(col > row);
-	  typename Configuration::PairData* pd =
-		new (memory) typename Configuration::PairData();
-	  conf.computePairData(col, row, *pd);
+	  ConstructPairDataFunction<Configuration>::function
+		(memory, col, row, conf);
 	}
 
 	// Used by PairData<Configuration> to destruct a PairData object
@@ -81,29 +95,104 @@ namespace mathic {
 	// destructed in any other way than to call this function.
 	//
 	// The default implementation just calls the
-	// destructor. Specialize (do not overload) this template function
-	// for your particular configuration type if you want something
-	// else to happen -- for example PairData might hold memory
-	// allocated from a memory pool that you want to return to the
-	// pool but you do not want to put a reference to the memory pool
-	// inside every PairData.
+	// destructor. Specialize DestructPairDataFunction for your
+	// particular configuration type if you want something else to
+	// happen -- for example PairData might hold memory allocated from
+	// a memory pool that you want to return to the pool but you do
+	// not want to put a reference to the memory pool inside every
+	// PairData.
+	//
+	// You could also specialize destructPairData directly, but this
+	// is not recommended since C++ does not allow partial
+	// specialization of function templates. Hence fully or partially
+	// specializing DestructPairDataFunction is a more general
+	// mechanism that you might as well use.
+	template<class Configuration>
+	struct DestructPairDataFunction {
+	  typedef typename Configuration::PairData PairData;
+	  static void function
+	  (PairData* pd, Index col, Index row, Configuration& conf) {
+		MATHIC_ASSERT(pd != 0);
+		MATHIC_ASSERT(col > row);
+		pd->~PairData();
+	  }
+	};
 	template<class Configuration>
 	void destructPairData
 	(typename Configuration::PairData* pd,
 	 Index col, Index row, Configuration& conf) {
-	  MATHIC_ASSERT(pd != 0);
-	  MATHIC_ASSERT(col > row);
-	  typedef typename Configuration::PairData PairData;
-	  pd->~PairData();
+	  DestructPairDataFunction<Configuration>::function(pd, col, row, conf);
 	}
+
+	// Used by PairData<Configuration> to determine whether to allow
+	// retirement of indexes. The default is to allow it, but there is
+	// some overhead. This is configured separately from the
+	// configuration to decrease the minimal size of a working
+	// configuration.
+	template<class Configuration>
+	struct SupportRetirement {
+	  // the value field must be static bool const.
+	  static bool const value = true;
+	};
+  }
+
+  namespace PairQueueInternal {
+	// Derive with true parameter to support retirement and false to
+	// not support retirement. The main point of this is not just to
+	// save the memory for the bool, it's to make it clear to the
+	// compiler that nothing is retired when retirement is not
+	// supported.
+	//
+	// This class has to be outside the PairQueue class since partial
+	// specialization is not supported for template member classes of
+	// template classes.
+	template<bool supportRetirement>
+	class SupportRetirement {
+	public:
+	  void addNextIndex() {mRetired.push_back(false);}
+	  void undoAdd() {
+		MATHIC_ASSERT(!mRetired.empty());
+		mRetired.pop_back();
+	  }
+
+	  void retireIndex(size_t index) {
+		MATHIC_ASSERT(index < mRetired.size());
+		mRetired[index] = true;
+	  }
+
+	  bool retired(size_t index) const {
+		MATHIC_ASSERT(index < mRetired.size());
+		return mRetired[index];
+	  }
+
+	private:
+	  std::vector<char> mRetired;
+	};
+
+	template<>
+	class SupportRetirement<false> {
+	public:
+	  void addNextIndex() {}
+	  void undoAdd() {}
+
+	  void retireIndex(size_t index) {
+		MATHIC_ASSERT(false); // this method should not be called.
+	  }
+
+	  bool retired(size_t index) const {
+		return false;
+	  }
+	};
   }
 
   template<class C>
-  class PairQueue {
+  class PairQueue : private PairQueueInternal::SupportRetirement<PairQueueNamespace::SupportRetirement<C>::value> {
   public:
 	typedef C Configuration;
 	typedef typename C::PairData PairData;
 	typedef PairQueueNamespace::Index Index;
+	static bool const SupportRetirement =
+	  PairQueueNamespace::SupportRetirement<Configuration>::value;
 
 	// PairQueue stores a copy of the passed in configuration.
 	PairQueue(const Configuration& conf);
@@ -134,7 +223,7 @@ namespace mathic {
     // 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;
 
@@ -149,10 +238,36 @@ namespace mathic {
 	// configured.
 	std::string name() const;
 
+	// Remove all pairs of the form (index,x) or (x,index). It is not
+	// allowed to add such pairs in future. You must not retire an
+	// index twice (that could be a bug and we want to assert in that
+	// case to surface the issue).
+	//
+	// ATTENTION: retired indexes can still appear in comparisons if
+	// that pair already has its PairData computed and stored and
+	// those comparisons must still work the same way that they did
+	// previously. No new PairData will be computed using retired
+	// indexes and of course topPair() will never involve a retired
+	// index.
+	//
+	// ATTENTION: All the retired pairs are not identified right away
+	// so pairCount() might still count some retired pairs.
+	//
+	// ATTENTION: If you have disabled support for retirement then you
+	// may not call this method.
+	void retireIndex(size_t index);
+
+	// Returns true if index has been retired. If support for
+	// retirement has been turned off then this method always returns
+	// false.
+	bool retired(size_t index) const;
+
   private:
 	typedef unsigned short SmallIndex;
 
-    class Column {
+	typedef PairQueueInternal::SupportRetirement<SupportRetirement> Retirer;
+
+    class Column  {
 	public:
 	  template<class Iter>
 	  static Column* create
@@ -162,7 +277,8 @@ namespace mathic {
 	  Index columnIndex() const {return mColumnIndex;}
 	  Index rowIndex() const;
 
-	  void incrementRowIndex(C& conf); // recomputes pairData if not empty
+	  // Recomputes pairData if not empty. Skips retired rows.
+	  void incrementRowIndex(PairQueue<C>& pq); 
 	  bool empty() const;
 	  size_t size() const; // number of pairs remaining in this column
 
@@ -342,25 +458,30 @@ namespace mathic {
   }
 
   template<class C>
-  void PairQueue<C>::Column::incrementRowIndex(C& conf) {
+  void PairQueue<C>::Column::incrementRowIndex(PairQueue<C>& pq) {
 	MATHIC_ASSERT(!empty());
 	if (big()) {
-	  ++bigBegin;
-	  if (bigBegin == bigEnd) {
-		MATHIC_ASSERT(empty());
-		destruct(*(bigBegin - 1), conf);
-		return;
-	  }
+	  do {
+		++bigBegin;
+		if (bigBegin == bigEnd) {
+		  MATHIC_ASSERT(empty());
+		  destruct(*(bigBegin - 1), pq.configuration());
+		  return;
+		}
+	  } while (PairQueue::SupportRetirement && pq.retired(*bigBegin));
 	} else {
-	  ++smallBegin;
-	  if (smallBegin == smallEnd) {
-		MATHIC_ASSERT(empty());
-		destruct(*(smallBegin - 1), conf);
-		return;
-	  }
+	  do {
+		++smallBegin;
+		if (smallBegin == smallEnd) {
+		  MATHIC_ASSERT(empty());
+		  destruct(*(smallBegin - 1), pq.configuration());
+		  return;
+		}
+	  } while (PairQueue::SupportRetirement && pq.retired(*smallBegin));
 	}
 	MATHIC_ASSERT(!empty());
-	conf.computePairData(columnIndex(), rowIndex(), mPairData);
+	MATHIC_ASSERT(!pq.retired(rowIndex()));
+	pq.configuration().computePairData(columnIndex(), rowIndex(), mPairData);
   }
 
   template<class C>
@@ -415,42 +536,70 @@ namespace mathic {
   template<class Iter>
   void PairQueue<C>::addColumnDescending
   (Iter const sortedRowsBegin, Iter const sortedRowsEnd) {
+#ifdef DEBUG
+	if (SupportRetirement) {
+	  for (Iter it = sortedRowsBegin; it != sortedRowsend; ++it)
+		MATHIC_ASSERT(!retired(*it));
+	}
+#endif
 	if (mColumnCount >= std::numeric_limits<Index>::max())
 	  throw std::overflow_error("Too large column index in PairQueue.");
 	Index const newColumnIndex = static_cast<Index>(mColumnCount);
+	Retirer::addNextIndex();
 	++mColumnCount;
-	if (sortedRowsBegin == sortedRowsEnd)
-	  return;
-	memt::Arena::Guard guard(mArena);
-
-	Column* column = Column::create
-	  (newColumnIndex, sortedRowsBegin, sortedRowsEnd, mConf, mArena);
-
-	try {
-	  mColumnQueue.push(column);
-	} catch (...) {
-	  column->destruct(mConf);
-	  throw;
+	if (sortedRowsBegin != sortedRowsEnd) {
+	  try {
+		memt::Arena::Guard guard(mArena);
+	
+		Column* column = Column::create
+		  (newColumnIndex, sortedRowsBegin, sortedRowsEnd, mConf, mArena);
+		
+		try {
+		  mColumnQueue.push(column);
+		} catch (...) {
+		  column->destruct(mConf);
+		  throw;
+		}
+		guard.release();
+	  } catch (...) {
+		Retirer::undoAdd();
+		--mColumnCount;
+		throw;
+	  }
 	}
-	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()) {
+	Column* topColumn = mColumnQueue.top();
+	do {
+	  MATHIC_ASSERT(!empty());
+	  MATHIC_ASSERT(topColumn == mColumnQueue.top());
+	  MATHIC_ASSERT(topColumn != 0);
+	  MATHIC_ASSERT(!topColumn->empty());
+
+	  if (!SupportRetirement || !retired(topColumn->columnIndex())) {
+	    // Note that all mathic queues allow doing this sequence of
+		// actions: top(), change top element in-place, do decreaseTop/pop.
+		topColumn->incrementRowIndex(*this);
+		if (!topColumn->empty()) {
+		  MATHIC_ASSERT(!retired(topColumn->columnIndex()));
+		  MATHIC_ASSERT(!retired(topColumn->rowIndex()));
+		  mColumnQueue.decreaseTop(topColumn);
+		  goto doNotDestroy;
+		}
+	  }
 	  topColumn->destruct(mConf);
 	  mColumnQueue.pop();
-	} else
-	  mColumnQueue.decreaseTop(topColumn);
+	doNotDestroy:;
+	  if (!SupportRetirement || mColumnQueue.empty())
+		break;
+	  topColumn = mColumnQueue.top();
+	} while (retired(topColumn->columnIndex()) ||
+			 retired(topColumn->rowIndex()));
+	MATHIC_ASSERT(!SupportRetirement || empty() || !retired(topPair().first));
+	MATHIC_ASSERT(!SupportRetirement || empty() || !retired(topPair().second));
   }
 
   template<class C>
@@ -464,6 +613,24 @@ namespace mathic {
   }
 
   template<class C>
+  void PairQueue<C>::retireIndex(size_t index) {
+	MATHIC_ASSERT(SupportRetirement);
+	MATHIC_ASSERT(index < columnCount());
+	Retirer::retireIndex(index);
+	if (!empty()) {
+	  std::pair<size_t, size_t> p = topPair();
+	  if (p.first == index || p.second == index)
+		pop();
+	}
+  }
+
+  template<class C>
+  bool PairQueue<C>::retired(size_t index) const {
+	MATHIC_ASSERT(index < columnCount());
+	return Retirer::retired(index);
+  }
+
+  template<class C>
   std::pair<size_t, size_t> PairQueue<C>::topPair() const {
 	MATHIC_ASSERT(!mColumnQueue.empty());
 	Column* topColumn = mColumnQueue.top();
diff --git a/src/test/PairQueue.cpp b/src/test/PairQueue.cpp
index 444c0cd..877a7bd 100755
--- a/src/test/PairQueue.cpp
+++ b/src/test/PairQueue.cpp
@@ -36,6 +36,23 @@ namespace {
   };
 }
 
+namespace mathic {
+  namespace PairQueueNamespace {
+	template<>
+	struct SupportRetirement<PQConf> {
+	  static bool const value = false;
+	};
+  }
+}
+
+TEST(PairQueue, RetirementSetToFalse) {
+  ASSERT_FALSE(mathic::PairQueue<PQConf>::SupportRetirement);
+  mathic::PairQueue<PQConf> pq(PQConf(421));
+  mathic::PairQueue<PQConf>::Index* null = 0;
+  pq.addColumnDescending(null, null);
+  ASSERT_FALSE(pq.retired(0));
+}
+
 TEST(PairQueue, NoOp) {
   mathic::PairQueue<PQConf> pq(PQConf(421));
   ASSERT_EQ(421, pq.configuration().id());
@@ -208,7 +225,13 @@ namespace {
   class PQConDeconCounterConf {
   public:
 	// better not have two of these objects around at the same time!
-	PQConDeconCounterConf() {mLive.clear();}
+	PQConDeconCounterConf(): owningQueue(0) {mLive.clear();}
+
+	// This is the queue that this is the configuration of. You have
+	// to set it after constructing the queue. It has to be void* as
+	// we can't instantiate the type now before we specialize the
+	// parameters like mathic::PairQueueNamespace::SupportRetirement.
+	void* owningQueue;
 
 	class PairData {
 	  friend class PQConDeconCounterConf;
@@ -254,10 +277,7 @@ namespace {
 	  pd->~PairData();
 	}
 
-	void computePairData(size_t col, size_t row, PairData& pd) {
-	  MATHIC_ASSERT(pd.live());
-	  pd.row = row;
-	}
+	void computePairData(size_t col, size_t row, PairData& pd);
 
 	typedef bool CompareResult;
 	bool compare(int colA, int rowA, const PairData& pdA,
@@ -279,20 +299,41 @@ namespace {
 namespace mathic {
   namespace PairQueueNamespace {
 	template<>
-	void constructPairData
-	(void* memory, Index col, Index row, PQConDeconCounterConf& conf) {
-	  PQConDeconCounterConf::PairData* pd =
-		static_cast<PQConDeconCounterConf::PairData*>(memory);
-	  conf.construct(pd);
-	  conf.computePairData(col, row, *pd);
-	}
+	struct ConstructPairDataFunction<PQConDeconCounterConf> {
+	  static void function
+	  (void* memory, Index col, Index row, PQConDeconCounterConf& conf) {
+		PQConDeconCounterConf::PairData* pd =
+		  static_cast<PQConDeconCounterConf::PairData*>(memory);
+		conf.construct(pd);
+		conf.computePairData(col, row, *pd);
+	  }
+	};
 
 	template<>
-	void destructPairData
-	(PQConDeconCounterConf::PairData* pd,
-	 Index col, Index row, PQConDeconCounterConf& conf) {
-	  conf.destruct(pd);
-	}
+	struct DestructPairDataFunction<PQConDeconCounterConf> {
+	  static void function
+	  (PQConDeconCounterConf::PairData* pd,
+	   Index col, Index row, PQConDeconCounterConf& conf) {
+		conf.destruct(pd);
+	  }
+	};
+
+	template<>
+	struct SupportRetirement<PQConDeconCounterConf> {
+	  static bool const value = true;
+	};
+  }
+}
+
+namespace {
+  inline void PQConDeconCounterConf::computePairData
+	(size_t col, size_t row, PairData& pd) {
+	MATHIC_ASSERT(pd.live());
+	MATHIC_ASSERT(!static_cast<mathic::PairQueue<PQConDeconCounterConf>*>
+				  (owningQueue)->retired(col));
+	MATHIC_ASSERT(!static_cast<mathic::PairQueue<PQConDeconCounterConf>*>
+				  (owningQueue)->retired(row));
+	pd.row = row;
   }
 }
 
@@ -300,11 +341,13 @@ namespace mathic {
 // all PairData objects.
 TEST(PairQueue, ConDeconOfPairData) {
   size_t const columnCount = 7;
-  mathic::PairQueue<PQConf>::Index rows[columnCount - 1] = {0,1,2,3,4,5};
+  mathic::PairQueue<PQConDeconCounterConf>::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()));
+	pq.configuration().owningQueue = &pq;
 	for (size_t i = 0; i < columnCount; ++i)
 	  pq.addColumnDescending(rows, rows + i);
 	pq.addColumnDescending(rows, rows);
@@ -320,6 +363,7 @@ TEST(PairQueue, ConDeconOfPairData) {
   // been pop'ed at all.
   {
 	mathic::PairQueue<PQConDeconCounterConf> pq((PQConDeconCounterConf()));
+	pq.configuration().owningQueue = &pq;
 	for (size_t i = 0; i < columnCount; ++i)
 	  pq.addColumnDescending(rows, rows + i);
 	pq.addColumnDescending(rows, rows);
@@ -331,6 +375,7 @@ TEST(PairQueue, ConDeconOfPairData) {
   // pop'ed but is not full.
   {
 	mathic::PairQueue<PQConDeconCounterConf> pq((PQConDeconCounterConf()));
+	pq.configuration().owningQueue = &pq;
 	for (size_t i = 0; i < columnCount; ++i)
 	  pq.addColumnDescending(rows, rows + i);
 	pq.addColumnDescending(rows, rows);
@@ -340,3 +385,67 @@ TEST(PairQueue, ConDeconOfPairData) {
   }
   ASSERT_EQ(0, PQConDeconCounterConf::liveCount());
 }
+
+TEST(PairQueue, RetirementSetToTrue) {
+  ASSERT_TRUE(mathic::PairQueue<PQConDeconCounterConf>::SupportRetirement);
+  mathic::PairQueue<PQConDeconCounterConf> pq((PQConDeconCounterConf()));
+  pq.configuration().owningQueue = &pq;
+  mathic::PairQueue<PQConf>::Index* null = 0;
+  pq.addColumnDescending(null, null);
+  ASSERT_TRUE(pq.empty());
+  ASSERT_FALSE(pq.retired(0));
+  pq.retireIndex(0);
+  ASSERT_TRUE(pq.retired(0));
+  ASSERT_TRUE(pq.empty());
+}
+
+TEST(PairQueue, Retirement) {
+  typedef mathic::PairQueue<PQConDeconCounterConf>::Index Index;
+  mathic::PairQueue<PQConDeconCounterConf> pq((PQConDeconCounterConf()));
+  pq.configuration().owningQueue = &pq;
+  Index const* null = 0;
+  pq.addColumnDescending(null, null);
+
+  {
+	Index const rows[] = {0};
+	pq.addColumnDescending(rows, rows + sizeof(rows) / sizeof(*rows));
+  }
+  {std::pair<size_t, size_t> p(1, 0); ASSERT_EQ(p, pq.topPair());}
+  
+  // retire higher component of the top pair
+  pq.retireIndex(1);
+  ASSERT_TRUE(pq.empty());
+  
+  {
+	Index const rows[] = {0};
+	pq.addColumnDescending(rows, rows + sizeof(rows) / sizeof(*rows));
+  }
+  {std::pair<size_t, size_t> p(2, 0); ASSERT_EQ(p, pq.topPair());}
+  {
+	Index const rows[] = {0,2};
+	pq.addColumnDescending(rows, rows + sizeof(rows) / sizeof(*rows));
+  }
+  ASSERT_EQ(0, pq.topPair().second);
+  {
+	Index const rows[] = {0,2,3};
+	pq.addColumnDescending(rows, rows + sizeof(rows) / sizeof(*rows));
+  }
+  ASSERT_EQ(0, pq.topPair().second);
+
+  // retire lower component of top pair
+  pq.retireIndex(0);
+  ASSERT_FALSE(pq.empty());
+  ASSERT_EQ(2, pq.topPair().second);
+
+  // retire component not involved in top pair
+  if (pq.topPair().first == 3) {
+	pq.retireIndex(4);
+	{std::pair<size_t, size_t> p(3, 2); ASSERT_EQ(p, pq.topPair());}
+  } else {
+	pq.retireIndex(3);
+	{std::pair<size_t, size_t> p(4, 2); ASSERT_EQ(p, pq.topPair());}
+  }
+  ASSERT_FALSE(pq.empty());
+  pq.pop();
+  ASSERT_TRUE(pq.empty());
+}

-- 
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