ATLAS Offline Software
BuildCombinations.icc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 namespace DerivationFramework { namespace TriggerMatchingUtils {
6 
7  template <typename T, typename PROJ>
8  bool insertIntoSortedVector(std::vector<T>& vec, const T& ele,
9  PROJ proj)
10  {
11  auto itr = std::ranges::lower_bound(vec, proj(ele), {}, proj);
12  // if no lower bound is found or the found element doesn't equal ele then
13  // insert ele at this point
14  if (itr == vec.end() || *itr != ele) {
15  vec.insert(itr, ele);
16  return true;
17  }
18  else
19  return false;
20  }
21 
22  template <typename R, typename PROJ>
23  std::vector<typename R::value_type> sorted(const R& r, PROJ proj)
24  {
25  std::vector<typename R::value_type> output(std::begin(r), std::end(r));
26  std::ranges::sort(output, {}, proj);
27  return output;
28  }
29 
30 
31  template <typename T, typename INNERPROJ, typename OUTERPROJ>
32  std::vector<std::vector<T>> getAllDistinctCombinations(
33  std::vector<RangedItr<typename std::vector<T>::const_iterator>>& inputs,
34  INNERPROJ innerproj,
35  OUTERPROJ outerproj)
36  {
37  // This is where the output will go
38  std::vector<std::vector<T>> output;
39 
40  // If inputs is empty or if any of the ranges within inputs are empty then
41  // no combinations will be found!
42  if (inputs.empty() || std::any_of(inputs.begin(), inputs.end(),
43  [] (const auto& rangedItr) { return rangedItr.exhausted(); }) )
44  return output;
45 
46  // This is the current output combination being built
47  std::vector<T> currentCombination;
48 
49  // This iterator points to where in the vector of ranged iterators we are
50  // looking. This can therefore often be acting as an iterator to and
51  // iterator! *itr is an iterator, **itr is a const T&!
52  // To disambiguate I will refer to itr as the outer iterator and *itr as the
53  // inner iterator.
54  auto itr = inputs.begin();
55  while (true) {
56  // Have we exhausted our current inner iterator?
57  if (itr == inputs.end() || itr->exhausted() ) {
58  // If the outer iterator has returned to the beginning then we're done
59  // and every combination has been checked.
60  if (itr == inputs.begin() )
61  break;
62  // otherwise we need to continue. To do this we
63  // - reset the current inner iterator. This is so that the next time we
64  // return to it we look through all the elements again
65  // - step the outer iterator back
66  // - remove the last element of the current combination. That
67  // element will be replaced by the next valid one from the list *now*
68  // pointed to by the outer iterator.
69  if (itr != inputs.end() )
70  itr->restart();
71  --itr;
72  currentCombination.pop_back();
73  } //> end if inner iterator is exhausted
74  else {
75  // Check if the pointed-to element is already in the current combination
76  const T& currentCandidate = **itr;
77  // Now that we've accessed this value, step past it
78  ++*itr;
79  if (std::find(
80  currentCombination.begin(),
81  currentCombination.end(),
82  currentCandidate) == currentCombination.end() ) {
83  // If it isn't, then add it.
84  currentCombination.push_back(currentCandidate);
85  // Step the outer iterator forwards so we're looking at the next list
86  ++itr;
87  if (itr == inputs.end() ) {
88  // If we're at the end then this is a complete combination, add it
89  // to the output.
90  insertIntoSortedVector(output,
91  sorted(currentCombination, innerproj),
92  outerproj);
93  }
94  } //> end if currentCandidate is in currentCombination
95  } //> end else (if inner iterator is exhausted)
96  } //> end while loop
97  return output;
98  } //> end function getAllDistinctCombinations
99 
100 }} //> end namespace DerivationFramework::TriggerMatchingUtils