diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/TrigJetHypoToolConfig.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/TrigJetHypoToolConfig.py
index 70e24b5c638b66a1898ac30052e27243ff185e39..9eacab4c37f90fa36a0ec656ab708f7b4ae9628f 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/TrigJetHypoToolConfig.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/TrigJetHypoToolConfig.py
@@ -120,7 +120,8 @@ def  trigJetHypoToolHelperFromDict(chain_dict):
     Tool tree structure."""
 
     
-    logger.debug('trigJetHypoToolFromDict chainDict ', str(chain_dict))
+    msg = 'trigJetHypoToolFromDict chainDict %s' %  str(chain_dict)
+    logger.debug(msg)
     algToolFactory = FastReductionAlgToolFactory()
 
     # Build the helper tool
@@ -164,9 +165,10 @@ def  trigJetHypoToolHelperFromDict(chain_dict):
 def  trigJetHypoToolFromDict_(chain_dict, hypo_tool, debug=False):
     """Produce  a jet trigger hypo tool from a chainDict"""
 
-    logger.debug('trigJetHypoToolFromDict_ tool type %s chainDict ', 
-                 hypo_tool.__class__.__name__,
-                 str(chain_dict))
+    
+    msg = 'trigJetHypoToolFromDict_ tool type %s  chainDict %s' % (
+        hypo_tool.__class__.__name__, str(chain_dict))
+    logger.debug(msg)
 
     # obtain  a Helper Tool (possibly a tree of tools) to
     # make the hypo decision.
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/DebugInfoCollector.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/DebugInfoCollector.cxx
index 382af2b1a84efdbc97fe67f27d2f1fc31a997411..b85a24cd7fcf391b993023830169c774da1bc1c3 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/DebugInfoCollector.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/DebugInfoCollector.cxx
@@ -19,7 +19,7 @@ DebugInfoCollector::~DebugInfoCollector(){write();}
 
 void DebugInfoCollector::collect(const std::string& key,
                                  const std::string& info){
-  m_info[key].emplace_back(m_timer.elapsed(), info);
+  m_info[key].emplace_back(m_timer.elapsed_to_now(), info);
 }
 
 std::string DebugInfoCollector::toStringByMsgKey() const {
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/FastReducer.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/FastReducer.cxx
index a0f6871e24d460287073009825b40c5bb8662a38..5970c8bac52288acdc76d39e5e6cef016e986ad4 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/FastReducer.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/FastReducer.cxx
@@ -194,11 +194,11 @@ bool FastReducer::findInitialJetGroups(const HypoJetCIter& jets_b,
 					    filtered_jets.end());
 
     while(true){
-      auto ojg = grouper->next();
+      auto ojg = grouper->next();  // obtain a vector of jet ptrs
       if (!ojg.has_value()) {break;}
 
       auto jg = *ojg;
-      auto jg_ind = m_jgRegister.record(jg);
+      auto jg_ind = m_jgRegister.record(jg);  // obtain an int index for the jg
       m_testedBy[leaf].insert(jg_ind);
       if (m_conditions[leaf]->isSatisfied(jg, collector)){
 	if(collector){recordJetGroup(jg_ind, jg, collector);}
@@ -341,52 +341,55 @@ bool FastReducer::propagate_(std::size_t child,
     
     auto jg_indices = *next;
 
-    std::vector<std::size_t> elem_jgs;
+    std::vector<std::size_t> elem_indices;
 
     // flatten the jet groups participating in the product to a list of
     // elemental jet groups (ie the incoming jets). The entities being
     // manipulated are integer indexes.
     
     for(auto i : jg_indices){
-      elem_jgs.insert(elem_jgs.end(),
+      elem_indices.insert(elem_indices.end(),
 		      m_jg2elemjgs[i].begin(),
 		      m_jg2elemjgs[i].end());
     }
 
     // if any of the elemental jet group indices are repeated,
-    // stop processing of the new jet group. (do not allow sharing for
-    // among leaf nodes. Sharing is handled by processing > 1 leaf groups
-    // each of which does not share.
+    // stop processing the new jet group. (do not allow sharing for
+    // among Conditions. Sharing is handled by having the matcher use
+    // multiple FastReducer instances).
 
-    std::set<std::size_t> unique_indices(elem_jgs.begin(),
-					 elem_jgs.end());
-    if(unique_indices.size() != elem_jgs.size()){
+    std::sort(elem_indices.begin(), elem_indices.end());
+    if (std::unique(elem_indices.begin(),
+		    elem_indices.end()) != elem_indices.end()){
       next = jg_product->next(collector);
       continue;
     }
 
+    // use the elemental jet group indices to form a vector of jet pointers
     HypoJetVector jg;
-    for(const auto& i : elem_jgs){
-      jg.push_back(m_indJetGroup.at(i)[0]);  // why [0]? assume elemental jg has size 1
+    for(const auto& i : elem_indices){
+      const auto& jetGroup = m_indJetGroup.at(i);
+      jg.insert(jg.end(), jetGroup.begin(), jetGroup.end());
     }
     
     // obtain an index for the new jet group.
-    // auto cur_jg = m_jgIndAllocator(elem_jgs);
     auto cur_jg = m_jgRegister.record(jg);
+
+    // keep track of which jet groups a Condition sees.
     if(m_testedBy[par].find(cur_jg) != m_testedBy[par].end()){
       next = jg_product->next(collector);
       continue;
     }
+    
     m_testedBy[par].insert(cur_jg);
-	
-
 
+    // check if parent is satisfied by a jet group (vector of jet ptrs)
     if (m_conditions[par]->isSatisfied(jg, collector)){// par is a tree ind.
 
-      // get an index for this set of elementary jet group indices
+      // get an index for this vector of elementary jet group indices
       m_satisfiedBy[par].push_back(cur_jg);
 
-      m_jg2elemjgs[cur_jg] = elem_jgs;
+      m_jg2elemjgs[cur_jg] = elem_indices;
       if(collector){recordJetGroup(cur_jg, jg, collector);}
     }
     
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupProduct.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupProduct.cxx
index 5b909300d5239fd96904c97aa772f49c0d90432d..0ff4e831940273ddc2a4d1019a84fa848ca5b0a9 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupProduct.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupProduct.cxx
@@ -6,23 +6,27 @@
 #include <set>
 #include <string>
 
+#include <iostream>
+
 
 JetGroupProduct::JetGroupProduct(const std::vector<std::size_t>& siblings,
 				 const CondInd2JetGroupsInds& satisfiedBy,
-				 const std::vector<std::size_t>& condMult
-				 ) {
-  // inner vector of jetGroupIndVec are the indices of indJetGroups
-  // that satisfy one child. A parent may have many children
-  // indJetGroups is a size_t: jet group table.
-
+				 const std::vector<std::size_t>& condMult) {
 
   // copy the parts od satisfiedBy corresponding to the sibling indices
   // into m_condIndices. The number of copies made per sibling is the
   // given by the sibling multiplicity.
-
   for(const auto& isib : siblings){
     auto mult = condMult[isib];  // jet groups indices of satisying jgs.
 
+    // find the greatest jet index we will deal with
+    const auto& sibjets = satisfiedBy.at(isib);
+    m_jetEnd = std::max(m_jetEnd,
+			   *std::max_element(sibjets.begin(),
+					     sibjets.end()));
+    ++m_jetEnd;; // to be used as an end marker
+    m_jetMask.reserve(m_jetEnd);
+    
     // no of copies = multiplicity of the Condition
     for (std::size_t im = 0; im != mult; ++im){
       m_condIndices.push_back(satisfiedBy.at(isib));
@@ -46,10 +50,23 @@ JetGroupProduct::JetGroupProduct(const std::vector<std::size_t>& siblings,
   
 std::optional<std::vector<std::size_t>>
 JetGroupProduct::next(const Collector& collector){
-
-  unsigned int ipass{0};
+  // Produce an ordered set sequence of jet indices, one from each Condition
+  // with no duplicate indices.
+  //
+  // Ask ProductGen for the next set of indices into the condIndices arrays
+  // use these to create a sequences of jet indices. The process is blocked
+  // if there is a duplicate jet index using m_jetMask
+  //
+  // finally, m_seenInidces keeps track of jet index seqiences that have
+  // already been generated. If this is the first time the sequece has
+  // been generated, it will be added to m_seenJetIndices, and then requrned
+  // to the caller. If it has already been seen, it will be abandoned.
   
+  unsigned int ipass{0};
+  std::vector<std::size_t> jg_indices;
   while(true){
+
+    m_jetMask.assign(m_jetEnd, false);
     
     if(collector){
       collector->collect("JetGroupProduct::next()",
@@ -66,21 +83,38 @@ JetGroupProduct::next(const Collector& collector){
 
     // select indicies from the child jet group indicies. Form a vector
     // of indices.
-    std::vector<std::size_t> jg_indices;
+    bool blocked{false};
     for(std::size_t i = 0; i < indices.size(); ++i){
-      jg_indices.push_back((m_condIndices.at(i)).at(indices[i]));
+      auto idx = (m_condIndices.at(i)).at(indices[i]);
+      if (m_jetMask[idx]) {
+	blocked = true;
+	break;
+      }
+ 
+      
+      m_jetMask[idx] = true;
     }
 
-    
-    // require there are no duplicate indices - this would be
-    // rejected  by a non-sharing flow network. but remove the
-    // case early. Sharing is handled otherwise...
-    std::set<std::size_t> unique_indices(jg_indices.begin(),
-					 jg_indices.end());
-    
-    if(jg_indices.size() == unique_indices.size()){
-      return jg_indices;
+    if (blocked){continue;}
+
+    jg_indices.clear();
+    jg_indices.reserve(m_condIndices.size());
+    for(std::size_t i = 0; i != m_jetEnd; ++i) {
+      if (m_jetMask[i]) {
+	jg_indices.push_back(i);
+      }
     }
-  }
+
+   
+      
+    if (std::find(m_seenIndices.begin(),
+		  m_seenIndices.end(),
+		  jg_indices) == m_seenIndices.end()){
+      
+	m_seenIndices.push_back(jg_indices);
+	return std::make_optional<std::vector<std::size_t>>(jg_indices);
+      }
+  }		       
+ 
   std::optional<std::vector<std::size_t>>();
 }
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupProduct.h b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupProduct.h
index 264a91dc06901fdc0b462df0a761cde011d3ea2d..31633b458cc02314835cf0f30d5af842baf121af 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupProduct.h
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupProduct.h
@@ -41,7 +41,11 @@ class JetGroupProduct: public IJetGroupProduct{
   
  private:
   std::vector<std::vector<std::size_t>>  m_condIndices;
+  std::vector<bool>  m_jetMask;
+  std::size_t  m_jetEnd{0};
   ProductGen m_productGen;
+  std::vector<std::vector<std::size_t>> m_seenIndices;
+
 };
 
 #endif
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupUnion.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupUnion.cxx
index f4a55a3a8f2ab127437a77c9f8b9fd9822373f8e..047bf47461e3d9dec8779827916168f273bd8d20 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupUnion.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetGroupUnion.cxx
@@ -26,6 +26,7 @@ JetGroupUnion::JetGroupUnion(const std::vector<std::size_t>& siblings,
   }
   
   m_jetIndices.assign(j_elem_indices.begin(), j_elem_indices.end());
+  if (m_jetIndices.empty()) {m_done = true;}
 }
   
 std::optional<std::vector<std::size_t>>
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetTrigTimer.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetTrigTimer.cxx
index 0cf30bc42be108bf5cfa3ef1c486c4cfe79d8bb4..15fcd9673614641f56d123d1bad8d177bc9838ba 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetTrigTimer.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetTrigTimer.cxx
@@ -13,62 +13,75 @@ using namespace std::chrono;
 JetTrigTimer::JetTrigTimer(bool ns): m_nanoseconds(ns) {}
 			     
 void JetTrigTimer::start(){
-
-  assert(!m_isRunning);
-  m_start = system_clock::now();
-  m_isRunning = true;
+  // reset start time to now
+  
   m_nCalls += 1;
+  m_start = system_clock::now();
 }
 
-void JetTrigTimer::stop(){
-
-  assert(m_isRunning);
+void JetTrigTimer::update(){
+  // reset stop time to now, calculate delta and sum delta.
+  // does not change the start time
+  
   m_stop = system_clock::now();
-  m_isRunning = false;
   if(m_nanoseconds){
-    m_delta += duration_cast<nanoseconds>(m_stop - m_start).count();
+     m_elapsedDelta += duration_cast<nanoseconds>(m_stop - m_start).count();
   } else {
-    m_delta += duration_cast<microseconds>(m_stop - m_start).count();
+     m_elapsedDelta += duration_cast<microseconds>(m_stop - m_start).count();
   }
-  m_elapsedDelta += m_delta;
-  m_delta = 0;
 }
 
 
 void JetTrigTimer::reset() noexcept {
+  // general reset
+  
+  m_nCalls = 0;
+  m_elapsedDelta = 0.;
   m_nCalls = 0;
-  m_delta = 0.;
-  m_elapsedDelta = 0;
+  m_start = std::chrono::system_clock::now();
+  m_stop = m_start;
 }
   
-std::string JetTrigTimer::read(){    
- 
-  assert (!m_isRunning);
+std::string JetTrigTimer::read() const{    
+  // reuturn curent data as a string
+  
+  auto record = read_bare();
+  auto delta = std::get<0>(record);
+  auto ncalls = std::get<1>(record);
+  auto units = std::get<2>(record);
+  
   std::stringstream ss;
-  double avTime  =  m_nCalls == 0 ? 0. : m_delta / m_nCalls;
-  ss << "time("<<units()<<"): " << m_delta << " nCalls: " << m_nCalls << " tav: "
-     << avTime << '\n';
+  double avTime  =  ncalls == 0 ? 0. : delta / m_nCalls;
+  ss << "time("<<units <<"): " << delta << " nCalls: " << ncalls << " tav: "
+     << avTime;
   return ss.str();
 }
-  
-std::string JetTrigTimer::readAndReset(){    
+
+std::tuple<double, int, std::string> JetTrigTimer::read_bare() const{
+  // reuturn curent data in numeric form
+
+  return std::make_tuple(m_elapsedDelta, m_nCalls, units());
+}
+
+std::string JetTrigTimer::readAndReset(){
+  // obtain current data as a string then reset the timer
   auto s = read();
   reset();
   return s;
 }
 
-void JetTrigTimer::accumulate(){
-  if(m_isRunning){
-    stop();
-    m_elapsedDelta += m_delta;
-    start();
-  }
+
+double JetTrigTimer::elapsed_to_now() {
+  update();
+  m_start = system_clock::now();
+  return m_elapsedDelta;
 }
 
-double JetTrigTimer::elapsed() {
-  accumulate();
+double JetTrigTimer::elapsed_to_update() {
+  //  update the current data, return the time since start()
   return m_elapsedDelta;
 }
+ 
   
 std::string JetTrigTimer::units() const {
   return m_nanoseconds ? "ns" : "us";
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetTrigTimer.h b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetTrigTimer.h
index 9bb7612024c77c3661dd8ba0f8c37f8684a9dc22..522aa790356b791e83c8b326a8c24106cd417f27 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetTrigTimer.h
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/JetTrigTimer.h
@@ -8,29 +8,29 @@
 #include <chrono>
 #include <vector>
 #include <string>
+#include <tuple>
 
 class JetTrigTimer{
  public:
   JetTrigTimer(bool nanoseconds=false);
   
   void start();
-  void stop();
-  std::string read();
+  void update();
+  std::string read() const;
+  std::tuple<double, int, std::string> read_bare() const;
   void reset() noexcept;
   std::string readAndReset();
-  std::string readAndContinue();
-  double elapsed();
+  double elapsed_to_now();
+  double elapsed_to_update();
+  std::string units() const;
  
  private:
   std::size_t m_nCalls{0};
   std::chrono::system_clock::time_point m_start;
   std::chrono::system_clock::time_point m_stop;
-  bool m_isRunning{false};
-  double m_delta{0.};
   double m_elapsedDelta{0.};
   bool m_nanoseconds;
   void accumulate();
-  std::string units() const;
 
 };
 #endif
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ProductGen.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ProductGen.cxx
index ffdb5a2226ef4d3765b5c62c41e5654c086b51d2..57502126c088dcab82901d2651d1c5344a15d722 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ProductGen.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ProductGen.cxx
@@ -11,13 +11,16 @@ ProductGen::ProductGen() {}
 
 ProductGen::ProductGen(const std::vector<std::size_t>& ends):
   m_ends(ends), m_counters(ends.size(), 0), m_ncounters(ends.size()){
-  
-  for(auto& i : m_ends){
-    if(i < 1) {
-      m_done = true;
-      break;
-    } else {
-      i -= 1;
+
+  if (!m_ends.empty()){
+    m_done = false;
+    for(auto& i : m_ends){
+      if(i < 1) {
+	m_done = true;
+	break;
+      } else {
+	i -= 1;
+      }
     }
   }
   
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ProductGen.h b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ProductGen.h
index ba04f6ae3fb91eee467f6337032fc5d422a9fece..11cf093037f8054782f5dea9dbf7c8c93b2f5046 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ProductGen.h
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ProductGen.h
@@ -23,7 +23,7 @@ class ProductGen{
   std::vector<std::size_t> m_ends;
   std::vector<std::size_t> m_counters;
   std::size_t m_ncounters;
-  bool m_done{false};
+  bool m_done{true};
   bool atEnd(){return (m_counters == m_ends);}
 };
 
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.cxx
index 60afc379f62f639ac927560d6b32986c18c1b1a4..93bdc1e9848512645822827eaf60edf156492434 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.cxx
@@ -60,9 +60,9 @@ TrigJetHypoToolHelperNoGrouper::pass(HypoJetVector& jetsIn,
   timer.start();
   
   if(jetsIn.empty()){   
-    timer.stop();
+    timer.update();
     bool pass = false;
-    collectData(timer.readAndReset(), collector, pass);
+    collectData(timer.read(), collector, pass);
     return pass;
   }
 
@@ -93,8 +93,8 @@ TrigJetHypoToolHelperNoGrouper::pass(HypoJetVector& jetsIn,
     }
   }
   
-  timer.stop();
-  collectData(timer.readAndReset(), collector, pass);
+  timer.update();
+  collectData(timer.read(), collector, pass);
 
   return pass;
 }
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/CMakeLists.txt b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/CMakeLists.txt
index 4b33b0daaaf3659e0275769404c7680e105938d1..66ccaa47e6f4d1e1d58fe9cad5bcf698384f4e8f 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/CMakeLists.txt
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 
 # Declare the package name:
 atlas_subdir( TrigHLTJetHypoUnitTests )
@@ -18,24 +18,15 @@ atlas_add_component( TrigHLTJetHypoUnitTests
                      exerciser/components/*.cxx
                      LINK_LIBRARIES TrigHLTJetHypoUnitTestsLib)
 		     
-# Test(s) in the package:
-atlas_add_test( TrigHLTJetHypoTimerTest
-   SOURCES src/Timer.cxx
-   INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
-   LINK_LIBRARIES ${ROOT_LIBRARIES} TrigHLTJetHypoLib )
-
-atlas_add_test( TrigHLTJetHypoUnitTests
+atlas_add_test( TrigHLTJetHypoUnitTestsg
    SOURCES tests/all_tests.cxx
    tests/CombinationsGenTest.cxx
-   tests/DijetDEtaMassConditionTest.cxx
-   tests/EtaEtConditionTest.cxx
-   tests/FlowEdgeTest.cxx
-   tests/FlowNetworkTest.cxx
-   tests/LlpCleanerTest.cxx
-   tests/LooseCleanerTest.cxx
+   tests/ProductGenTest.cxx
+   tests/JetGroupProductTest.cxx
+   tests/JetGroupUnionTest.cxx
    tests/TLorentzVectorFactoryTest.cxx
-   tests/TightCleanerTest.cxx
    tests/xAODJetCollectorTest.cxx
+   tests/JetTrigTimerTest.cxx
    INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS}
    LINK_LIBRARIES ${ROOT_LIBRARIES} GoogleTestTools ${GMOCK_LIBRARIES} TrigHLTJetHypoLib TrigHLTJetHypoUnitTestsLib )
    
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/exerciser/JetHypoExerciserCompareAlg.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/exerciser/JetHypoExerciserCompareAlg.cxx
index dfe58d2cea8d15b54392bb2adba388f10cc0885f..01f7d54d1d554a6d08fe6ce4f1ebe09598d06334 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/exerciser/JetHypoExerciserCompareAlg.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/exerciser/JetHypoExerciserCompareAlg.cxx
@@ -152,11 +152,12 @@ StatusCode JetHypoExerciserCompareAlg::execute() {
   
   timer.start();
   ATH_CHECK (execute_(m_helper0, jv, collectorName, jetCollector0, logname, pass0));
-  timer.stop();
+  timer.update();
   
   if(!m_visitDebug){
     std::stringstream ss;
-    ss << timer.elapsed() <<  " tool0 " << std::boolalpha << pass0 << '\n';
+    ss << timer.elapsed_to_update()
+       <<  " tool0 " << std::boolalpha << pass0 << '\n';
     timer.reset();
     std::ofstream outfile;
     outfile.open(logname);
@@ -173,11 +174,12 @@ StatusCode JetHypoExerciserCompareAlg::execute() {
   timer.start();
 
   ATH_CHECK (execute_(m_helper1, jv, collectorName, jetCollector1, logname, pass1));
-  timer.stop();
+  timer.update();
 
   if(!m_visitDebug){
     std::stringstream ss;
-    ss << timer.elapsed() <<  " tool1 "<< std::boolalpha << pass1 << '\n';
+    ss << timer.elapsed_to_update()
+       <<  " tool1 "<< std::boolalpha << pass1 << '\n';
     timer.reset();
     std::ofstream outfile;
     outfile.open(logname);
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/JetGroupProductTest.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/JetGroupProductTest.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ddbe24ef08157e36a55066ed9d14e57b5f56cd09
--- /dev/null
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/JetGroupProductTest.cxx
@@ -0,0 +1,106 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "gtest/gtest.h"
+#include "TrigHLTJetHypo/../src/JetGroupProduct.h"
+#include "TrigHLTJetHypo/../src/DebugInfoCollector.h"
+#include <vector>
+#include <map>
+
+using res = std::pair<std::vector<unsigned int>, bool>;
+using vec = std::vector<unsigned int>;
+
+TEST(JetGroupProductTester, empty) {
+  std::vector<std::size_t> siblings;
+  CondInd2JetGroupsInds satisfiedBy;
+  std::vector<std::size_t> condMult;
+  
+  JetGroupProduct jgp(siblings, satisfiedBy, condMult);
+  auto collector = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
+
+  EXPECT_FALSE((jgp.next(collector)).has_value());
+}
+
+
+
+TEST(JetGroupProductTester, onecond) {
+  std::vector<std::size_t> siblings{0};
+
+  CondInd2JetGroupsInds satisfiedBy;
+  satisfiedBy[0] = std::vector<std::size_t> {0, 1, 2};
+
+  std::vector<std::size_t> condMult{1};
+
+  JetGroupProduct jgp(siblings, satisfiedBy, condMult);
+
+  auto collector = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
+  
+  auto exp = std::vector<std::size_t>{0};
+  EXPECT_EQ(*(jgp.next(collector)), exp);
+
+  exp = std::vector<std::size_t>{1};
+  EXPECT_EQ(*(jgp.next(collector)), exp);
+
+  exp = std::vector<std::size_t>{2};
+  EXPECT_EQ(*(jgp.next(collector)), exp);
+
+  EXPECT_FALSE((jgp.next(collector)).has_value());
+
+}
+
+TEST(JetGroupProductTester, repeatedcond) {
+  std::vector<std::size_t> siblings{0};
+
+  CondInd2JetGroupsInds satisfiedBy;
+  satisfiedBy[0] = std::vector<std::size_t> {0, 1, 2};
+
+  std::vector<std::size_t> condMult{2};
+
+  JetGroupProduct jgp(siblings, satisfiedBy, condMult);
+
+  auto collector = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
+  
+  auto exp = std::vector<std::size_t>{0, 1};
+  EXPECT_EQ(*(jgp.next(collector)), exp);
+
+  exp = std::vector<std::size_t>{0, 2};
+  EXPECT_EQ(*(jgp.next(collector)), exp);
+
+ 
+  exp = std::vector<std::size_t>{1, 2};
+  EXPECT_EQ(*(jgp.next(collector)), exp);
+
+  EXPECT_FALSE((jgp.next(collector)).has_value());
+
+}
+
+TEST(JetGroupProductTester, twocond) {
+  std::vector<std::size_t> siblings{0, 1};
+
+  CondInd2JetGroupsInds satisfiedBy;
+  satisfiedBy[0] = std::vector<std::size_t> {0, 1, 2};
+  satisfiedBy[1] = std::vector<std::size_t> {0, 1, 2};
+
+  std::vector<std::size_t> condMult{1, 1};
+
+  JetGroupProduct jgp(siblings, satisfiedBy, condMult);
+
+  auto collector = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
+  
+  auto exp = std::vector<std::size_t>{0, 1};
+  EXPECT_EQ(*(jgp.next(collector)), exp);
+
+  exp = std::vector<std::size_t>{0, 2};
+  EXPECT_EQ(*(jgp.next(collector)), exp);
+  
+  exp = std::vector<std::size_t>{1, 2};
+  EXPECT_EQ(*(jgp.next(collector)), exp);
+
+  EXPECT_FALSE((jgp.next(collector)).has_value());
+
+  if(collector) {collector->write();}
+
+
+}
+
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/JetGroupUnionTest.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/JetGroupUnionTest.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..7f80c480ee07f7e06472ad5fe6bf7b623e487d02
--- /dev/null
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/JetGroupUnionTest.cxx
@@ -0,0 +1,157 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "gtest/gtest.h"
+#include "TrigHLTJetHypo/../src/JetGroupUnion.h"
+#include <vector>
+#include <map>
+
+using res = std::pair<std::vector<unsigned int>, bool>;
+using vec = std::vector<unsigned int>;
+
+TEST(JetGroupUnionTester, empty) {
+  std::vector<std::size_t> siblings;
+  CondInd2JetGroupsInds satisfiedBy;
+  JetGroupInd2ElemInds jg2elemjgs;
+  
+  auto collector = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
+
+  JetGroupUnion jgu(siblings, satisfiedBy, jg2elemjgs, collector);
+
+  EXPECT_FALSE((jgu.next(collector)).has_value());
+}
+
+
+TEST(JetGroupUnionTester, one_elementary_cond) {
+  std::vector<std::size_t> siblings{0};
+
+  CondInd2JetGroupsInds satisfiedBy;
+  satisfiedBy[0] = std::vector<std::size_t> {0, 1, 2};
+  std::vector<std::size_t> condMult{1};
+
+  JetGroupInd2ElemInds jg2elemjgs;
+  jg2elemjgs[0] = std::vector<std::size_t> {0};
+  jg2elemjgs[1] = std::vector<std::size_t> {1};
+  jg2elemjgs[2] = std::vector<std::size_t> {2};
+
+  auto collector = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
+
+  JetGroupUnion jgu(siblings, satisfiedBy, jg2elemjgs, collector);
+  
+  auto exp = std::vector<std::size_t>{0, 1, 2};
+  EXPECT_EQ(*(jgu.next(collector)), exp);
+
+  EXPECT_FALSE((jgu.next(collector)).has_value());
+}
+
+
+TEST(JetGroupUnionTester, two_elem_nooverlap) {
+  std::vector<std::size_t> siblings{0, 1};
+
+  CondInd2JetGroupsInds satisfiedBy;
+  satisfiedBy[0] = std::vector<std::size_t> {0, 1, 2};
+  satisfiedBy[1] = std::vector<std::size_t> {3, 4, 5};
+  std::vector<std::size_t> condMult{1};
+
+  JetGroupInd2ElemInds jg2elemjgs;
+  jg2elemjgs[0] = std::vector<std::size_t> {0};
+  jg2elemjgs[1] = std::vector<std::size_t> {1};
+  jg2elemjgs[2] = std::vector<std::size_t> {2};
+  jg2elemjgs[3] = std::vector<std::size_t> {3};
+  jg2elemjgs[4] = std::vector<std::size_t> {4};
+  jg2elemjgs[5] = std::vector<std::size_t> {5};
+
+  auto collector = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
+
+  JetGroupUnion jgu(siblings, satisfiedBy, jg2elemjgs, collector);
+  
+  auto exp = std::vector<std::size_t>{0, 1, 2, 3, 4, 5};
+  EXPECT_EQ(*(jgu.next(collector)), exp);
+
+  EXPECT_FALSE((jgu.next(collector)).has_value());
+}
+
+TEST(JetGroupUnionTester, two_elem_withoverlap) {
+  std::vector<std::size_t> siblings{0, 1};
+
+  CondInd2JetGroupsInds satisfiedBy;
+  satisfiedBy[0] = std::vector<std::size_t> {0, 1, 2};
+  satisfiedBy[1] = std::vector<std::size_t> {3, 1, 5};
+  std::vector<std::size_t> condMult{1};
+
+  JetGroupInd2ElemInds jg2elemjgs;
+  jg2elemjgs[0] = std::vector<std::size_t> {0};
+  jg2elemjgs[1] = std::vector<std::size_t> {1};
+  jg2elemjgs[2] = std::vector<std::size_t> {2};
+  jg2elemjgs[3] = std::vector<std::size_t> {3};
+  jg2elemjgs[4] = std::vector<std::size_t> {4};
+  jg2elemjgs[5] = std::vector<std::size_t> {5};
+
+  auto collector = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
+
+  JetGroupUnion jgu(siblings, satisfiedBy, jg2elemjgs, collector);
+  
+  auto exp = std::vector<std::size_t>{0, 1, 2, 3, 5};
+  EXPECT_EQ(*(jgu.next(collector)), exp);
+
+  EXPECT_FALSE((jgu.next(collector)).has_value());
+}
+
+
+TEST(JetGroupUnionTester, two_comp_nooverlap) {
+  std::vector<std::size_t> siblings{0, 1};
+
+  CondInd2JetGroupsInds satisfiedBy;
+  satisfiedBy[0] = std::vector<std::size_t> {10};
+  satisfiedBy[1] = std::vector<std::size_t> {20};
+  std::vector<std::size_t> condMult{1};
+
+  JetGroupInd2ElemInds jg2elemjgs;
+  jg2elemjgs[0] = std::vector<std::size_t> {0};
+  jg2elemjgs[1] = std::vector<std::size_t> {1};
+  jg2elemjgs[2] = std::vector<std::size_t> {2};
+  jg2elemjgs[3] = std::vector<std::size_t> {3};
+  jg2elemjgs[4] = std::vector<std::size_t> {4};
+  jg2elemjgs[5] = std::vector<std::size_t> {5};
+  jg2elemjgs[10] = std::vector<std::size_t> {0, 1, 2};
+  jg2elemjgs[20] = std::vector<std::size_t> {3, 4, 5};
+
+  auto collector = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
+
+  JetGroupUnion jgu(siblings, satisfiedBy, jg2elemjgs, collector);
+  
+  auto exp = std::vector<std::size_t>{0, 1, 2, 3, 4, 5};
+  EXPECT_EQ(*(jgu.next(collector)), exp);
+
+  EXPECT_FALSE((jgu.next(collector)).has_value());
+}
+
+
+TEST(JetGroupUnionTester, two_comp_withoverlap) {
+  std::vector<std::size_t> siblings{0, 1};
+
+  CondInd2JetGroupsInds satisfiedBy;
+  satisfiedBy[0] = std::vector<std::size_t> {10};
+  satisfiedBy[1] = std::vector<std::size_t> {20};
+  std::vector<std::size_t> condMult{1};
+
+  JetGroupInd2ElemInds jg2elemjgs;
+  jg2elemjgs[0] = std::vector<std::size_t> {0};
+  jg2elemjgs[1] = std::vector<std::size_t> {1};
+  jg2elemjgs[2] = std::vector<std::size_t> {2};
+  jg2elemjgs[3] = std::vector<std::size_t> {3};
+  jg2elemjgs[4] = std::vector<std::size_t> {4};
+  jg2elemjgs[5] = std::vector<std::size_t> {5};
+  jg2elemjgs[10] = std::vector<std::size_t> {0, 1, 2};
+  jg2elemjgs[20] = std::vector<std::size_t> {0, 1, 5};
+
+  auto collector = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
+
+  JetGroupUnion jgu(siblings, satisfiedBy, jg2elemjgs, collector);
+  
+  auto exp = std::vector<std::size_t>{0, 1, 2, 5};
+  EXPECT_EQ(*(jgu.next(collector)), exp);
+
+  EXPECT_FALSE((jgu.next(collector)).has_value());
+}
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/JetTrigTimerTest.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/JetTrigTimerTest.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..eb77e89880c5d9d81f6ad688f2279975ae835fd0
--- /dev/null
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/JetTrigTimerTest.cxx
@@ -0,0 +1,60 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TrigHLTJetHypo/../src/JetTrigTimer.h"
+#include "gtest/gtest.h"
+#include <math.h>
+#include <tuple>
+
+
+double delay(std::size_t d){
+  auto sumNum{0.};
+  for (std::size_t i = 0; i != d; ++i) {
+    sumNum  += i* atan(i);
+  }
+  return sumNum;
+}
+
+TEST(JetTrigTimerTest, exerciser) {
+
+  auto timer = JetTrigTimer(false);
+  EXPECT_EQ(timer.units(), "us");
+  
+  timer = JetTrigTimer(true);
+  EXPECT_EQ(timer.units(), "ns");
+
+  std::size_t delay_par(10000);
+
+  auto tup = timer.read_bare();
+  EXPECT_EQ(std::get<0>(tup), 0.);  // elapsed time = 0
+  EXPECT_EQ(std::get<1>(tup), 0);  // no of calls = 0
+  EXPECT_EQ(std::get<2>(tup), "ns");  // units are ns.
+
+  timer.start();
+  delay(delay_par);
+  timer.update();
+
+  tup = timer.read_bare();
+  EXPECT_GE(std::get<0>(tup), 0.0);  // elapsed time
+  EXPECT_EQ(std::get<1>(tup), 1);  // no of calls
+  EXPECT_EQ(std::get<2>(tup), "ns");  // units are ns.
+
+  timer.reset();
+  timer.start();
+  double e0 = timer.elapsed_to_now();
+  delay(10);
+  double e1 = timer.elapsed_to_now();
+  delay(10);
+  double e2 = timer.elapsed_to_update();
+  
+
+  EXPECT_GT(e1, e0);
+  EXPECT_EQ(e2, e1);
+
+  timer.reset();
+  tup = timer.read_bare();
+  EXPECT_EQ(std::get<0>(tup), 0.0);  // elapsed time = 0
+  EXPECT_EQ(std::get<1>(tup), 0);  // no of calls = 0
+  EXPECT_EQ(std::get<2>(tup), "ns");  // units are ns.
+}
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/PartitionsGenTest.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/PartitionsGenTest.cxx
deleted file mode 100644
index c2431418555599be35dcac51ade939d7bbd87b09..0000000000000000000000000000000000000000
--- a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/PartitionsGenTest.cxx
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
-*/
-
-#include "gtest/gtest.h"
-#include "TrigHLTJetHypo/TrigHLTJetHypoUtils/PartitionsGen.h"
-#include <vector>
-#include <set>
-
-using res = std::vector<std::vector<std::size_t>>;
-using vec = std::vector<std::size_t>;
-
-TEST(PartitionsGenTester, zero0) {
-
-  vec v;
-  std::size_t toAllocate{0};
-  PartitionsGen gen(toAllocate, v);
-
-
-  EXPECT_FALSE (gen.next().has_value());
-}
-
-TEST(PartitionsGenTester, m2m1) {
-  using vst = std::vector<std::size_t>;
-  std::vector<res> expected{
-    res{vst{1, 2}, vst{3}},
-    res{vst{1, 3}, vst{2}},
-    res{vst{2, 3}, vst{1}},
-    res{vst{0, 2}, vst{3}},
-    res{vst{0, 3}, vst{2}},
-    res{vst{0, 1}, vst{3}},
-    res{vst{0, 1}, vst{2}},
-    res{vst{0, 3}, vst{1}},
-    res{vst{0, 2}, vst{1}},
-    res{vst{2, 3}, vst{0}},
-    res{vst{1, 3}, vst{0}},
-    res{vst{1, 2}, vst{0}}
-  };
-  
-  vec v{2, 1};
-  std::size_t to_allocate{4};
-  PartitionsGen gen(to_allocate, v);
-
-  std::set<res> results_set;
-
-  // check values without considering order, and total number of
-  // different partitions.
-  while(true){
-    auto next = gen.next();
-    if (!next.has_value()){break;}
-    EXPECT_NE(std::find(expected.begin(),
-			expected.end(),
-			*next),
-	      expected.end());
-    results_set.insert(*next);
-  }
-
-  EXPECT_EQ (results_set.size(), expected.size());
-}
-
-TEST(PartitionsGenTester, m2m3) {
-  
-  vec v{2, 3};
-  std::size_t to_allocate{6};
-  PartitionsGen gen(to_allocate, v);
-
-  // check values without considering order, and total number of
-  // different partitions.
-  std::set<res> results_set;
-
-  while(true){
-    auto next = gen.next();
-    if (!next.has_value()){break;}
-    results_set.insert(*next);
-  }
-
-  //60 = 6.5.4.3.2/(2! 3!)
-  EXPECT_EQ (results_set.size(), 60u);
-}
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/PartitionsGrouperTest.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/PartitionsGrouperTest.cxx
deleted file mode 100644
index e8143f21e18a6ab9006e819d4cace802ff61995a..0000000000000000000000000000000000000000
--- a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/PartitionsGrouperTest.cxx
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
-  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
-*/
-
-#include "TrigHLTJetHypo/TrigHLTJetHypoUtils/HypoJetDefs.h"
-#include "TrigHLTJetHypo/TrigHLTJetHypoUtils/PartitionsGrouper.h"
-
-#include "../src/TLorentzVectorFactory.h"
-#include "../src/TLorentzVectorAsIJet.h"
-
-#include <TLorentzVector.h>
-
-#include "gtest/gtest.h"
-
-
-// using ::testing::Return;
-// using ::testing::_;
-// using ::testing::SetArgReferee;
-
-
-TEST(PartitionsGrouperTest, zeroJets){
-  HypoJetVector jets;
-  std::vector<std::size_t> mults{2, 3};
-  PartitionsGrouper pg(mults);
-  auto jb = jets.begin();
-  auto je = jets.end();
-  auto groupVectors = pg.group(jb, je);
-  EXPECT_TRUE(groupVectors.size() == 0);
-}
-
-
-TEST(PartitionsGrouperTest, test0){
-  /* basic PartitionsGrouper testing 
-   */
-
-  auto factory = TLorentzVectorFactory();
-  
-  HypoJetVector jets;
-
-  constexpr double eta{0.5}; 
-  for(int i = 1; i < 6; ++i){
-    auto tlv = factory.make(eta, i);
-    auto tl_j = std::make_shared<TLorentzVectorAsIJet>(tlv);
-    jets.push_back(tl_j);
-  }
-
-  EXPECT_TRUE(jets.size() == 5);  // started at 1, not 0...
-  std::vector<std::size_t> mults{2, 3};
-  PartitionsGrouper pg(mults);
-  auto jb = jets.begin();
-  auto je = jets.end();
-  auto groupVectors = pg.group(jb, je);
-
-  // need to place 5 jets in 2 conditions
-  // 5.4.3.2/(2!3!) ways ot to this (j1j2 and j2j1 in c1 is counted once).
-  EXPECT_TRUE(groupVectors.size() == 10);
-  for(const auto& groupVector: groupVectors){
-    EXPECT_TRUE(groupVector.size()== 2); // two conditions objects
-    EXPECT_TRUE(groupVector[0].size()== 2); // two jets first group
-    EXPECT_TRUE(groupVector[1].size()== 3); // three jets second group
-  }
-}
-
-TEST(PartitionsGrouperTest, test1){
-  /* basic PartitionsGrouper testing 
-   */
-
-  auto factory = TLorentzVectorFactory();
-  
-  HypoJetVector jets;
-
-  constexpr double eta{0.5}; 
-  for(int i = 1; i < 10; ++i){
-    auto tlv = factory.make(eta, i);
-    auto tl_j = std::make_shared<TLorentzVectorAsIJet>(tlv);
-    jets.push_back(tl_j);
-  }
-
-  EXPECT_TRUE(jets.size() == 9);  // started at 1, not 0...
-  std::vector<std::size_t> mults{2, 3, 1};
-  PartitionsGrouper pg(mults);
-  auto jb = jets.begin();
-  auto je = jets.end();
-  auto groupVectors = pg.group(jb, je);
-
-  // need to place 9 jets in 3 conditions requiring 6 jets
-  // 9.8.7.6.5.4/(2!3!1!) ways ot to this (j1j2 and j2j1 in c1 is counted once).
-  EXPECT_TRUE(groupVectors.size() == 5040);
-  for(const auto& groupVector: groupVectors){
-    EXPECT_TRUE(groupVector.size()== 3); // two conditions objects
-    EXPECT_TRUE(groupVector[0].size()== 2); // two jets first group
-    EXPECT_TRUE(groupVector[1].size()== 3); // three jets second group
-    EXPECT_TRUE(groupVector[2].size()== 1); // one jet third group
-  }
-}
-
-
-TEST(PartitionsGrouperTest, test2){
-  /* too few jets 
-   */
-
-  auto factory = TLorentzVectorFactory();
-  
-  HypoJetVector jets;
-
-  constexpr double eta{0.5}; 
-  for(int i = 1; i < 5; ++i){
-    auto tlv = factory.make(eta, i);
-    auto tl_j = std::make_shared<TLorentzVectorAsIJet>(tlv);
-    jets.push_back(tl_j);
-  }
-
-  EXPECT_TRUE(jets.size() == 4);  // started at 1, not 0...
-  std::vector<std::size_t> mults{2, 3};
-  PartitionsGrouper pg(mults);
-  auto jb = jets.begin();
-  auto je = jets.end();
-  auto groupVectors = pg.group(jb, je);
-
-  // need to place 5 jets in 2 conditions
-  // 5.4.3.2/(2!3!) ways to do this (j1j2 and j2j1 in c1 is counted once).
-  EXPECT_TRUE(groupVectors.size() == 0);
-}
-
-
-TEST(PartitionsGrouperTest, SingleJetGrouperBehavioiur){
-  /* Test SingleJetGrouper bevaiour - split incoming jet collection
-   * of n jets int n groups of 1 jet.
-   */
-
-  auto factory = TLorentzVectorFactory();
-  
-  HypoJetVector jets;
-
-  constexpr double eta{0.5}; 
-  for(int i = 1; i < 7; ++i){
-    auto tlv = factory.make(eta, i);
-    auto tl_j = std::make_shared<TLorentzVectorAsIJet>(tlv);
-    jets.push_back(tl_j);
-  }
-
-  EXPECT_TRUE(jets.size() == 6);  // started at 1, not 0...
-
-  //------------
-  std::vector<std::size_t> mults{1}; // determines "single jet" behaviour
-  //------------
-
-  PartitionsGrouper pg(mults);
-  auto jb = jets.begin();
-  auto je = jets.end();
-  auto groupVectors = pg.group(jb, je);
-
-  // need to place 1 jet in 1 conditions
-  // 6  ways to do this
-
-  // grouper returns {[(1)], [(2)], [(3)], [(4)], [(5)], [(6)]}
-  // "{" delimits vector<HypoJetGroupsVector<
-  // "[" delimits HypoJetGroupVector (with one HypoJetVector entry)
-  // "(" delimits HypoJetVector (with one jet entry)
-  
-  EXPECT_TRUE(groupVectors.size() ==6);
-  for (const auto& gv : groupVectors){
-    EXPECT_TRUE(gv.size() == 1);
-    for(const auto & jv : gv){
-      EXPECT_TRUE(jv.size() == 1);
-    }
-  }
-}
-
-
-
-
-TEST(PartitionsGrouperTest, CombinationsGrouperBehavioiur){
-  /* Test SingleJetGrouper bevaiour - split incoming jet collection
-   * of n jets int n groups of 1 jet.
-   */
-
-  auto factory = TLorentzVectorFactory();
-  
-  HypoJetVector jets;
-
-  constexpr double eta{0.5}; 
-  for(int i = 1; i < 7; ++i){
-    auto tlv = factory.make(eta, i);
-    auto tl_j = std::make_shared<TLorentzVectorAsIJet>(tlv);
-    jets.push_back(tl_j);
-  }
-
-  EXPECT_TRUE(jets.size() == 6);  // started at 1, not 0...
-
-  //------------
-  std::vector<std::size_t> mults{2};  // "combinations" behaviour
-  //------------
-
-  PartitionsGrouper pg(mults);
-  auto jb = jets.begin();
-  auto je = jets.end();
-  auto groupVectors = pg.group(jb, je);
-
-  // need to place 1 jet in 1 conditions
-  // 6  ways to do this
-
-  // grouper returns {[(1, 2)], [(1, 3)], [(1, 4)], [(1, 5)], [(1, 6)],
-  //                  [(2, 3)], [(2, 4)], [(2, 5)], [(2,6)]
-  //                  [(3, 4)], [(3, 5)], [(3, 6)],
-  //                  [(4, 5)], [(4, 6)],
-  //                  [(5, 6)]
-  //                 }
-  // "{" delimits vector<HypoJetGroupsVector<
-  // "[" delimits HypoJetGroupVector (with one HypoJetVector entry)
-  // "(" delimits HypoJetVector (with one jet entry)
-  
- 
-
-  EXPECT_TRUE(groupVectors.size() == 15);
-  for (const auto& gv : groupVectors){
-    EXPECT_TRUE(gv.size() == 1);
-    for(const auto & jv : gv){
-      EXPECT_TRUE(jv.size() == 2);
-    }
-  }
-}
-
-TEST(PartitionsGrouperTest, FullPartitionGrouperBehavioiur){
-  /* Test SingleJetGrouper bevaiour - split incoming jet collection
-   * of n jets int n groups of 1 jet.
-   */
-
-  auto factory = TLorentzVectorFactory();
-  
-  HypoJetVector jets;
-
-  constexpr double eta{0.5}; 
-  for(int i = 1; i < 7; ++i){
-    auto tlv = factory.make(eta, i);
-    auto tl_j = std::make_shared<TLorentzVectorAsIJet>(tlv);
-    jets.push_back(tl_j);
-  }
-
-  EXPECT_TRUE(jets.size() == 6);  // started at 1, not 0...
-
-  //------------
-  std::vector<std::size_t> mults{1, 1};
-  //------------
-
-  
-  PartitionsGrouper pg(mults);
-  auto jb = jets.begin();
-  auto je = jets.end();
-  auto groupVectors = pg.group(jb, je);
-
-  // need to place 1 jet in 1 conditions
-  // 6  ways to do this
-
-  // grouper returns
-  // {
-  //   [(1), (2)], [(1), (3)], [(1), (4)], [(1), (5)], [(1), (6)],
-  //   [(2), (1)], [(2), (3)], [(2), (4)], [(2), (5)], [(2), (6)],
-  //   [(3), (1)], [(3), (2)], [(3), (4)], [(3), (5)], [(3), (6)],
-  //   [(4), (1)], [(4), (2)], [(4), (3)], [(4), (5)], [(4), (6)],
-  //   [(5), (1)], [(5), (2)], [(5), (3)], [(5), (4)], [(5), (6)],
-  //   [(6), (1)], [(6), (2)], [(6), (3)], [(6), (4)], [(6), (5)],
-  // }
-  //
-  // "{" delimits vector<HypoJetGroupsVector<
-  // "[" delimits HypoJetGroupVector (with one HypoJetVector entry)
-  // "(" delimits HypoJetVector (with one jet entry)
-  
- 
-
-  EXPECT_TRUE(groupVectors.size() == 30);
-  for (const auto& gv : groupVectors){
-    EXPECT_TRUE(gv.size() == 2);
-    for(const auto & jv : gv){
-      EXPECT_TRUE(jv.size() == 1);
-    }
-  }
-}
-
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/PartitionsGroupsMatcherMTTest.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/PartitionsGroupsMatcherMTTest.cxx
deleted file mode 100644
index b6708b3f4df116de60932edf8ceba4180b9d257c..0000000000000000000000000000000000000000
--- a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/PartitionsGroupsMatcherMTTest.cxx
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
-  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
-*/
-
-#include "TrigHLTJetHypo/../src/PartitionsGroupsMatcherMT.h"
-#include "TrigHLTJetHypo/TrigHLTJetHypoUtils/PartitionsGrouper.h"
-#include "TrigHLTJetHypo/TrigHLTJetHypoUtils/HypoJetDefs.h"
-#include "TrigHLTJetHypo/../src/ConditionsDefsMT.h"
-#include "TrigHLTJetHypo/../src/conditionsFactoryMT.h"
-#include "TrigHLTJetHypo/../src/DebugInfoCollector.h"
-#include "TrigHLTJetHypo/../src/xAODJetCollector.h"
-#include "TrigHLTJetHypo/TrigHLTJetHypoUtils/CombinationsGrouper.h"
-
-#include "../src/TLorentzVectorAsIJet.h"
-#include "../src/TLorentzVectorFactory.h"
-#include "gtest/gtest.h"
-#include "gmock/gmock.h"
-
-#include <TLorentzVector.h>
-#include <memory>
-#include <iostream>
-
-
-/*
- * PartitionsMatcher functionality tested:
- * 0 fail if no jet vector indices
- * 1 fail if no if there are fewer passing jets than conditions
- * 2 pass if there are at least as many passing jets as conditions
- * 3 conditions are ordered by threshold
- * 4 jets are ordered by Et
- *
- * Mocked objects:
- * - jet: will be ordered on ET, need TLorentzVector, hence
- *        MockJetWithLorentzVector
- * - ICondition
- */
-
-using ::testing::Return;
-using ::testing::_;
-using ::testing::SetArgReferee;
-
-
-class PartitionsGroupsMatcherMTTest: public ::testing::Test {
-public:
-  PartitionsGroupsMatcherMTTest() {
-
-
-    std::vector<double> etaMins{-1., -1., -1.};
-    std::vector<double> etaMaxs{1., 1, 1.};
-    std::vector<double> thresholds{100., 120., 140.};
-    std::vector<int> asymmetricEtas{0, 0, 0};
-
-    m_conditions = conditionsFactoryEtaEtMT(etaMins, etaMaxs,
-                                            thresholds, asymmetricEtas);
-    m_nconditions = m_conditions.size();
-  }
-
-  ConditionsMT m_conditions;
-  int m_nconditions;
-  bool m_debug{false};
-};
-
-std::vector<HypoJetGroupVector>
-makejetGroupsPartitionMT(HypoJetIter b, HypoJetIter e){
-  std::vector<std::size_t> mults{1, 1};
-  PartitionsGrouper g(mults);  // single jet groups
-  return g.group(b, e);
-}
-
-TEST_F(PartitionsGroupsMatcherMTTest, debugFlagIsFalse){
-  /* idiot tst to ensure dbug flag is of prior to commiting */
-   EXPECT_FALSE(m_debug);
-}
-
-TEST_F(PartitionsGroupsMatcherMTTest, zeroInputJets){
-  /* test with 0 jets - fails, no passed for failed jets */
-
-  PartitionsGroupsMatcherMT matcher(std::move(m_conditions));
-  // 
-  EXPECT_TRUE(true);
-  HypoJetVector jets;
-  auto groups = makejetGroupsPartitionMT(jets.begin(), jets.end());
-  EXPECT_TRUE(groups.empty());
-}
-
-
-TEST_F(PartitionsGroupsMatcherMTTest, tooFewSelectedJets){
-   /* pass fewer jets than indicies. Fails, all jets are bad */
- 
-   double eta{5};
-   double et{100.};
- 
-   auto factory = TLorentzVectorFactory();
-   auto tlv = factory.make(eta, et);
-
-   auto tl_j = std::make_shared<const TLorentzVectorAsIJet>(tlv);
-   HypoJetVector jets;
-   jets.push_back(tl_j);
-
-   auto groupsVec = makejetGroupsPartitionMT(jets.begin(), jets.end());
-   auto visitor = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
-   if(m_debug){
-     visitor.reset(new DebugInfoCollector("toofewselectedjets"));
-   }
-
-   PartitionsGroupsMatcherMT matcher(std::move(m_conditions));
- 
-   xAODJetCollector jetCollector;
-
-   for (const auto& groups : groupsVec){
-     auto pass = matcher.match(groups.begin(),
-			       groups.end(),
-			       jetCollector,
-			       visitor);
-     
-     //f(visitor){visitor->write();}
-     EXPECT_TRUE(pass.has_value());
-     EXPECT_FALSE(*pass);
-     EXPECT_TRUE(jetCollector.empty());
-
-     if(visitor){visitor->write();}
-
-   }
- }
- 
-
-TEST_F(PartitionsGroupsMatcherMTTest, PassingJets){
-   /* pass fewer jets than indicies. Fails, all jets are bad */
-
-
-  std::vector<double> ets {200., 220., 225.};
-  double eta{0.5};
-  
-  HypoJetVector jets;
-  jets.resize(ets.size());
-
-  auto factory = TLorentzVectorFactory();
-  
-  auto makeJetFromEt = [&factory, eta](double et){
-    return std::make_shared<const TLorentzVectorAsIJet>(factory.make(eta, et));
-  };
-
-  std::transform(ets.begin(),
-		 ets.end(),
-		 jets.begin(),
-		 makeJetFromEt);
-
-  EXPECT_EQ(ets.size(), jets.size());
-  EXPECT_EQ(m_conditions.size(), 3u);
-  // --------
-  std::vector<std::size_t> mults{1, 1, 1};
-  PartitionsGrouper g(mults);  // single jet groups
-
-  auto b = jets.begin();
-  auto e = jets.end();
-  auto groupsVec = g.group(b, e);
-  //{[(1),(2), (3)], [(1), (3), (2)], [(2), (1), (3)], [(2), (3), (1)],
-  // [(3), (1), (2)], [(3), (2), (1)]}
-  EXPECT_EQ(groupsVec.size(), 6u);
-  
-  auto visitor = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
-
-
-  if(m_debug){
-    visitor.reset(new DebugInfoCollector("PassingJetsTest"));
-  }
-
-  PartitionsGroupsMatcherMT matcher(std::move(m_conditions));
-
-  for (const auto& groups : groupsVec){
-    EXPECT_EQ(groups.size(), 3);
-    xAODJetCollector jetCollector;
-
-    auto pass = matcher.match(groups.begin(),
-			      groups.end(),
-			      jetCollector,
-			      visitor);
-    EXPECT_TRUE(pass.has_value());
-    EXPECT_TRUE(*pass);
-    EXPECT_EQ(jetCollector.size(), 9);  // non-xAOD HypoJets now collected
-  }
-
-  if(visitor){visitor->write();}
-
-}
- 
-
-
-TEST_F(PartitionsGroupsMatcherMTTest, Passing3Failing1){
-   /* pass fewer jets than indicies. Fails, all jets are bad */
-
-
-  std::vector<double> ets {200., 220., 225., 50};
-  double eta{0.5};
-  
-  HypoJetVector jets;
-  jets.resize(ets.size());
-
-  auto factory = TLorentzVectorFactory();
-  
-  auto makeJetFromEt = [&factory, eta](double et){
-    return std::make_shared<const TLorentzVectorAsIJet>(factory.make(eta, et));
-  };
-
-  std::transform(ets.begin(),
-		 ets.end(),
-		 jets.begin(),
-		 makeJetFromEt);
-
-  EXPECT_EQ(ets.size(), jets.size());
-  EXPECT_EQ(m_conditions.size(), 3u);
-
-  std::vector<std::size_t> mults{1, 1, 1};
-  PartitionsGrouper g(mults);  // single jet groups
-
-  auto b = jets.begin();
-  auto e = jets.end();
-  auto groupsVec = g.group(b, e);
-  auto visitor = std::unique_ptr<ITrigJetHypoInfoCollector>(nullptr);
-
-  if(m_debug){
-    visitor.reset(new DebugInfoCollector("Passing3Failing1Test"));
-  }
-  PartitionsGroupsMatcherMT matcher(std::move(m_conditions));
-  xAODJetCollector jetCollector;
-
-  std::size_t npass{0};
-  std::size_t ncall{0};
-
-  for (const auto& groups : groupsVec){
-    ++ncall;
-    auto pass = matcher.match(groups.begin(),
-			      groups.end(),
-			      jetCollector,
-			      visitor);
-    if(pass.has_value()){
-      if(*pass){++npass;}
-    }
-  }
-
-  // calls: 4 jets, conditions need three: 4.3.2  = 24
-  // pass: ignore failing jet. 3.2.1 = 6
-  EXPECT_EQ(npass,  6u);
-  EXPECT_EQ(ncall, 24u);  
-}
- 
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/ProductGenTest.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/ProductGenTest.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..57e888667113a2cbc921eccd5b4700ec214b17e6
--- /dev/null
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypoUnitTests/tests/ProductGenTest.cxx
@@ -0,0 +1,54 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "gtest/gtest.h"
+#include "TrigHLTJetHypo/../src/ProductGen.h"
+#include <vector>
+
+using res = std::pair<std::vector<unsigned int>, bool>;
+using vec = std::vector<unsigned int>;
+
+TEST(ProductGenTester, noargs) { 
+  ProductGen gen;
+  EXPECT_FALSE((gen.next()).has_value());
+}
+
+TEST(ProductGenTester, empty) {
+  std::vector<std::size_t> ends;
+  ProductGen gen(ends);
+  EXPECT_FALSE((gen.next()).has_value());
+}
+
+TEST(ProductGenTester, zeroend) { 
+  ProductGen gen(std::vector<std::size_t>({0,2}));
+
+  // no values due to and end value of 0.
+  EXPECT_FALSE((gen.next()).has_value());
+}
+
+
+TEST(ProductGenTester, ends12) { 
+  ProductGen gen(std::vector<std::size_t>({2, 3}));
+
+  std::vector<std::size_t> exp{0, 0};
+  EXPECT_EQ(*(gen.next()), exp);
+
+  exp = std::vector<std::size_t>{1, 0};
+  EXPECT_EQ(*gen.next(), exp);
+
+  exp = std::vector<std::size_t>{0, 1};
+  EXPECT_EQ(*gen.next(), exp);
+
+  exp = std::vector<std::size_t>{1, 1};
+  EXPECT_EQ(*gen.next(), exp);
+
+  exp = std::vector<std::size_t>{0, 2};
+  EXPECT_EQ(*gen.next(), exp);
+  
+  exp = std::vector<std::size_t>{1, 2};
+  EXPECT_EQ(*gen.next(), exp);
+  
+  EXPECT_FALSE((gen.next()).has_value());
+}
+