LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
FindManyInChainP.tcc
Go to the documentation of this file.
1 /**
2  * @file lardata/Utilities/FindManyInChainP.tcc
3  * @brief Template implementation for `FindManyInChainP.h`.
4  * @author Gianluca Petrillo (petrillo@fnal.gov)
5  * @date June 26, 2017
6  *
7  */
8 
9 #ifndef LARDATA_UTILITIES_FINDMANYINCHAINP_TCC
10 #define LARDATA_UTILITIES_FINDMANYINCHAINP_TCC
11 
12 #ifndef LARDATA_UTILITIES_FINDMANYINCHAINP_H
13 #error "FindManyInChainP.tcc must not be included directly. Include FindManyInChainP.h instead."
14 #endif // LARDATA_UTILITIES_FINDMANYINCHAINP_H
15 
16 // framework
17 #include "art/Framework/Principal/Handle.h"
18 #include "canvas/Persistency/Common/Assns.h"
19 #include "canvas/Persistency/Common/FindManyP.h"
20 #include "canvas/Persistency/Provenance/BranchDescription.h"
21 #include "canvas/Utilities/Exception.h"
22 
23 // C/C++ standard library
24 #include <algorithm> // std::lower_bound(), std::sort(), std::transform()...
25 #include <iterator> // std::begin(), std::cbegin(), std::distance()...
26 #include <map>
27 #include <tuple> // std::tuple_cat(), ...
28 #include <type_traits> // std::decay_t<>, std::enable_if_t<>, ...
29 #include <utility> // std::pair<>, std::move(), std::declval()...
30 
31 //------------------------------------------------------------------------------
32 //--- template implementation
33 //---
34 namespace lar {
35 
36  namespace details {
37 
38  //--------------------------------------------------------------------------
39  // this is a copy of things in cetlib; should be replaced by the original
40  // if they get exposed
41  template <class T, class R /* = void */>
42  struct enable_if_type_exists {
43  using type = R;
44  };
45 
46  //--------------------------------------------------------------------------
47  /// Trait evaluating to true if H is a art Handle type.
48  template <typename H, typename = void>
49  struct is_handle : public std::false_type {};
50 
51  template <typename H>
52  struct is_handle<H, enable_if_is_handle_t<H>> : public std::true_type {};
53 
54  template <typename H>
55  constexpr bool is_handle_v = is_handle<H>::value;
56 
57  //--------------------------------------------------------------------------
58  template <typename T>
59  struct is_art_ptr_impl : public std::false_type {};
60 
61  template <typename T>
62  struct is_art_ptr_impl<art::Ptr<T>> : public std::true_type {};
63 
64  /// Whether the specified type is an art::Ptr.
65  template <typename T>
66  using is_art_ptr = is_art_ptr_impl<std::decay_t<T>>;
67 
68  //--------------------------------------------------------------------------
69 
70  /// Hosts as `type` the type in position `N` (first is `0`).
71  // this might be implemented with std::tuple_element;
72  // here we get control on error messages
73  template <unsigned int N, typename... Args>
74  struct get_type;
75 
76  template <unsigned int N, typename... Args>
77  using get_type_t = typename get_type<N, Args...>::type;
78 
79  template <unsigned int N, typename First, typename... Others>
80  struct get_type<N, First, Others...> {
81  static_assert(N <= sizeof...(Others), "No type in the specified position.");
82  using type = get_type_t<N - 1, Others...>;
83  }; // get_type<>
84 
85  template <typename First, typename... Others>
86  struct get_type<0U, First, Others...> {
87  using type = First;
88  };
89 
90  //--------------------------------------------------------------------------
91 
92  template <unsigned int NCopies>
93  struct TupleAppender {
94  template <typename Tuple, typename T>
95  static auto append(Tuple&& t, T const& value)
96  {
97  return TupleAppender<(NCopies - 1)>::append(
98  std::tuple_cat(std::forward<Tuple>(t), std::make_tuple(value)), value);
99  }
100  }; // TupleAppender<>
101 
102  template <>
103  struct TupleAppender<0> {
104  template <typename Tuple, typename T>
105  static decltype(auto) append(Tuple&& t, T const&)
106  {
107  return std::forward<Tuple>(t);
108  }
109  }; // TupleAppender<0>
110 
111  template <unsigned int NCopies, typename T, typename Tuple>
112  auto appendToTuple(Tuple&& t, T value)
113  {
114  return TupleAppender<NCopies>::append(std::forward<Tuple>(t), value);
115  }
116 
117  //--------------------------------------------------------------------------
118  /// Returns the input tag of the product identified by the handle.
119  template <typename Handle>
120  art::InputTag tagFromHandle(Handle&& handle)
121  {
122  return handle.provenance()->inputTag();
123  }
124 
125  /// Returns the input tag of the product identified by `id`.
126  template <typename Data, typename Event>
127  art::InputTag tagFromProductID(art::ProductID const& id, Event const& event)
128  {
129  // This is not efficient for repeated queries--perhaps a map can
130  // be created that maps ProductID to input tag.
131  auto pd = event.getProductDescription(id);
132  if (pd != nullptr) { return pd->inputTag(); }
133  throw art::Exception(art::errors::ProductNotFound)
134  << "Couldn't find data product with product ID " << id << "\n";
135  } // tagFromProductID()
136 
137  //--------------------------------------------------------------------------
138  template <typename Value>
139  class SimpleDataIndex {
140  public:
141  using Value_t = Value;
142 
143  /// Constructor: indexes pointers to data in the specified collection.
144  template <typename BeginIter, typename EndIter>
145  SimpleDataIndex(BeginIter begin, EndIter end)
146  {
147  init(begin, end);
148  }
149 
150  /// Constructor: indexes pointers to data in the specified collection.
151  template <typename Coll>
152  SimpleDataIndex(Coll& data)
153  {
154  using std::begin;
155  using std::end;
156  init(begin(data), end(data));
157  }
158 
159  /// Returns a pointer to the matched data, nullptr if none.
160  std::size_t const* operator()(Value_t const& key) const
161  {
162  auto const e = index.cend();
163  auto const it = std::lower_bound(index.cbegin(), e, key, comparer());
164  return ((it == e) || (key < it->first)) ? nullptr : &(it->second);
165  }
166 
167  private:
168  using IndexElement_t = std::pair<Value_t const*, std::size_t>;
169 
170  std::vector<IndexElement_t> index; ///< The index.
171 
172  template <typename BeginIter, typename EndIter>
173  void init(BeginIter begin, EndIter end)
174  {
175  index.reserve(std::distance(begin, end)); // hope it's fast
176  std::size_t i = 0;
177  for (auto it = begin; it != end; ++it, ++i)
178  index.emplace_back(&*it, i);
179  std::sort(index.begin(), index.end(), comparer());
180  }
181 
182  struct KeyComparer {
183  bool operator()(Value_t const& a, Value_t const& b) const { return a < b; }
184  bool operator()(IndexElement_t const& a, Value_t const& b) const { return *(a.first) < b; }
185  bool operator()(Value_t const& a, IndexElement_t const& b) const { return a < *(b.first); }
186  bool operator()(IndexElement_t const& a, IndexElement_t const& b) const
187  {
188  return *(a.first) < *(b.first);
189  }
190  }; // struct KeyComparer
191 
192  static auto comparer() { return KeyComparer(); }
193 
194  }; // class SimpleDataIndex
195 
196  template <typename Key, typename Value>
197  class DataIndex {
198  public:
199  using Key_t = Key;
200  using Value_t = Value;
201  using ValuePtr_t = std::add_pointer_t<Value_t>;
202 
203  /// Constructor: indexes pointers to data in the specified collection.
204  template <typename BeginIter, typename EndIter, typename KeyExtractor>
205  DataIndex(BeginIter begin, EndIter end, KeyExtractor&& getKey)
206  {
207  init(begin, end, std::forward<KeyExtractor>(getKey));
208  }
209 
210  /// Constructor: indexes pointers to data in the specified collection.
211  template <typename Coll, typename KeyExtractor>
212  DataIndex(Coll& data, KeyExtractor&& getKey)
213  {
214  using std::begin;
215  using std::end;
216  init(begin(data), end(data), std::forward<KeyExtractor>(getKey));
217  }
218 
219  /// Returns a pointer to the matched data, nullptr if none.
220  ValuePtr_t operator()(Key_t const& key) const
221  {
222  auto const e = index.cend();
223  auto const it = std::lower_bound(index.cbegin(), e, key, comparer());
224  return ((it == e) || (key < it->first)) ? nullptr : it->second;
225  }
226 
227  private:
228  using IndexElement_t = std::pair<Key_t, Value_t*>;
229 
230  std::vector<IndexElement_t> index; ///< The index.
231 
232  template <typename BeginIter, typename EndIter, typename KeyExtractor>
233  void init(BeginIter begin, EndIter end, KeyExtractor&& getKey)
234  {
235  index.reserve(std::distance(begin, end)); // hope it's fast
236  for (auto it = begin; it != end; ++it)
237  index.emplace_back(getKey(*it), &*it);
238  std::sort(index.begin(), index.end(), comparer());
239  }
240 
241  struct KeyComparer {
242  bool operator()(Key_t const& a, Key_t const& b) const { return a < b; }
243  bool operator()(IndexElement_t const& a, Key_t const& b) const { return a.first < b; }
244  bool operator()(Key_t const& a, IndexElement_t const& b) const { return a < b.first; }
245  bool operator()(IndexElement_t const& a, IndexElement_t const& b) const
246  {
247  return a.first < b.first;
248  }
249  }; // struct KeyComparer
250 
251  static auto comparer() { return KeyComparer(); }
252 
253  }; // class DataIndex
254 
255  /// Returns a new index for data using the keys extracted by getKey.
256  template <typename Coll, typename KeyExtractor>
257  auto makeIndex(Coll& data, KeyExtractor&& getKey)
258  {
259  using Value_t = typename Coll::value_type;
260  using Key_t = std::decay_t<decltype(getKey(std::declval<Value_t>()))>;
261  return DataIndex<Key_t, Value_t>(data, std::forward<KeyExtractor>(getKey));
262  }
263 
264  /// Returns a new index for data in range using the data itself as key.
265  template <typename BeginIter, typename EndIter>
266  auto makeSimpleIndex(BeginIter begin, EndIter end)
267  {
268  using Value_t = std::decay_t<decltype(*begin)>;
269  return SimpleDataIndex<Value_t>(begin, end);
270  }
271 
272  /// Returns a new index for data using the data itself as key.
273  template <typename Coll>
274  auto makeSimpleIndex(Coll& data)
275  {
276  using Value_t = typename Coll::value_type;
277  return SimpleDataIndex<Value_t>(data);
278  }
279 
280  //--------------------------------------------------------------------------
281  //--- finders implementation
282  //---
283 
284  namespace AssociationFinderBase {
285 
286  /// Information on a single source: its index in the source list, and
287  /// "key" (it can be any sortable object).
288  template <typename Key = std::size_t>
289  struct SourceIDinfo_t : public std::tuple<Key, std::size_t> {
290  using Base_t = std::tuple<Key, std::size_t>;
291  using Key_t = Key;
292 
293  /// Returns the key of the art pointer to this source element.
294  Key_t const& key() const { return std::get<0>(*this); }
295  /// Returns the index of this source element within the source list.
296  std::size_t position() const { return std::get<1>(*this); }
297 
298  using Base_t::Base_t; // inherit constructor
299 
300  /// Comparison functor: compares art pointer keys.
301  struct Comparer_t {
302  bool operator()(SourceIDinfo_t const& a, SourceIDinfo_t const& b) const
303  {
304  return a.key() < b.key();
305  }
306  bool operator()(SourceIDinfo_t const& a, Key_t const& b) const { return a.key() < b; }
307  bool operator()(Key_t const& a, SourceIDinfo_t const& b) const { return a < b.key(); }
308  }; // struct SourceIDinfo_t::Comparer_t
309 
310  static Comparer_t comparer() { return {}; }
311  }; // struct SourceIDinfo_t
312 
313  template <typename Key = std::size_t>
314  struct SourceVector_t : private std::vector<SourceIDinfo_t<Key>> {
315  using Base_t = std::vector<SourceIDinfo_t<Key>>;
316  using Info_t = typename Base_t::value_type;
317  using Key_t = typename Info_t::Key_t;
318 
319  using const_iterator = typename Base_t::const_iterator;
320 
321  using Base_t::begin;
322  using Base_t::empty;
323  using Base_t::end;
324  using Base_t::reserve;
325  using Base_t::size;
326 
327  struct MatchConstIterator_t : public std::pair<const_iterator, bool> {
328  using Base_t = std::pair<const_iterator, bool>;
329  using Base_t::Base_t; // inherit all constructors
330  auto const& operator->() const { return std::get<0>(*this); }
331  operator bool() const { return std::get<1>(*this); }
332  bool operator!() const { return !this->operator bool(); }
333  }; // struct MatchConstIterator_t
334 
335  /// Default constructor: starts empty.
336  SourceVector_t() = default;
337 
338  /// Constructor: from the elements between the specified iterators.
339  template <typename BeginIter, typename EndIter>
340  SourceVector_t(BeginIter b, EndIter e)
341  {
342  assign(b, e);
343  }
344 
345  /// Sorts the vector according to keys.
346  void sort() { std::sort(Base_t::begin(), Base_t::end(), Info_t::comparer()); }
347 
348  /// Constructs a new element at the end of the vector.
349  void emplace_back(Key_t const& key, std::size_t pos) { Base_t::emplace_back(key, pos); }
350 
351  /// Returns iterator to element with key, if it exists.
352  /// Requires the vector to be sort()ed.
353  /// If key is not present it may return end() or the wrong element.
354  auto find(Key_t const& key) const
355  {
356  auto const e = end();
357  auto it = std::lower_bound(begin(), e, key, Info_t::comparer());
358  return MatchConstIterator_t(it, (it != e) && (it->key() == key));
359  }
360 
361  /// Resets the content to map a container delimited by the iterators.
362  template <typename BeginIter, typename EndIter>
363  void assign(BeginIter b, EndIter e)
364  {
365  Base_t::clear();
366  std::size_t i = 0;
367  for (auto it = b; it != e; ++it, ++i)
368  emplace_back(it->key(), i);
369  sort();
370  }
371 
372  }; // struct SourceVector_t
373 
374  /// Returns a tuple of NTags elements, including all specified tags and
375  /// then copies of the default one to make it N.
376  template <unsigned int NTags, typename DefaultTag, typename... InputTags>
377  auto makeTagsTuple(DefaultTag defTag, InputTags... tags)
378  {
379  return appendToTuple<(NTags - sizeof...(InputTags))>(std::make_tuple(tags...), defTag);
380  } // makeTagsTuple()
381 
382  /// A named pair of a key and all its connections.
383  /// Comparisons are based solely on key.
384  template <typename A, typename B>
385  struct Connections : private std::pair<A, std::vector<B>> {
386  using Base_t = std::pair<A, std::vector<B>>;
387  using Key_t = A;
388  using ConnectedItem_t = B;
389  using Connections_t = Connections<A, B>;
390  using Connected_t = std::tuple_element_t<1, Base_t>;
391  using Base_t::Base_t; // import pair constructors
392 
393  /// Additional constructor: key with no connected items.
394  Connections(Key_t const& key) : Base_t(key, {}) {}
395 
396  Key_t const& key() const { return std::get<0>(*this); }
397  Connected_t const& connectedTo() const { return std::get<1>(*this); }
398  Connected_t& connectedTo() { return std::get<1>(*this); }
399 
400  struct Comparer_t {
401  bool operator()(Key_t const& a, Key_t const& b) const { return a < b; }
402  bool operator()(Key_t const& a, Connections_t const& b) const { return a < b.key(); }
403  bool operator()(Connections_t const& a, Key_t const& b) const { return a.key() < b; }
404  bool operator()(Connections_t const& a, Connections_t const& b) const
405  {
406  return a.key() < b.key();
407  }
408  }; // Comparer_t
409 
410  static Comparer_t comparer() { return {}; }
411 
412  /// Compares the connection lists by key only.
413  bool operator<(Connections_t const& b) const { return comparer()(*this, b); }
414 
415  }; // struct Connections<>
416 
417  struct KeysFromTag {};
418  struct KeysFromIndexTag {};
419  struct KeysFromOtherListTag {};
420  constexpr KeysFromTag KeysFrom;
421  constexpr KeysFromIndexTag KeysFromIndex;
422  constexpr KeysFromOtherListTag KeysFromOtherList;
423 
424  /// A connections list.
425  template <typename Key, typename Connected>
426  class ConnectionList : private std::vector<Connections<Key, Connected>> {
427  using Connections_t = Connections<Key, Connected>;
428  using Data_t = std::vector<Connections_t>;
429 
430  Data_t& data() { return *this; }
431  Data_t const& data() const { return *this; }
432 
433  public:
434  /// Type of an item connected to the key.
435  using ConnectedItem_t = typename Connections_t::ConnectedItem_t;
436  /// Type of list of connected items.
437  using Connected_t = typename Connections_t::Connected_t;
438 
439  // import types from the base class
440  using value_type = typename Data_t::value_type;
441 
442  /// Initializes an empty connection list.
443  ConnectionList() = default;
444 
445  /// Initializes with no connection for each of the keys from b to e.
446  template <typename BeginIter, typename EndIter>
447  ConnectionList(KeysFromTag, BeginIter b, EndIter e)
448  {
449  data().reserve(std::distance(b, e)); // hope this is fast
450  std::transform(b, e, std::back_inserter(data()), [](auto const& key) {
451  return Connections_t(key, {});
452  });
453  }
454 
455  /// Initializes with no connection for each of the keys from a list.
456  template <typename Other>
457  ConnectionList(KeysFromOtherListTag, ConnectionList<Key, Other> const& source)
458  {
459  data().reserve(source.size()); // hope this is fast
460  using std::cbegin;
461  using std::cend;
462  std::transform(cbegin(source),
463  cend(source),
464  std::back_inserter(data()),
465  [](auto const& conn) { return Connections_t(conn.key(), {}); });
466  }
467 
468  /// Initializes with no connection for each of the generated keys.
469  template <typename KeyMaker>
470  ConnectionList(KeysFromIndexTag, std::size_t n, KeyMaker keyMaker)
471  {
472  data().reserve(n);
473  for (std::size_t i = 0; i < n; ++i)
474  data().emplace_back(keyMaker(i));
475  }
476 
477  // import methods from the base class (not the constructors)
478  using Data_t::size;
479  using Data_t::operator[];
480  using Data_t::begin;
481  using Data_t::cbegin;
482  using Data_t::cend;
483  using Data_t::clear;
484  using Data_t::end;
485 
486  /// @{
487  /// @name Special methods
488 
489  void addConnectionAt(std::size_t index, ConnectedItem_t const& item)
490  {
491  data()[index].connectedTo().push_back(item);
492  }
493  void addConnectionAt(std::size_t index, ConnectedItem_t&& item)
494  {
495  data()[index].connectedTo().push_back(std::move(item));
496  }
497 
498  /// Returns all connected objects on a vector, one element per key.
499  /// As a R-value reference method, the current content is lost
500  std::vector<Connected_t> values() &&
501  {
502  std::vector<Connected_t> result;
503  result.reserve(data().size());
504  std::transform(data().begin(), data().end(), std::back_inserter(result), [](auto& conn) {
505  return std::move(conn.connectedTo());
506  });
507  return result;
508  } // values()
509 
510  /// @}
511 
512  }; // struct ConnectionList<>
513 
514  /// Class handling a connection list.
515  template <typename Source, typename Target>
516  class PtrConnectionManager {
517 
518  public:
519  using Manager_t = PtrConnectionManager<Source, Target>;
520  using Source_t = Source;
521  using Target_t = Target;
522  using SourcePtr_t = art::Ptr<Source_t>;
523  using TargetPtr_t = art::Ptr<Target_t>;
524  using ConnectionList_t = ConnectionList<SourcePtr_t, TargetPtr_t>;
525 
526  /// Constructor: steals the connections from the argument.
527  PtrConnectionManager(ConnectionList_t&& data) : data(std::move(data)) {}
528 
529  /// Returns all connected objects as a single connection of pointers.
530  std::vector<TargetPtr_t> allConnected() const
531  {
532 
533  // this might as well be implemented with ranges
534 
535  std::vector<TargetPtr_t> result;
536 
537  using std::cbegin;
538  using std::cend;
539 
540  for (std::size_t i = 0; i < data.size(); ++i) {
541  auto const& connected = data[i].connectedTo();
542  result.insert(result.end(), cbegin(connected), cend(connected));
543  } // for
544  return result;
545 
546  } // allConnected()
547 
548  /// Returns a new connection list connecting each of our sources to
549  /// FurtherPtr_t objects, using shared TargetPtr_t to map connections.
550  template <typename FurtherPtr_t>
551  ConnectionList<SourcePtr_t, FurtherPtr_t> join(
552  ConnectionList<TargetPtr_t, FurtherPtr_t>&& other) const
553  {
554  // internally, we'll call the current data A -> B, other data B -> C,
555  // and we are after A -> C
556  using APtr_t = SourcePtr_t;
557  // using BPtr_t = TargetPtr_t;
558  using CPtr_t = FurtherPtr_t;
559 
560  auto const& AB = data;
561  auto&& BC = other;
562  ConnectionList<APtr_t, CPtr_t> AC(KeysFromOtherList, data);
563 
564  // the match object returns the iterator to the list connected to the
565  // specified key; it does not own the data structure;
566  auto const match = makeIndex(BC, [](auto const& elem) { return elem.key(); });
567 
568  for (std::size_t iA = 0; iA < AB.size(); iA++) {
569  auto const& connAB = AB[iA]; // connections for this A
570  auto const& BsForA = connAB.connectedTo();
571 
572  auto& CsForA = AC[iA].connectedTo(); // connected list to fill (empty)
573 
574  // for each B connected to this A:
575  for (auto const& key : BsForA) {
576 
577  auto iConnBC = match(key);
578  if (!iConnBC) continue; // no C's associated to the matching B
579 
580  // matched! move or copy the while list of Cs
581  auto& CsForB = iConnBC->connectedTo(); // (this list)
582  if (CsForA.empty())
583  CsForA = std::move(CsForB); // move
584  else
585  CsForA.insert(CsForA.end(), cbegin(CsForB), cend(CsForB));
586 
587  } // for connected Bs
588  } // for iA
589 
590  return AC;
591  } // join()
592 
593  private:
594  ConnectionList_t data; ///< All connection information.
595 
596  }; // PtrConnectionManager<>
597 
598  /// Helper function to created the right type of manager from a temporary.
599  template <typename A, typename B>
600  auto makeConnectionManager(ConnectionList<A, B>&& data)
601  {
602  return PtrConnectionManager<typename A::value_type, typename B::value_type>(
603  std::move(data));
604  }
605 
606  } // namespace AssociationFinderBase
607 
608  template <typename Target>
609  struct AssociationFinder {
610 
611  using Target_t = Target;
612  using TargetPtr_t = art::Ptr<Target_t>;
613 
614  /// Type of result of finders: one list of associated targets per source.
615  template <typename Source>
616  using Result_t = AssociationFinderBase::ConnectionList<art::Ptr<Source>, TargetPtr_t>;
617 
618  template <typename Source>
619  using Assns_t = art::Assns<Source, Target>;
620 
621  template <typename Assns>
622  using AssnKey_t = typename Assns::left_t;
623 
624  template <typename Assns>
625  using AssnKeyPtr_t = art::Ptr<AssnKey_t<Assns>>;
626 
627  template <typename Assns>
628  using ResultFromAssns_t = Result_t<AssnKey_t<Assns>>;
629 
630  template <typename Handle>
631  using ValueFromHandle_t = typename Handle::element_type::value_type;
632 
633  template <typename Handle>
634  using AssnsFromHandle_t = Assns_t<ValueFromHandle_t<Handle>>;
635 
636  template <typename Handle>
637  using ResultFromHandle_t = Result_t<art::Ptr<ValueFromHandle_t<Handle>>>;
638 
639  /// Helper finding a associations from a single complete data product.
640  template <typename Assns>
641  static ResultFromAssns_t<Assns> findFromDataProduct(art::ProductID const& sourceID,
642  std::size_t nSources,
643  Assns const& assns)
644  {
645  using SourcePtr_t = AssnKeyPtr_t<Assns>;
646 
647  using namespace AssociationFinderBase;
648 
649  // as many lists in result as sources, keys created from source ID
650  ResultFromAssns_t<Assns> result(KeysFromIndex, nSources, [id = sourceID](std::size_t i) {
651  return SourcePtr_t(id, i, nullptr);
652  });
653 
654  // get the associations, following the content of the assns data product
655  for (decltype(auto) assn : assns) {
656  auto const& sourcePtr = assn.first;
657 
658  // does this association contain a pointer with an ID different than
659  // the one we are looking for? (it may mean our assumption that the
660  // same module produced source and its association is wrong)
661  if (sourcePtr.id() != sourceID) continue;
662 
663  // we follow the assumption that the data product is complete with
664  // nSources elements, therefore no pointer can exist with a larger
665  // key:
666  assert(sourcePtr.key() < nSources);
667 
668  // push target pointer into the result of the matched source
669  result.addConnectionAt(sourcePtr.key(), assn.second);
670 
671  } // for
672 
673  return result;
674  } // findFromDataProduct()
675 
676  /// Helper finding a associations from a single list.
677  template <typename Source, typename IndexBegin, typename IndexEnd, typename Event>
678  static Result_t<Source> findSingle(art::ProductID const& sourceID,
679  IndexBegin sbegin,
680  IndexEnd send,
681  Assns_t<Source> const& assns)
682  {
683  // type of the source object (hidden in PtrColl)
684  using Source_t = Source;
685  using SourcePtr_t = art::Ptr<Source_t>;
686  using namespace AssociationFinderBase;
687 
688  std::size_t const nSources = std::distance(sbegin, send);
689  Result_t<Source_t> result(nSources);
690 
691  // These are the source "pointers" we still have to find; they are all
692  // on the same product ID. We keep track of the original position too
693  // (unordered map may or may not be better...);
694  // given that these are all from the same product ID, the product ID is
695  // not saved. SourceVector_t provides log(N) lookup.
696  SourceVector_t<std::size_t> sourceInfos(sbegin, send);
697 
698  // get the association, following the content of the assns data product
699  for (decltype(auto) assn : assns) {
700  SourcePtr_t const& sourcePtr = assn.first;
701 
702  // does this association contain a pointer with an ID different than
703  // the one we are looking for? (it may mean our assumption that the
704  // same module produced source and its association is wrong)
705  if (sourcePtr.id() != sourceID) continue;
706 
707  // is this pointer interesting?
708  auto iSrcInfo = sourceInfos.find(sourcePtr.key());
709  if (!iSrcInfo) continue; // it's not it
710 
711  // match! push target pointer into the result of the matched source
712  result[iSrcInfo->position()].connectedTo().push_back(assn.second);
713 
714  } // for
715 
716  return result;
717  } // findSingle()
718 
719  /// Helper finding a single degree of associations from the tag of each
720  /// source art pointer.
721  template <typename PtrCollBegin, typename PtrCollEnd, typename Event>
722  static auto findWithRange(PtrCollBegin sbegin,
723  PtrCollEnd send,
724  Event const& event,
725  art::InputTag const& tag)
726  {
727  /*
728  * The strategy of this implementation is:
729  *
730  * 1. collect all source art pointers, sorted by key for faster lookup
731  * 2. parse all the associated pairs
732  * 1. if the source pointer of a pair is in the list of
733  * interesting source pointers, push the target pointer of the
734  * pair into the results for this source
735  *
736  */
737 
738  using SourcePtr_t = std::decay_t<decltype(*sbegin)>;
739  static_assert(is_art_ptr<SourcePtr_t>(),
740  "Collection for AssociationFinder (FindManyInChainP) is not art pointers!");
741 
742  // type of the source object (hidden in PtrColl)
743  using Source_t = typename SourcePtr_t::value_type;
744  using namespace AssociationFinderBase;
745 
746  Result_t<Source_t> result(KeysFrom, sbegin, send);
747  // std::size_t const nSources = result.size();
748 
749  // use this index for fast lookup of the sources
750  auto match = makeSimpleIndex(sbegin, send);
751 
752  // fetch the association data product
753  auto const& assns = *(event.template getValidHandle<Assns_t<Source_t>>(tag));
754 
755  // get the association, following the content of the assns data product
756  for (decltype(auto) assn : assns) {
757  SourcePtr_t const& sourcePtr = assn.first;
758 
759  // is this pointer interesting?
760  auto iPos = match(sourcePtr);
761  if (!iPos) continue; // it's not it
762 
763  // match! push target pointer into the result of the matched source
764  result[*iPos].connectedTo().push_back(assn.second);
765 
766  } // for
767 
768  return result;
769  } // findWithRange()
770 
771  /// Helper finding a single degree of associations from the tag of each
772  /// source art pointer.
773  template <typename PtrCollBegin, typename PtrCollEnd, typename Event>
774  static auto findWithRange(PtrCollBegin sbegin, PtrCollEnd send, Event const& event)
775  {
776  /*
777  * The strategy of this implementation is:
778  *
779  * 1. collect all the source art pointers, grouped by product ID
780  * (and sorted by key for faster lookup)
781  * 2. for each interesting product ID:
782  * 1. fetch the association collection; this is assumed to have been
783  * created with the same input tag as the source product
784  * 2. parse all the associated pairs
785  * 1. if the source pointer of a pair is in the list of
786  * interesting source pointers, push the target pointer of the
787  * pair into the results for this source
788  *
789  * The maximum complexity of this algorithm is N log(M), where M is no
790  * larger than the maximum number of source pointers with a single
791  * product ID and N is the number of associations in each association
792  * data product.
793  *
794  */
795 
796  using SourcePtr_t = std::decay_t<decltype(*sbegin)>;
797  static_assert(is_art_ptr<SourcePtr_t>(),
798  "Collection for AssociationFinder (FindManyInChainP) is not art pointers!");
799 
800  // type of the source object (hidden in PtrColl)
801  using Source_t = typename SourcePtr_t::value_type;
802  using namespace AssociationFinderBase;
803 
804  Result_t<Source_t> result(KeysFrom, sbegin, send);
805 
806  // These are the source pointers we still have to find,
807  // organised by product ID; we keep track of the original position too
808  // (unordered map may or may not be better...);
809  // given that these are all from the same product ID, the product ID is
810  // not saved.
811  // Also, for fast lookup the lists are sorted by pointer key.
812  std::map<art::ProductID, SourceVector_t<std::size_t>> sourcesLeft;
813  std::size_t iSource = 0;
814  for (auto it = sbegin; it != send; ++it, ++iSource)
815  sourcesLeft[it->id()].emplace_back(it->key(), iSource);
816  for (auto& sourcesWithinID : sourcesLeft)
817  sourcesWithinID.second.sort();
818 
819  // look for all sources in each product ID
820  for (auto const& sourcesWithinID : sourcesLeft) {
821 
822  art::ProductID sourceID = sourcesWithinID.first;
823  auto const& sourceInfos = sourcesWithinID.second;
824 
825  // need to get the association between source and target,
826  // as produced by the same producer that produced the source itself
827  art::InputTag tag = tagFromProductID<std::vector<Source_t>>(sourceID, event);
828 
829  // fetch the association data product
830  auto const& assns = *(event.template getValidHandle<Assns_t<Source_t>>(tag));
831 
832  // get the association, following the content of the assns data product
833  for (decltype(auto) assn : assns) {
834  SourcePtr_t const& sourcePtr = assn.first;
835 
836  // does this association contain a pointer with an ID different than
837  // the one we are looking for? (it may mean our assumption that the
838  // same module produced source and its association is wrong)
839  if (sourcePtr.id() != sourceID) continue;
840 
841  // is this pointer interesting?
842  auto iSrcInfo = sourceInfos.find(sourcePtr.key());
843  if (!iSrcInfo) continue; // it's not it
844 
845  // match! push target pointer into the result of the matched source
846  result[iSrcInfo->position()].connectedTo().push_back(assn.second);
847 
848  } // for
849 
850  } // for sourcesWithinID
851 
852  return result;
853  } // findWithRange()
854 
855  }; // class AssociationFinder
856 
857  /// Helper finding a single degree of associations from a specified tag.
858  template <typename Target,
859  typename Handle,
860  typename Event,
861  typename = std::enable_if_t<is_handle_v<Handle>>>
862  auto findAssociations(Handle&& handle, Event const& event, art::InputTag const& assnsTag)
863  {
864  using Finder = AssociationFinder<Target>;
865  using Assns_t = typename Finder::template AssnsFromHandle_t<std::decay_t<Handle>>;
866 
867  auto const& assns = *(event.template getValidHandle<Assns_t>(assnsTag));
868 
869  // FIXME: this implementation is NOT canvas-compatible (Handle::id())
870  return Finder::findFromDataProduct(handle.id(), handle->size(), assns);
871  } // findAssociations(Handle, tag)
872 
873  /// Helper finding a single degree of associations from any tag.
874  template <typename Target,
875  typename Handle,
876  typename Event,
877  typename = std::enable_if_t<is_handle_v<Handle>>>
878  auto findAssociations(Handle handle, Event const& event, lar::SameAsDataTag)
879  {
880  /*
881  * By definition, here all the interesting sources share the same product
882  * ID, which simplifies the implementation.
883  */
884 
885  // need to get the association between source and target,
886  // as produced by the same producer that produced the source itself
887  // FIXME: the implementation of tagFromHandle is NOT canvas-compatible
888  art::InputTag tag = tagFromHandle(handle);
889 
890  return findAssociations<Target>(std::forward<Handle>(handle), event, tag);
891  } // findAssociations(Handle, same tag)
892 
893  template <typename Target,
894  typename PtrColl,
895  typename Event,
896  typename = std::enable_if_t<!is_handle_v<PtrColl>>>
897  auto findAssociations(PtrColl const& coll, Event const& event, lar::SameAsDataTag)
898  {
899  using std::cbegin;
900  using std::cend;
901  return AssociationFinder<Target>::findWithRange(cbegin(coll), cend(coll), event);
902  }
903 
904  /// Helper finding a single degree of associations from a specified tag.
905  template <typename Target,
906  typename PtrColl,
907  typename Event,
908  typename = std::enable_if_t<!is_handle_v<PtrColl>>>
909  auto findAssociations(PtrColl const& coll, Event const& event, art::InputTag const& tag)
910  {
911  using std::cbegin;
912  using std::cend;
913  return AssociationFinder<Target>::findWithRange(cbegin(coll), cend(coll), event, tag);
914  } // findAssociations(Handle, tag)
915 
916  //--------------------------------------------------------------------------
917 
918  /**
919  * @tparam Target type to be associated to the source data
920  * @tparam Tier tier: 0 -> get target, 1 -> get leftmost intermediate, ...
921  * @tparam Intermediate intermediate types, leftmost is closest to `Target`
922  */
923  template <typename Target, unsigned int Tier, typename... Intermediate>
924  struct FindManyInChainPimpl {
925 
926  /// Total number of tiers (original source + all intermediates).
927  static constexpr unsigned int Tiers = sizeof...(Intermediate) + 1;
928 
929  static_assert(Tier < Tiers, "Invalid tier: there is no type in position #N");
930 
931  /// Type used for the association to source.
932  using Intermediate_t = get_type_t<(Tier - 1), Intermediate...>;
933 
934  template <typename Source, typename Event, typename InputTags>
935  static auto find(Source&& source, Event const& event, InputTags const& tags)
936  {
937  static_assert(std::tuple_size<InputTags>() == Tiers,
938  "Wrong number of input tags specified");
939 
940  // number of the intermediate being addressed
941  constexpr auto nTag = Tiers - 1 - Tier;
942 
943  // find the associations between source and the next tier:
944  // Source <==> Intermediate;
945  // result is a ConnectionList object, that we store in a "manager"
946  auto iq = makeConnectionManager(
947  findAssociations<Intermediate_t>(source, event, std::get<nTag>(tags)));
948 
949  // collapse the result for input into the next tier;
950  // this will result in a long sequence of art pointers
951  auto const intermediateData = iq.allConnected();
952 
953  // process the next tier: Intermediate <==> Target;
954  // this is also a ConnectionList
955  auto oq = FindManyInChainPimpl<Target, (Tier - 1U), Intermediate...>::find(
956  intermediateData, event, tags);
957 
958  // combine the result into jet another connection list:
959  // Source <==> Intermediate (+) Intermediate <==> Target
960  return iq.join(std::move(oq)); // AssociationFinderBase::combine(iq, oq);
961 
962  } // find()
963 
964  }; // FindManyInChainPimpl<>
965 
966  // Specialization for tier 0
967  // (target association to first intermediate in list)
968  template <typename Target, typename... Intermediate>
969  struct FindManyInChainPimpl<Target, 0U, Intermediate...> {
970 
971  /// Total number of tiers (original source + all intermediates).
972  static constexpr unsigned int Tiers = sizeof...(Intermediate) + 1;
973 
974  template <typename Source, typename Event, typename InputTags>
975  static auto find(Source&& source, Event const& event, InputTags const& tags)
976  {
977  static_assert(std::tuple_size<InputTags>() == Tiers,
978  "Wrong number of input tags specified");
979  // find the associations between the last intermediate and the target
980  // (using the last tag)
981  return findAssociations<Target>(source, event, std::get<Tiers - 1>(tags));
982  } // find()
983 
984  }; // FindManyInChainPimpl
985 
986  //--------------------------------------------------------------------------
987 
988  } // namespace details
989 
990 } // namespace lar
991 
992 //------------------------------------------------------------------------------
993 //
994 // Implementation note:
995 // - FindManyInChainP is the front-end utility with minimal template arguments;
996 // it sets up and runs FindManyInChainPimpl...
997 // - FindManyInChainPimpl is the implementation of recursion where associations
998 // are extracted for the results of the previous level ("tier") of
999 // associated objects; it juggles with tags and parameters, and relies on
1000 // findAssociations()...
1001 // - findAssociations() implements the extraction of a single association tier,
1002 // from a (flattened) collection of input objects
1003 //
1004 //------------------------------------------------------------------------------
1005 template <typename Target, typename... Intermediate>
1006 template <typename Source, typename Event, typename... InputTags>
1007 auto lar::FindManyInChainP<Target, Intermediate...>::find(Source&& source,
1008  Event const& event,
1009  InputTags... tags)
1010  -> std::vector<TargetPtrCollection_t>
1011 {
1012  constexpr auto Tiers = sizeof...(Intermediate) + 1U;
1013 
1014  // create a parameter pack with one tag per association
1015  auto const allTags = details::AssociationFinderBase::makeTagsTuple<Tiers>(
1016  SameAsData, std::forward<InputTags>(tags)...);
1017 
1018  return details::FindManyInChainPimpl<Target, (Tiers - 1), Intermediate...>::find(
1019  source, event, allTags)
1020  .values();
1021 
1022 } // lar::FindManyInChainP<Target, Intermediate...>::FindManyInChainP()
1023 
1024 //------------------------------------------------------------------------------
1025 template <typename Target, typename... Intermediate>
1026 std::size_t lar::FindManyInChainP<Target, Intermediate...>::size() const noexcept
1027 {
1028  return results.size();
1029 }
1030 
1031 //------------------------------------------------------------------------------
1032 template <typename Target, typename... Intermediate>
1033 typename lar::FindManyInChainP<Target, Intermediate...>::TargetPtrCollection_t const&
1034 lar::FindManyInChainP<Target, Intermediate...>::at(std::size_t i) const
1035 {
1036  return results.at(i);
1037 }
1038 
1039 //------------------------------------------------------------------------------
1040 
1041 #endif // LARDATA_UTILITIES_FINDMANYINCHAINP_TCC
1042 
1043 // Local variables:
1044 // mode: c++
1045 // End: