@@ -125,20 +125,24 @@ class BaseSelection
125125 addSelection (observableIndex);
126126 }
127127
128- // / \brief Add a static-value based selection for a specific observable.
128+ // / \brief Add a range-based selection (either inside or outside the configured ranges) for a
129+ // / specific observable.
129130 // / \param observableIndex Index of the observable.
130131 // / \param selectionName Name of the selection.
131- // / \param selectionValues Vector of threshold values.
132- // / \param limitType Type of limit (from limits::LimitType).
132+ // / \param selectionRanges Vector of "lower;upper" range strings.
133133 // / \param skipMostPermissiveBit Whether to skip the loosest threshold when assembling the bitmask.
134134 // / \param isMinimalCut Whether this cut is mandatory (must be passed for the candidate to be accepted).
135135 // / \param isOptionalCut Whether this cut is optional (candidate is accepted if any optional cut passes).
136+ // / \param limitType Either limits::kInsideRange (value must fall inside) or limits::kOutsideRange
137+ // / (value must fall outside, e.g. rejecting a competing mass hypothesis). A
138+ // / fully-open range entry (";") is exempt for either type -- see SelectionContainer.
136139 void addSelection (int observableIndex,
137140 std::string const & selectionName,
138141 std::vector<std::string> const & selectionRanges,
139142 bool skipMostPermissiveBit,
140143 bool isMinimalCut,
141- bool isOptionalCut)
144+ bool isOptionalCut,
145+ limits::LimitType limitType = limits::kInsideRange )
142146 {
143147 // check index
144148 if (static_cast <std::size_t >(observableIndex) >= NumObservables) {
@@ -149,7 +153,8 @@ class BaseSelection
149153 selectionRanges,
150154 mPassThrough ? false : skipMostPermissiveBit,
151155 mPassThrough ? false : isMinimalCut,
152- mPassThrough ? false : isOptionalCut);
156+ mPassThrough ? false : isOptionalCut,
157+ limitType);
153158
154159 addSelection (observableIndex);
155160 }
@@ -248,6 +253,42 @@ class BaseSelection
248253 }
249254 }
250255
256+ // / \brief Evaluate the same observable for two independently-computed values of the same
257+ // / candidate and store only the logical AND of the two resulting per-threshold bitmasks
258+ // / \param observableIndex Index of the observable.
259+ // / \param value1 First value to evaluate.
260+ // / \param value2 Second value to evaluate.
261+ void evaluateObservable (int observableIndex, T value1, T value2)
262+ {
263+ // if there are no selections configured, bail out
264+ if (mSelectionContainers .at (observableIndex).isEmpty ()) {
265+ return ;
266+ }
267+ // if a previous observable already failed a minimal selection,
268+ // there is no point in evaluating further observables
269+ if (!mPassesMinimalSelections ) {
270+ return ;
271+ }
272+ auto & container = mSelectionContainers .at (observableIndex);
273+ container.evaluate (value1);
274+ auto const bitmask1 = container.getRawBitmask ();
275+ container.evaluate (value2);
276+ auto const bitmask2 = container.getRawBitmask ();
277+ container.setBitmask ((bitmask1 & bitmask2).to_ullong ());
278+ // if any minimal selection is not fulfilled, the candidate is rejected
279+ if (mHasMinimalSelection ) {
280+ if (!container.passesAsMinimalCut ()) {
281+ mPassesMinimalSelections = false ;
282+ }
283+ }
284+ // if any optional selection is fulfilled, the candidate is accepted
285+ if (mHasOptionalSelection ) {
286+ if (container.passesAsOptionalCut ()) {
287+ mPassesOptionalSelections = true ;
288+ }
289+ }
290+ }
291+
251292 // / \brief Evaluate a single observable against its configured selections.
252293 // / \param observableIndex Index of the observable.
253294 // / \param values Vector of values of the observable.
0 commit comments