From aa32f594872f86d218e3bc144a14f1918f1da8df Mon Sep 17 00:00:00 2001
From: Marino Missiroli <marino.missiroli@cern.ch>
Date: Wed, 7 Sep 2022 10:24:25 +0200
Subject: [PATCH] introduce MASKING operator in triggerExpression::Parser

---
 .../interface/TriggerExpressionConstant.h     |  8 +-
 .../HLTcore/interface/TriggerExpressionData.h |  6 +-
 .../interface/TriggerExpressionEvaluator.h    | 27 +++++--
 .../interface/TriggerExpressionL1uGTReader.h  | 20 +++--
 .../interface/TriggerExpressionOperators.h    | 72 +++++++++++++-----
 .../interface/TriggerExpressionParser.h       | 47 ++++++++----
 .../interface/TriggerExpressionPathReader.h   | 22 +++---
 .../interface/TriggerExpressionPrescaler.h    | 13 ++--
 .../src/TriggerExpressionL1uGTReader.cc       | 75 ++++++++++++++-----
 .../src/TriggerExpressionPathReader.cc        | 59 ++++++++++++---
 .../HLTcore/src/TriggerExpressionPrescaler.cc |  3 +-
 .../test_catch2_TriggerExpressionParser.cc    | 19 +++++
 .../triggerResultsFilter_by_PathStatus.py     | 14 ++++
 .../triggerResultsFilter_by_PathStatus.sh     | 36 ++++-----
 .../triggerResultsFilter_by_TriggerResults.py | 14 ++++
 .../triggerResultsFilter_by_TriggerResults.sh | 32 ++++----
 16 files changed, 340 insertions(+), 127 deletions(-)

diff --git a/HLTrigger/HLTcore/interface/TriggerExpressionConstant.h b/HLTrigger/HLTcore/interface/TriggerExpressionConstant.h
index 7636b96a1c17e..64d710aeffb3f 100644
--- a/HLTrigger/HLTcore/interface/TriggerExpressionConstant.h
+++ b/HLTrigger/HLTcore/interface/TriggerExpressionConstant.h
@@ -1,5 +1,5 @@
-#ifndef HLTrigger_HLTfilters_TriggerExpressionConstant_h
-#define HLTrigger_HLTfilters_TriggerExpressionConstant_h
+#ifndef HLTrigger_HLTcore_TriggerExpressionConstant_h
+#define HLTrigger_HLTcore_TriggerExpressionConstant_h
 
 #include "HLTrigger/HLTcore/interface/TriggerExpressionEvaluator.h"
 
@@ -13,7 +13,7 @@ namespace triggerExpression {
 
     bool operator()(const Data& data) const override { return m_value; }
 
-    void dump(std::ostream& out) const override { out << (m_value ? "TRUE" : "FALSE"); }
+    void dump(std::ostream& out, bool const ignoreMasks = false) const override { out << (m_value ? "TRUE" : "FALSE"); }
 
   private:
     bool m_value;
@@ -21,4 +21,4 @@ namespace triggerExpression {
 
 }  // namespace triggerExpression
 
-#endif  // HLTrigger_HLTfilters_TriggerExpressionConstant_h
+#endif  // HLTrigger_HLTcore_TriggerExpressionConstant_h
diff --git a/HLTrigger/HLTcore/interface/TriggerExpressionData.h b/HLTrigger/HLTcore/interface/TriggerExpressionData.h
index 918d4dc99a8d0..b4181e5bbcdd7 100644
--- a/HLTrigger/HLTcore/interface/TriggerExpressionData.h
+++ b/HLTrigger/HLTcore/interface/TriggerExpressionData.h
@@ -1,5 +1,5 @@
-#ifndef HLTrigger_HLTfilters_TriggerExpressionData_h
-#define HLTrigger_HLTfilters_TriggerExpressionData_h
+#ifndef HLTrigger_HLTcore_TriggerExpressionData_h
+#define HLTrigger_HLTcore_TriggerExpressionData_h
 
 #include "FWCore/Framework/interface/ConsumesCollector.h"
 #include "FWCore/Utilities/interface/InputTag.h"
@@ -220,4 +220,4 @@ namespace triggerExpression {
 
 }  // namespace triggerExpression
 
-#endif  // HLTrigger_HLTfilters_TriggerExpressionData_h
+#endif  // HLTrigger_HLTcore_TriggerExpressionData_h
diff --git a/HLTrigger/HLTcore/interface/TriggerExpressionEvaluator.h b/HLTrigger/HLTcore/interface/TriggerExpressionEvaluator.h
index 81e8e54d8e728..09ff7e3d2f2ad 100644
--- a/HLTrigger/HLTcore/interface/TriggerExpressionEvaluator.h
+++ b/HLTrigger/HLTcore/interface/TriggerExpressionEvaluator.h
@@ -1,9 +1,10 @@
-#ifndef HLTrigger_HLTfilters_TriggerExpressionEvaluator_h
-#define HLTrigger_HLTfilters_TriggerExpressionEvaluator_h
+#ifndef HLTrigger_HLTcore_TriggerExpressionEvaluator_h
+#define HLTrigger_HLTcore_TriggerExpressionEvaluator_h
 
 #include <iostream>
 #include <string>
 #include <vector>
+#include <utility>
 
 namespace triggerExpression {
 
@@ -13,6 +14,9 @@ namespace triggerExpression {
   public:
     Evaluator() = default;
 
+    // virtual destructor
+    virtual ~Evaluator() = default;
+
     // check if the data satisfies the logical expression
     virtual bool operator()(const Data& data) const = 0;
 
@@ -22,11 +26,22 @@ namespace triggerExpression {
     // list CMSSW path patterns associated to the logical expression
     virtual std::vector<std::string> patterns() const { return {}; }
 
+    // list of triggers associated to the Evaluator (filled only for certain derived classes)
+    virtual std::vector<std::pair<std::string, unsigned int>> triggers() const { return {}; }
+
     // dump the logical expression to the output stream
-    virtual void dump(std::ostream& out) const = 0;
+    virtual void dump(std::ostream& out, bool const ignoreMasks = false) const = 0;
 
-    // virtual destructor
-    virtual ~Evaluator() = default;
+    // apply masks based on another Evaluator
+    virtual void mask(Evaluator const&) {}
+
+    // methods to control m_masksEnabled boolean
+    virtual bool masksEnabled() const { return m_masksEnabled; }
+    virtual void enableMasks() { m_masksEnabled = true; }
+    virtual void disableMasks() { m_masksEnabled = false; }
+
+  private:
+    bool m_masksEnabled = false;
   };
 
   inline std::ostream& operator<<(std::ostream& out, const Evaluator& eval) {
@@ -36,4 +51,4 @@ namespace triggerExpression {
 
 }  // namespace triggerExpression
 
-#endif  // HLTrigger_HLTfilters_TriggerExpressionEvaluator_h
+#endif  // HLTrigger_HLTcore_TriggerExpressionEvaluator_h
diff --git a/HLTrigger/HLTcore/interface/TriggerExpressionL1uGTReader.h b/HLTrigger/HLTcore/interface/TriggerExpressionL1uGTReader.h
index dcfadcf7199b1..2f98e7424c5c2 100644
--- a/HLTrigger/HLTcore/interface/TriggerExpressionL1uGTReader.h
+++ b/HLTrigger/HLTcore/interface/TriggerExpressionL1uGTReader.h
@@ -1,8 +1,5 @@
-#ifndef HLTrigger_HLTfilters_TriggerExpressionL1uGTReader_h
-#define HLTrigger_HLTfilters_TriggerExpressionL1uGTReader_h
-
-#include <vector>
-#include <string>
+#ifndef HLTrigger_HLTcore_TriggerExpressionL1uGTReader_h
+#define HLTrigger_HLTcore_TriggerExpressionL1uGTReader_h
 
 #include "HLTrigger/HLTcore/interface/TriggerExpressionEvaluator.h"
 
@@ -10,20 +7,27 @@ namespace triggerExpression {
 
   class L1uGTReader : public Evaluator {
   public:
-    L1uGTReader(const std::string& pattern) : m_pattern{pattern}, m_triggers{}, m_initialised{false} {}
+    L1uGTReader(const std::string& pattern)
+        : m_pattern{pattern}, m_triggers{}, m_triggersAfterMasking{}, m_initialised{false} {}
 
     bool operator()(const Data& data) const override;
 
     void init(const Data& data) override;
 
-    void dump(std::ostream& out) const override;
+    void dump(std::ostream& out, bool const ignoreMasks = false) const override;
+
+    void mask(Evaluator const& eval) override;
+
+    std::vector<std::pair<std::string, unsigned int>> triggers() const override { return m_triggers; }
+    std::vector<std::pair<std::string, unsigned int>> triggersAfterMasking() const { return m_triggersAfterMasking; }
 
   private:
     std::string m_pattern;
     std::vector<std::pair<std::string, unsigned int>> m_triggers;
+    std::vector<std::pair<std::string, unsigned int>> m_triggersAfterMasking;
     bool m_initialised;
   };
 
 }  // namespace triggerExpression
 
-#endif  // HLTrigger_HLTfilters_TriggerExpressionL1uGTReader_h
+#endif  // HLTrigger_HLTcore_TriggerExpressionL1uGTReader_h
diff --git a/HLTrigger/HLTcore/interface/TriggerExpressionOperators.h b/HLTrigger/HLTcore/interface/TriggerExpressionOperators.h
index de3a18abd3e33..7971625432b2c 100644
--- a/HLTrigger/HLTcore/interface/TriggerExpressionOperators.h
+++ b/HLTrigger/HLTcore/interface/TriggerExpressionOperators.h
@@ -1,7 +1,8 @@
-#ifndef HLTrigger_HLTfilters_TriggerExpressionOperators_h
-#define HLTrigger_HLTfilters_TriggerExpressionOperators_h
+#ifndef HLTrigger_HLTcore_TriggerExpressionOperators_h
+#define HLTrigger_HLTcore_TriggerExpressionOperators_h
 
 #include <memory>
+
 #include "HLTrigger/HLTcore/interface/TriggerExpressionEvaluator.h"
 
 namespace triggerExpression {
@@ -14,6 +15,9 @@ namespace triggerExpression {
     // initialize the depending modules
     void init(const Data& data) override { m_arg->init(data); }
 
+    // apply mask(s) to the Evaluator
+    void mask(Evaluator const& arg) override { m_arg->mask(arg); }
+
     // return the patterns from the depending modules
     std::vector<std::string> patterns() const override { return m_arg->patterns(); }
 
@@ -32,6 +36,12 @@ namespace triggerExpression {
       m_arg2->init(data);
     }
 
+    // apply mask(s) to the Evaluators
+    void mask(Evaluator const& arg) override {
+      m_arg1->mask(arg);
+      m_arg2->mask(arg);
+    }
+
     // return the patterns from the depending modules
     std::vector<std::string> patterns() const override {
       std::vector<std::string> patterns = m_arg1->patterns();
@@ -54,10 +64,10 @@ namespace triggerExpression {
 
     bool operator()(const Data& data) const override { return not(*m_arg)(data); }
 
-    void dump(std::ostream& out) const override {
+    void dump(std::ostream& out, bool const ignoreMasks = false) const override {
       out << '(';
       out << "NOT ";
-      m_arg->dump(out);
+      m_arg->dump(out, ignoreMasks);
       out << ')';
     }
   };
@@ -67,17 +77,17 @@ namespace triggerExpression {
     OperatorAnd(Evaluator* arg1, Evaluator* arg2) : BinaryOperator(arg1, arg2) {}
 
     bool operator()(const Data& data) const override {
-      // force the execution af both arguments, otherwise precalers won't work properly
+      // force the execution of both arguments, otherwise prescalers won't work properly
       bool r1 = (*m_arg1)(data);
       bool r2 = (*m_arg2)(data);
       return r1 and r2;
     }
 
-    void dump(std::ostream& out) const override {
+    void dump(std::ostream& out, bool const ignoreMasks = false) const override {
       out << '(';
-      m_arg1->dump(out);
+      m_arg1->dump(out, ignoreMasks);
       out << " AND ";
-      m_arg2->dump(out);
+      m_arg2->dump(out, ignoreMasks);
       out << ')';
     }
   };
@@ -87,17 +97,17 @@ namespace triggerExpression {
     OperatorOr(Evaluator* arg1, Evaluator* arg2) : BinaryOperator(arg1, arg2) {}
 
     bool operator()(const Data& data) const override {
-      // force the execution af both arguments, otherwise precalers won't work properly
+      // force the execution of both arguments, otherwise prescalers won't work properly
       bool r1 = (*m_arg1)(data);
       bool r2 = (*m_arg2)(data);
       return r1 or r2;
     }
 
-    void dump(std::ostream& out) const override {
+    void dump(std::ostream& out, bool const ignoreMasks = false) const override {
       out << '(';
-      m_arg1->dump(out);
+      m_arg1->dump(out, ignoreMasks);
       out << " OR ";
-      m_arg2->dump(out);
+      m_arg2->dump(out, ignoreMasks);
       out << ')';
     }
   };
@@ -107,19 +117,47 @@ namespace triggerExpression {
     OperatorXor(Evaluator* arg1, Evaluator* arg2) : BinaryOperator(arg1, arg2) {}
 
     bool operator()(const Data& data) const override {
-      // force the execution af both arguments, otherwise precalers won't work properly
+      // force the execution of both arguments, otherwise prescalers won't work properly
       bool r1 = (*m_arg1)(data);
       bool r2 = (*m_arg2)(data);
       return r1 xor r2;
     }
 
-    void dump(std::ostream& out) const override {
-      m_arg1->dump(out);
+    void dump(std::ostream& out, bool const ignoreMasks = false) const override {
+      out << '(';
+      m_arg1->dump(out, ignoreMasks);
       out << " XOR ";
-      m_arg2->dump(out);
+      m_arg2->dump(out, ignoreMasks);
+      out << ')';
+    }
+  };
+
+  class OperatorMasking : public BinaryOperator {
+  public:
+    OperatorMasking(Evaluator* arg1, Evaluator* arg2) : BinaryOperator(arg1, arg2) {}
+
+    bool operator()(const Data& data) const override { return (*m_arg1)(data); }
+
+    void init(const Data& data) override {
+      m_arg1->init(data);
+      m_arg2->init(data);
+      m_arg1->mask(*m_arg2);
+    }
+
+    // apply mask(s) only to the first Evaluator
+    // (the second Evaluator is not used in the decision of OperatorMasking)
+    void mask(Evaluator const& arg) override { m_arg1->mask(arg); }
+
+    void dump(std::ostream& out, bool const ignoreMasks = false) const override {
+      out << '(';
+      // ignore masks on the first Evaluator to dump the full logical expression
+      m_arg1->dump(out, true);
+      out << " MASKING ";
+      m_arg2->dump(out, ignoreMasks);
+      out << ')';
     }
   };
 
 }  // namespace triggerExpression
 
-#endif  // HLTrigger_HLTfilters_TriggerExpressionOperators_h
+#endif  // HLTrigger_HLTcore_TriggerExpressionOperators_h
diff --git a/HLTrigger/HLTcore/interface/TriggerExpressionParser.h b/HLTrigger/HLTcore/interface/TriggerExpressionParser.h
index 5c107d16c0933..ac81d2d21d7ad 100644
--- a/HLTrigger/HLTcore/interface/TriggerExpressionParser.h
+++ b/HLTrigger/HLTcore/interface/TriggerExpressionParser.h
@@ -1,5 +1,5 @@
-#ifndef HLTrigger_HLTfilters_TriggerExpressionParser_h
-#define HLTrigger_HLTfilters_TriggerExpressionParser_h
+#ifndef HLTrigger_HLTcore_TriggerExpressionParser_h
+#define HLTrigger_HLTcore_TriggerExpressionParser_h
 
 // Note: this requires Boost 1.41 or higher, for Spirit 2.1 or higher
 #include <boost/phoenix.hpp>
@@ -25,20 +25,27 @@ namespace triggerExpression {
     Parser() : Parser::base_type(expression) {
       auto delimiter = qi::copy(qi::eoi | !qi::char_("a-zA-Z0-9_*?"));
 
-      token_true = qi::lexeme[qi::lit("TRUE") >> delimiter];
-      token_false = qi::lexeme[qi::lit("FALSE") >> delimiter];
-
       operand_not = qi::lexeme[qi::lit("NOT") >> delimiter];
       operand_and = qi::lexeme[qi::lit("AND") >> delimiter];
       operand_or = qi::lexeme[qi::lit("OR") >> delimiter];
+      operand_masking = qi::lexeme[qi::lit("MASKING") >> delimiter];
+
+      // "TRUE": keyword to accept all events
+      token_true = qi::lexeme[qi::lit("TRUE") >> delimiter];
 
+      // "FALSE": keyword to reject all events
+      token_false = qi::lexeme[qi::lit("FALSE") >> delimiter];
+
+      // Level-1 Global Trigger decisions: must begin with characters "L1_"
       token_l1algo %= qi::raw[qi::lexeme["L1_" >> +(qi::char_("a-zA-Z0-9_*?"))]];
-      token_path %= qi::raw[qi::lexeme[+(qi::char_("a-zA-Z0-9_*?"))] - operand_not - operand_and - operand_or];
 
-      token = (token_true[qi::_val = new_<Constant>(true)] |         // TRUE
-               token_false[qi::_val = new_<Constant>(false)] |       // FALSE
-               token_l1algo[qi::_val = new_<L1uGTReader>(qi::_1)] |  // L1_*
-               token_path[qi::_val = new_<PathReader>(qi::_1)]);     // * (except "NOT", "AND" and "OR")
+      // Decisions of Paths in the CMSSW configuration (e.g. high-level triggers):
+      // any alphanumeric pattern except for "TRUE", "FALSE", "NOT", "AND", "OR", and "MASKING"
+      token_path %= qi::raw[qi::lexeme[+(qi::char_("a-zA-Z0-9_*?"))] - token_true - token_false - operand_not -
+                            operand_and - operand_or - operand_masking];
+
+      token = (token_true[qi::_val = new_<Constant>(true)] | token_false[qi::_val = new_<Constant>(false)] |
+               token_l1algo[qi::_val = new_<L1uGTReader>(qi::_1)] | token_path[qi::_val = new_<PathReader>(qi::_1)]);
 
       parenthesis %= ('(' >> expression >> ')');
 
@@ -50,9 +57,18 @@ namespace triggerExpression {
 
       unary = ((operand_not >> unary)[qi::_val = new_<OperatorNot>(qi::_1)] | operand[qi::_val = qi::_1]);
 
-      expression =
-          unary[qi::_val = qi::_1] >> *((operand_and >> unary)[qi::_val = new_<OperatorAnd>(qi::_val, qi::_1)] |
-                                        (operand_or >> unary)[qi::_val = new_<OperatorOr>(qi::_val, qi::_1)]);
+      // token_masking is used to restrict the argument (rhs) of the "MASKING"
+      // operation to Constant[FALSE], L1uGTReader and PathReader evaluators
+      token_masking =
+          (token_false[qi::_val = new_<Constant>(false)] | token_l1algo[qi::_val = new_<L1uGTReader>(qi::_1)] |
+           token_path[qi::_val = new_<PathReader>(qi::_1)]);
+
+      argument_masking %= (token_masking | ('(' >> argument_masking >> ')'));
+
+      expression = unary[qi::_val = qi::_1] >>
+                   *((operand_and >> unary)[qi::_val = new_<OperatorAnd>(qi::_val, qi::_1)] |
+                     (operand_or >> unary)[qi::_val = new_<OperatorOr>(qi::_val, qi::_1)] |
+                     (operand_masking >> argument_masking)[qi::_val = new_<OperatorMasking>(qi::_val, qi::_1)]);
     }
 
   private:
@@ -66,6 +82,7 @@ namespace triggerExpression {
     terminal_rule operand_not;
     terminal_rule operand_and;
     terminal_rule operand_or;
+    terminal_rule operand_masking;
 
     name_rule token_l1algo;
     name_rule token_path;
@@ -76,6 +93,8 @@ namespace triggerExpression {
     rule prescale;
     rule operand;
     rule unary;
+    rule token_masking;
+    rule argument_masking;
     rule expression;
   };
 
@@ -121,4 +140,4 @@ namespace triggerExpression {
 
 }  // namespace triggerExpression
 
-#endif  // HLTrigger_HLTfilters_TriggerExpressionParser_h
+#endif  // HLTrigger_HLTcore_TriggerExpressionParser_h
diff --git a/HLTrigger/HLTcore/interface/TriggerExpressionPathReader.h b/HLTrigger/HLTcore/interface/TriggerExpressionPathReader.h
index decba2a243aa7..9ea681307a41e 100644
--- a/HLTrigger/HLTcore/interface/TriggerExpressionPathReader.h
+++ b/HLTrigger/HLTcore/interface/TriggerExpressionPathReader.h
@@ -1,8 +1,5 @@
-#ifndef HLTrigger_HLTfilters_TriggerExpressionPathReader_h
-#define HLTrigger_HLTfilters_TriggerExpressionPathReader_h
-
-#include <vector>
-#include <string>
+#ifndef HLTrigger_HLTcore_TriggerExpressionPathReader_h
+#define HLTrigger_HLTcore_TriggerExpressionPathReader_h
 
 #include "HLTrigger/HLTcore/interface/TriggerExpressionEvaluator.h"
 
@@ -10,22 +7,29 @@ namespace triggerExpression {
 
   class PathReader : public Evaluator {
   public:
-    PathReader(const std::string& pattern) : m_pattern{pattern}, m_triggers{}, m_initialised{false} {}
+    PathReader(const std::string& pattern)
+        : m_pattern{pattern}, m_triggers{}, m_triggersAfterMasking{}, m_initialised{false} {}
 
     bool operator()(const Data& data) const override;
 
     void init(const Data& data) override;
 
-    std::vector<std::string> patterns() const override { return std::vector<std::string>{m_pattern}; }
+    std::vector<std::string> patterns() const override { return {m_pattern}; }
+
+    void dump(std::ostream& out, bool const ignoreMasks = false) const override;
+
+    void mask(Evaluator const& eval) override;
 
-    void dump(std::ostream& out) const override;
+    std::vector<std::pair<std::string, unsigned int>> triggers() const override { return m_triggers; }
+    std::vector<std::pair<std::string, unsigned int>> triggersAfterMasking() const { return m_triggersAfterMasking; }
 
   private:
     std::string m_pattern;
     std::vector<std::pair<std::string, unsigned int>> m_triggers;
+    std::vector<std::pair<std::string, unsigned int>> m_triggersAfterMasking;
     bool m_initialised;
   };
 
 }  // namespace triggerExpression
 
-#endif  // HLTrigger_HLTfilters_TriggerExpressionPathReader_h
+#endif  // HLTrigger_HLTcore_TriggerExpressionPathReader_h
diff --git a/HLTrigger/HLTcore/interface/TriggerExpressionPrescaler.h b/HLTrigger/HLTcore/interface/TriggerExpressionPrescaler.h
index cc8e6d8294c2d..c7d65e86f3c06 100644
--- a/HLTrigger/HLTcore/interface/TriggerExpressionPrescaler.h
+++ b/HLTrigger/HLTcore/interface/TriggerExpressionPrescaler.h
@@ -1,8 +1,7 @@
-#ifndef HLTrigger_HLTfilters_TriggerExpressionPrescaler_h
-#define HLTrigger_HLTfilters_TriggerExpressionPrescaler_h
+#ifndef HLTrigger_HLTcore_TriggerExpressionPrescaler_h
+#define HLTrigger_HLTcore_TriggerExpressionPrescaler_h
 
 #include "HLTrigger/HLTcore/interface/TriggerExpressionOperators.h"
-#include "HLTrigger/HLTcore/interface/TriggerExpressionData.h"
 
 namespace triggerExpression {
 
@@ -14,7 +13,11 @@ namespace triggerExpression {
 
     void init(const Data& data) override;
 
-    void dump(std::ostream& out) const override { out << "(" << (*m_arg) << " / " << m_prescale << ")"; }
+    void dump(std::ostream& out, bool const ignoreMasks = false) const override {
+      out << '(';
+      m_arg->dump(out, ignoreMasks);
+      out << " / " << m_prescale << ')';
+    }
 
   private:
     unsigned int m_prescale;
@@ -23,4 +26,4 @@ namespace triggerExpression {
 
 }  // namespace triggerExpression
 
-#endif  // HLTrigger_HLTfilters_TriggerExpressionPrescaler_h
+#endif  // HLTrigger_HLTcore_TriggerExpressionPrescaler_h
diff --git a/HLTrigger/HLTcore/src/TriggerExpressionL1uGTReader.cc b/HLTrigger/HLTcore/src/TriggerExpressionL1uGTReader.cc
index 38826ba674500..1a6a0d468d5b3 100644
--- a/HLTrigger/HLTcore/src/TriggerExpressionL1uGTReader.cc
+++ b/HLTrigger/HLTcore/src/TriggerExpressionL1uGTReader.cc
@@ -3,9 +3,7 @@
 
 #include "FWCore/MessageLogger/interface/MessageLogger.h"
 #include "FWCore/Utilities/interface/RegexMatch.h"
-#include "CondFormats/L1TObjects/interface/L1GtTriggerMask.h"
 #include "CondFormats/L1TObjects/interface/L1TUtmTriggerMenu.h"
-#include "DataFormats/L1GlobalTrigger/interface/L1GlobalTriggerReadoutRecord.h"
 #include "HLTrigger/HLTcore/interface/TriggerExpressionData.h"
 #include "HLTrigger/HLTcore/interface/TriggerExpressionL1uGTReader.h"
 
@@ -20,26 +18,31 @@ namespace triggerExpression {
     if (word.empty())
       return false;
 
-    for (auto const& trigger : m_triggers)
+    auto const& triggers = masksEnabled() ? m_triggersAfterMasking : m_triggers;
+
+    for (auto const& trigger : triggers)
       if (trigger.second < word.size() and word[trigger.second])
         return true;
 
     return false;
   }
 
-  void L1uGTReader::dump(std::ostream& out) const {
+  void L1uGTReader::dump(std::ostream& out, bool const ignoreMasks) const {
     if (not m_initialised) {
       out << "Uninitialised_L1_Expression";
       return;
     }
-    if (m_triggers.empty()) {
+
+    auto const& triggers = ignoreMasks or not masksEnabled() ? m_triggers : m_triggersAfterMasking;
+
+    if (triggers.empty()) {
       out << "FALSE";
-    } else if (m_triggers.size() == 1) {
-      out << m_triggers[0].first;
+    } else if (triggers.size() == 1) {
+      out << triggers[0].first;
     } else {
-      out << "(" << m_triggers[0].first;
-      for (unsigned int i = 1; i < m_triggers.size(); ++i)
-        out << " OR " << m_triggers[i].first;
+      out << "(" << triggers[0].first;
+      for (unsigned int i = 1; i < triggers.size(); ++i)
+        out << " OR " << triggers[i].first;
       out << ")";
     }
   }
@@ -61,14 +64,15 @@ namespace triggerExpression {
       if (entry != triggerMap.end()) {
         // single L1 bit
         m_triggers.push_back(std::make_pair(m_pattern, entry->second.getIndex()));
-      } else
-          // trigger not found in the current menu
-          if (data.shouldThrow())
-        throw cms::Exception("Configuration")
-            << "requested L1 trigger \"" << m_pattern << "\" does not exist in the current L1 menu";
-      else
-        edm::LogWarning("Configuration") << "requested L1 trigger \"" << m_pattern
-                                         << "\" does not exist in the current L1 menu";
+      } else {
+        // trigger not found in the current menu
+        if (data.shouldThrow())
+          throw cms::Exception("Configuration")
+              << "requested L1 trigger \"" << m_pattern << "\" does not exist in the current L1 menu";
+        else
+          edm::LogWarning("Configuration")
+              << "requested L1 trigger \"" << m_pattern << "\" does not exist in the current L1 menu";
+      }
     } else {
       // expand wildcards in the pattern
       bool match = false;
@@ -90,7 +94,42 @@ namespace triggerExpression {
       }
     }
 
+    m_triggersAfterMasking = m_triggers;
     m_initialised = true;
   }
 
+  void L1uGTReader::mask(Evaluator const& eval) {
+    auto const& triggersToMask = eval.triggers();
+
+    if (triggersToMask.empty()) {
+      edm::LogInfo("NoTriggersToMask") << "\tL1uGTReader[\"" << *this << "\"]::mask(arg = \"" << eval << "\")"
+                                       << " failed: arg.triggers() is empty";
+      return;
+    }
+
+    // patterns() is always empty for a L1uGTReader, and not empty for PathReader;
+    // here, PathReader evaluators are skipped as they shouldn't be used to mask a L1uGTReader
+    if (not eval.patterns().empty()) {
+      edm::LogWarning("InvalidArgumentForMasking")
+          << "\tL1uGTReader[\"" << *this << "\"]::mask(arg = \"" << eval << "\")"
+          << " failed: arg.patterns() is not empty (arg is not a L1uGTReader)";
+      return;
+    }
+
+    enableMasks();
+
+    // clang-format off
+    m_triggersAfterMasking.erase(
+      std::remove_if(
+        m_triggersAfterMasking.begin(),
+        m_triggersAfterMasking.end(),
+        [&triggersToMask](auto const& foo) {
+          return std::find(triggersToMask.begin(), triggersToMask.end(), foo) != triggersToMask.end();
+        }
+      ),
+      m_triggersAfterMasking.end()
+    );
+    // clang-format on
+  }
+
 }  // namespace triggerExpression
diff --git a/HLTrigger/HLTcore/src/TriggerExpressionPathReader.cc b/HLTrigger/HLTcore/src/TriggerExpressionPathReader.cc
index 0fda7507e34bd..71254f9f514d6 100644
--- a/HLTrigger/HLTcore/src/TriggerExpressionPathReader.cc
+++ b/HLTrigger/HLTcore/src/TriggerExpressionPathReader.cc
@@ -3,9 +3,8 @@
 
 #include "FWCore/Utilities/interface/RegexMatch.h"
 #include "FWCore/MessageLogger/interface/MessageLogger.h"
-#include "DataFormats/Common/interface/TriggerResults.h"
-#include "HLTrigger/HLTcore/interface/TriggerExpressionPathReader.h"
 #include "HLTrigger/HLTcore/interface/TriggerExpressionData.h"
+#include "HLTrigger/HLTcore/interface/TriggerExpressionPathReader.h"
 
 namespace triggerExpression {
 
@@ -14,26 +13,31 @@ namespace triggerExpression {
     if (not data.hasHLT() && not data.usePathStatus())
       return false;
 
-    for (auto const& trigger : m_triggers)
+    auto const& triggers = masksEnabled() ? m_triggersAfterMasking : m_triggers;
+
+    for (auto const& trigger : triggers)
       if (data.passHLT(trigger.second))
         return true;
 
     return false;
   }
 
-  void PathReader::dump(std::ostream& out) const {
+  void PathReader::dump(std::ostream& out, bool const ignoreMasks) const {
     if (not m_initialised) {
       out << "Uninitialised_Path_Expression";
       return;
     }
-    if (m_triggers.empty()) {
+
+    auto const& triggers = ignoreMasks or not masksEnabled() ? m_triggers : m_triggersAfterMasking;
+
+    if (triggers.empty()) {
       out << "FALSE";
-    } else if (m_triggers.size() == 1) {
-      out << m_triggers[0].first;
+    } else if (triggers.size() == 1) {
+      out << triggers[0].first;
     } else {
-      out << "(" << m_triggers[0].first;
-      for (unsigned int i = 1; i < m_triggers.size(); ++i)
-        out << " OR " << m_triggers[i].first;
+      out << "(" << triggers[0].first;
+      for (unsigned int i = 1; i < triggers.size(); ++i)
+        out << " OR " << triggers[i].first;
       out << ")";
     }
   }
@@ -89,7 +93,42 @@ namespace triggerExpression {
       }
     }
 
+    m_triggersAfterMasking = m_triggers;
     m_initialised = true;
   }
 
+  void PathReader::mask(Evaluator const& eval) {
+    auto const& triggersToMask = eval.triggers();
+
+    if (triggersToMask.empty()) {
+      edm::LogInfo("NoTriggersToMask") << "\tPathReader[\"" << *this << "\"]::mask(arg = \"" << eval << "\")"
+                                       << " failed: arg.triggers() is empty";
+      return;
+    }
+
+    // patterns() is always empty for a L1uGTReader, and not empty for PathReader;
+    // here, L1uGTReader evaluators are skipped as they shouldn't be used to mask a PathReader
+    if (eval.patterns().empty()) {
+      edm::LogWarning("InvalidArgumentForMasking")
+          << "\tPathReader[\"" << *this << "\"]::mask(arg = \"" << eval << "\")"
+          << " failed: arg.patterns() is empty (arg is not a PathReader)";
+      return;
+    }
+
+    enableMasks();
+
+    // clang-format off
+    m_triggersAfterMasking.erase(
+      std::remove_if(
+        m_triggersAfterMasking.begin(),
+        m_triggersAfterMasking.end(),
+        [&triggersToMask](auto const& foo) {
+          return std::find(triggersToMask.begin(), triggersToMask.end(), foo) != triggersToMask.end();
+        }
+      ),
+      m_triggersAfterMasking.end()
+    );
+    // clang-format on
+  }
+
 }  // namespace triggerExpression
diff --git a/HLTrigger/HLTcore/src/TriggerExpressionPrescaler.cc b/HLTrigger/HLTcore/src/TriggerExpressionPrescaler.cc
index 6eed7dc05e173..dd4bdb3e9a7fb 100644
--- a/HLTrigger/HLTcore/src/TriggerExpressionPrescaler.cc
+++ b/HLTrigger/HLTcore/src/TriggerExpressionPrescaler.cc
@@ -1,4 +1,5 @@
 #include "HLTrigger/HLTcore/interface/TriggerExpressionPrescaler.h"
+#include "HLTrigger/HLTcore/interface/TriggerExpressionData.h"
 
 namespace triggerExpression {
 
@@ -24,7 +25,7 @@ namespace triggerExpression {
     UnaryOperator::init(data);
 
     // initialize the counter to the first event number seen,
-    // in order to avoid all prescalers on different FUs to be syncronous
+    // in order to avoid all prescalers on different FUs to be synchronous
     m_counter = data.eventNumber();
   }
 
diff --git a/HLTrigger/HLTcore/test/test_catch2_TriggerExpressionParser.cc b/HLTrigger/HLTcore/test/test_catch2_TriggerExpressionParser.cc
index 93c64886b6d96..0b7d5c5d666b9 100644
--- a/HLTrigger/HLTcore/test/test_catch2_TriggerExpressionParser.cc
+++ b/HLTrigger/HLTcore/test/test_catch2_TriggerExpressionParser.cc
@@ -80,6 +80,16 @@ TEST_CASE("Test TriggerExpressionParser", "[TriggerExpressionParser]") {
     REQUIRE(testExpression("NOT(THIS OR THAT)AND(L1_THEOTHER)OR(NOTFALSE)",  //
                            "(((NOT (Uninitialised_Path_Expression OR Uninitialised_Path_Expression))"
                            " AND Uninitialised_L1_Expression) OR Uninitialised_Path_Expression)"));
+    REQUIRE(testExpression("EXPR_A MASKING L1_?",  //
+                           "(Uninitialised_Path_Expression MASKING Uninitialised_L1_Expression)"));
+    REQUIRE(testExpression(
+        "L1_*copy* MASKING L1_*copy MASKING ((L1_*copy2))",  //
+        "((Uninitialised_L1_Expression MASKING Uninitialised_L1_Expression) MASKING Uninitialised_L1_Expression)"));
+    REQUIRE(testExpression(
+        "(A AND B OR C) MASKING D OR E",  //
+        "((((Uninitialised_Path_Expression AND Uninitialised_Path_Expression) OR Uninitialised_Path_Expression)"
+        " MASKING Uninitialised_Path_Expression) OR Uninitialised_Path_Expression)"));
+    REQUIRE(testExpression("EXPR_A MASKING FALSE", "(Uninitialised_Path_Expression MASKING FALSE)"));
   }
 
   // examples of expressions not supported by the triggerExpression parser
@@ -92,5 +102,14 @@ TEST_CASE("Test TriggerExpressionParser", "[TriggerExpressionParser]") {
     REQUIRE(not testExpression("ThisPath ANDThatPath"));
     REQUIRE(not testExpression("ThisPath AND ThatPath AND OR"));
     REQUIRE(not testExpression("ThisPath AND ThatPath OR NOT"));
+    REQUIRE(not testExpression("ThisPath AND ThatPath MASKING MASKING"));
+    REQUIRE(not testExpression("Path_? AND MASKING Path_2"));
+    REQUIRE(not testExpression("MASKING Path_1 AND Path_?"));
+    REQUIRE(not testExpression("EXPR_1 MASKING (Path_1 OR Path_2)"));
+    REQUIRE(not testExpression("EXPR_1 MASKING TRUE"));
+    REQUIRE(not testExpression("EXPR_1 MASKING (NOT Path_1)"));
+    REQUIRE(not testExpression("EXPR_1 MASKING (Path_1 / 15)"));
+    REQUIRE(not testExpression("EXPR_1 MASKING (Path*_* MASKING Path1_*)"));
+    REQUIRE(not testExpression("EXPR_1 MASKINGPath2"));
   }
 }
diff --git a/HLTrigger/HLTfilters/test/triggerResultsFilter_by_PathStatus.py b/HLTrigger/HLTfilters/test/triggerResultsFilter_by_PathStatus.py
index 2085db39d2bc9..569b1ba7b317d 100644
--- a/HLTrigger/HLTfilters/test/triggerResultsFilter_by_PathStatus.py
+++ b/HLTrigger/HLTfilters/test/triggerResultsFilter_by_PathStatus.py
@@ -86,6 +86,18 @@
     triggerConditions =  ( '(Path_1) / 15', )
 )
 
+# accept if 'Path_1' succeeds, prescaled by 15
+# masking Path_2 (equivalent to filter_1_pre)
+process.filter_1_pre_with_masks1 = _triggerResultsFilter.clone(
+    triggerConditions =  ( '(Path_1 / 15 OR Path_2) MASKING Path_2', )
+)
+
+# accept if 'Path_1' succeeds, prescaled by 15
+# masking Path_2 and Path_3 (equivalent to filter_1_pre)
+process.filter_1_pre_with_masks2 = _triggerResultsFilter.clone(
+    triggerConditions =  ( '(Path_? / 15) MASKING Path_2 MASKING Path_3', )
+)
+
 # accept if 'Path_1' prescaled by 15 does not succeed
 process.filter_not_1_pre = _triggerResultsFilter.clone(
     triggerConditions =  ( 'NOT (Path_1 / 15)', )
@@ -193,6 +205,8 @@
 process.Check_Any_Star = cms.Path( process.filter_any_star )
 
 process.Check_1_Pre     = cms.Path( process.filter_1_pre )
+process.Check_1_Pre_With_Masks1 = cms.Path( process.filter_1_pre_with_masks1 )
+process.Check_1_Pre_With_Masks2 = cms.Path( process.filter_1_pre_with_masks2 )
 process.Check_NOT_1_Pre = cms.Path( process.filter_not_1_pre )
 process.Check_2_Pre     = cms.Path( process.filter_2_pre )
 process.Check_Any_Pre   = cms.Path( process.filter_any_pre )
diff --git a/HLTrigger/HLTfilters/test/triggerResultsFilter_by_PathStatus.sh b/HLTrigger/HLTfilters/test/triggerResultsFilter_by_PathStatus.sh
index 74bfacaffb8fd..52ca6e8d947ab 100755
--- a/HLTrigger/HLTfilters/test/triggerResultsFilter_by_PathStatus.sh
+++ b/HLTrigger/HLTfilters/test/triggerResultsFilter_by_PathStatus.sh
@@ -34,23 +34,25 @@ TrigReport     1    9       1000         33        967          0 Check_All_Expl
 TrigReport     1   10       1000        733        267          0 Check_Any_Or
 TrigReport     1   11       1000        733        267          0 Check_Any_Star
 TrigReport     1   12       1000         33        967          0 Check_1_Pre
-TrigReport     1   13       1000        967         33          0 Check_NOT_1_Pre
-TrigReport     1   14       1000         33        967          0 Check_2_Pre
-TrigReport     1   15       1000         99        901          0 Check_Any_Pre
-TrigReport     1   16       1000         99        901          0 Check_Any_Pre_DoubleNOT
-TrigReport     1   17       1000        901         99          0 Check_Not_Any_Pre
-TrigReport     1   18       1000        733        267          0 Check_Any_Question
-TrigReport     1   19       1000        733        267          0 Check_Any_StarQuestion
-TrigReport     1   20       1000          0       1000          0 Check_Wrong_Name
-TrigReport     1   21       1000          0       1000          0 Check_Wrong_Pattern
-TrigReport     1   22       1000       1000          0          0 Check_Not_Wrong_Pattern
-TrigReport     1   23       1000          0       1000          0 Check_Empty_Pattern
-TrigReport     1   24       1000          0       1000          0 Check_L1Path_Pattern
-TrigReport     1   25       1000          0       1000          0 Check_L1Singlemuopen_Pattern
-TrigReport     1   26       1000       1000          0          0 Check_True_Pattern
-TrigReport     1   27       1000          0       1000          0 Check_False_Pattern
-TrigReport     1   28       1000       1000          0          0 Check_AlwaysNOTFalse_Pattern
-TrigReport     1   29       1000       1000          0          0 Check_NOTAlwaysFALSE_Pattern
+TrigReport     1   13       1000         33        967          0 Check_1_Pre_With_Masks1
+TrigReport     1   14       1000         33        967          0 Check_1_Pre_With_Masks2
+TrigReport     1   15       1000        967         33          0 Check_NOT_1_Pre
+TrigReport     1   16       1000         33        967          0 Check_2_Pre
+TrigReport     1   17       1000         99        901          0 Check_Any_Pre
+TrigReport     1   18       1000         99        901          0 Check_Any_Pre_DoubleNOT
+TrigReport     1   19       1000        901         99          0 Check_Not_Any_Pre
+TrigReport     1   20       1000        733        267          0 Check_Any_Question
+TrigReport     1   21       1000        733        267          0 Check_Any_StarQuestion
+TrigReport     1   22       1000          0       1000          0 Check_Wrong_Name
+TrigReport     1   23       1000          0       1000          0 Check_Wrong_Pattern
+TrigReport     1   24       1000       1000          0          0 Check_Not_Wrong_Pattern
+TrigReport     1   25       1000          0       1000          0 Check_Empty_Pattern
+TrigReport     1   26       1000          0       1000          0 Check_L1Path_Pattern
+TrigReport     1   27       1000          0       1000          0 Check_L1Singlemuopen_Pattern
+TrigReport     1   28       1000       1000          0          0 Check_True_Pattern
+TrigReport     1   29       1000          0       1000          0 Check_False_Pattern
+TrigReport     1   30       1000       1000          0          0 Check_AlwaysNOTFalse_Pattern
+TrigReport     1   31       1000       1000          0          0 Check_NOTAlwaysFALSE_Pattern
 @EOF
 
 # compare to expected output of test job
diff --git a/HLTrigger/HLTfilters/test/triggerResultsFilter_by_TriggerResults.py b/HLTrigger/HLTfilters/test/triggerResultsFilter_by_TriggerResults.py
index de019f1825cac..e89ad7d74f7fb 100644
--- a/HLTrigger/HLTfilters/test/triggerResultsFilter_by_TriggerResults.py
+++ b/HLTrigger/HLTfilters/test/triggerResultsFilter_by_TriggerResults.py
@@ -47,6 +47,18 @@
     triggerConditions =  ( '(Path_1) / 15', )
 )
 
+# accept if 'Path_1' succeeds, prescaled by 15
+# masking Path_2 (equivalent to filter_1_pre)
+process.filter_1_pre_with_masks1 = _triggerResultsFilter.clone(
+    triggerConditions =  ( '(Path_1 / 15 OR Path_2) MASKING Path_2', )
+)
+
+# accept if 'Path_1' succeeds, prescaled by 15
+# masking Path_2 and Path_3 (equivalent to filter_1_pre)
+process.filter_1_pre_with_masks2 = _triggerResultsFilter.clone(
+    triggerConditions =  ( '(Path_? / 15) MASKING Path_2 MASKING Path_3', )
+)
+
 # accept if 'Path_1' prescaled by 15 does not succeed
 process.filter_not_1_pre = _triggerResultsFilter.clone(
     triggerConditions =  ( 'NOT (Path_1 / 15)', )
@@ -146,6 +158,8 @@
 process.path_any_star = cms.Path( process.filter_any_star )
 
 process.path_1_pre     = cms.Path( process.filter_1_pre )
+process.path_1_pre_with_masks1 = cms.Path( process.filter_1_pre_with_masks1 )
+process.path_1_pre_with_masks2 = cms.Path( process.filter_1_pre_with_masks2 )
 process.path_not_1_pre = cms.Path( process.filter_not_1_pre )
 process.path_2_pre     = cms.Path( process.filter_2_pre )
 process.path_any_pre   = cms.Path( process.filter_any_pre )
diff --git a/HLTrigger/HLTfilters/test/triggerResultsFilter_by_TriggerResults.sh b/HLTrigger/HLTfilters/test/triggerResultsFilter_by_TriggerResults.sh
index b036db4b6f64c..968a2d5e6cf16 100755
--- a/HLTrigger/HLTfilters/test/triggerResultsFilter_by_TriggerResults.sh
+++ b/HLTrigger/HLTfilters/test/triggerResultsFilter_by_TriggerResults.sh
@@ -31,21 +31,23 @@ TrigReport     1    3       1000         33        967          0 path_all_expli
 TrigReport     1    4       1000        733        267          0 path_any_or
 TrigReport     1    5       1000       1000          0          0 path_any_star
 TrigReport     1    6       1000         33        967          0 path_1_pre
-TrigReport     1    7       1000        967         33          0 path_not_1_pre
-TrigReport     1    8       1000         33        967          0 path_2_pre
-TrigReport     1    9       1000         99        901          0 path_any_pre
-TrigReport     1   10       1000         99        901          0 path_any_pre_doubleNOT
-TrigReport     1   11       1000        901         99          0 path_not_any_pre
-TrigReport     1   12       1000       1000          0          0 path_any_doublestar
-TrigReport     1   13       1000        733        267          0 path_any_question
-TrigReport     1   14       1000          0       1000          0 path_wrong_name
-TrigReport     1   15       1000          0       1000          0 path_wrong_pattern
-TrigReport     1   16       1000       1000          0          0 path_not_wrong_pattern
-TrigReport     1   17       1000          0       1000          0 path_empty_pattern
-TrigReport     1   18       1000          0       1000          0 path_l1path_pattern
-TrigReport     1   19       1000          0       1000          0 path_l1singlemuopen_pattern
-TrigReport     1   20       1000       1000          0          0 path_true_pattern
-TrigReport     1   21       1000          0       1000          0 path_false_pattern
+TrigReport     1    7       1000         33        967          0 path_1_pre_with_masks1
+TrigReport     1    8       1000         33        967          0 path_1_pre_with_masks2
+TrigReport     1    9       1000        967         33          0 path_not_1_pre
+TrigReport     1   10       1000         33        967          0 path_2_pre
+TrigReport     1   11       1000         99        901          0 path_any_pre
+TrigReport     1   12       1000         99        901          0 path_any_pre_doubleNOT
+TrigReport     1   13       1000        901         99          0 path_not_any_pre
+TrigReport     1   14       1000       1000          0          0 path_any_doublestar
+TrigReport     1   15       1000        733        267          0 path_any_question
+TrigReport     1   16       1000          0       1000          0 path_wrong_name
+TrigReport     1   17       1000          0       1000          0 path_wrong_pattern
+TrigReport     1   18       1000       1000          0          0 path_not_wrong_pattern
+TrigReport     1   19       1000          0       1000          0 path_empty_pattern
+TrigReport     1   20       1000          0       1000          0 path_l1path_pattern
+TrigReport     1   21       1000          0       1000          0 path_l1singlemuopen_pattern
+TrigReport     1   22       1000       1000          0          0 path_true_pattern
+TrigReport     1   23       1000          0       1000          0 path_false_pattern
 @EOF
 
 # compare to expected output of test job