Belle II Software  release-06-01-15
GeneralCut.h
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 #pragma once
9 
10 #include <framework/utilities/Conversion.h>
11 
12 #include <string>
13 #include <vector>
14 #include <memory>
15 
16 #include <boost/algorithm/string.hpp>
17 #include <sstream>
18 
19 #include <iostream>
20 #include <stdexcept>
21 #include <cmath>
22 
23 namespace Belle2 {
33  unsigned long int findMatchedParenthesis(std::string str, char open = '[', char close = ']');
34 
38  std::vector<std::string> splitOnDelimiterAndConserveParenthesis(std::string str, char delimiter, char open, char close);
39 
43  unsigned long int findIgnoringParenthesis(std::string str, std::string pattern, unsigned int begin = 0);
44 
48  bool almostEqualFloat(const float& a, const float& b);
49 
53  bool almostEqualDouble(const double& a, const double& b);
54 
90  template <class AVariableManager>
91  class GeneralCut {
93  typedef typename AVariableManager::Object Object;
95  typedef typename AVariableManager::Var Var;
96 
97  public:
104  static std::unique_ptr<GeneralCut> compile(const std::string& cut)
105  {
106  return std::unique_ptr<GeneralCut>(new GeneralCut(cut));
107  }
113  bool check(const Object* p) const
114  {
115  switch (m_operation) {
116  case EMPTY:
117  return true;
118  case NONE:
119  return std::isnan(this->get(p)) ? false : this->get(p);
120  case AND:
121  return m_left->check(p) and m_right->check(p);
122  case OR:
123  return m_left->check(p) or m_right->check(p);
124  case LT:
125  return m_left->get(p) < m_right->get(p);
126  case LE:
127  return m_left->get(p) <= m_right->get(p);
128  case GT:
129  return m_left->get(p) > m_right->get(p);
130  case GE:
131  return m_left->get(p) >= m_right->get(p);
132  case EQ:
133  return almostEqualDouble(m_left->get(p), m_right->get(p));
134  case NE:
135  return not almostEqualDouble(m_left->get(p), m_right->get(p));
136  }
137  throw std::runtime_error("Cut string has an invalid format: Invalid operation");
138  return false;
139  }
140 
144  void print() const
145  {
146  switch (m_operation) {
147  case EMPTY: std::cout << "EMPTY" << std::endl; break;
148  case NONE: std::cout << "NONE" << std::endl; break;
149  case AND: std::cout << "AND" << std::endl; break;
150  case OR: std::cout << "OR" << std::endl; break;
151  case LT: std::cout << "LT" << std::endl; break;
152  case LE: std::cout << "LE" << std::endl; break;
153  case GT: std::cout << "GT" << std::endl; break;
154  case GE: std::cout << "GE" << std::endl; break;
155  case EQ: std::cout << "EQ" << std::endl; break;
156  case NE: std::cout << "NE" << std::endl; break;
157  }
158  if (m_left != nullptr) {
159  std::cout << "Left " << std::endl;
160  m_left->print();
161  std::cout << "End Left" << std::endl;
162  }
163  if (m_right != nullptr) {
164  std::cout << "Right " << std::endl;
165  m_right->print();
166  std::cout << "End Right" << std::endl;
167  }
168  }
169 
173  std::string decompile() const
174  {
175  std::stringstream stringstream;
176  if (m_operation == EMPTY) {
177  return "";
178  } else if (m_left != nullptr and m_right != nullptr) {
179 
180  stringstream << "[";
181  stringstream << m_left->decompile();
182 
183  switch (m_operation) {
184  case AND: stringstream << " and "; break;
185  case OR: stringstream << " or "; break;
186  case LT: stringstream << " < "; break;
187  case LE: stringstream << " <= "; break;
188  case GT: stringstream << " > "; break;
189  case GE: stringstream << " >= "; break;
190  case EQ: stringstream << " == "; break;
191  case NE: stringstream << " != "; break;
192  default: throw std::runtime_error("Cut string has an invalid format: Operator does not support left and right!"); break;
193  }
194 
195  stringstream << m_right->decompile();
196  stringstream << "]";
197 
198  } else if (m_left == nullptr and m_right == nullptr) {
199  switch (m_operation) {
200  case NONE:
201  if (m_isNumeric) {
202  stringstream << m_number;
203  } else if (m_var != nullptr) {
204  stringstream << m_var->name;
205  } else {
206  throw std::runtime_error("Cut string has an invalid format: Variable is empty!");
207  }
208  break;
209  default: throw std::runtime_error("Cut string has an invalid format: Invalid operator without left and right!"); break;
210  }
211  } else {
212  throw std::runtime_error("Cut string has an invalid format: invalid combination of left and right!");
213  }
214 
215  return stringstream.str();
216  }
217 
218 
219  private:
224  explicit GeneralCut(std::string str)
225  {
226  str = preprocess(str);
227  if (str.empty()) {
228  m_operation = EMPTY;
229  return;
230  }
231 
232  if (not processLogicConditions(str)) {
233  if (not processTernaryNumericConditions(str)) {
234  if (not processBinaryNumericConditions(str)) {
235  m_operation = NONE;
236  try {
237  m_number = Belle2::convertString<double>(str);
238  m_isNumeric = true;
239  } catch (std::invalid_argument&) {
240  m_isNumeric = false;
241  processVariable(str);
242  }
243  }
244  }
245  }
246  }
247 
251  GeneralCut(const GeneralCut&) = delete;
252 
256  GeneralCut& operator=(const GeneralCut&) = delete;
257 
261  std::string preprocess(std::string str) const
262  {
263  boost::algorithm::trim(str);
264 
265  while (str.size() > 1 and findMatchedParenthesis(str) == str.size() - 1) {
266  str = str.substr(1, str.size() - 2);
267  boost::algorithm::trim(str);
268  }
269 
270  return str;
271  }
272 
276  bool processLogicConditions(std::string str)
277  {
278  unsigned long int begin = findMatchedParenthesis(str);
279  unsigned long int pos = 0;
280 
281  if ((pos = findIgnoringParenthesis(str, " or ", begin)) != std::string::npos) {
282  m_operation = OR;
283  m_left = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(0, pos)));
284  m_right = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(pos + 4)));
285  return true;
286  }
287 
288  if ((pos = findIgnoringParenthesis(str, " and ", begin)) != std::string::npos) {
289  m_operation = AND;
290  m_left = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(0, pos)));
291  m_right = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(pos + 5)));
292  return true;
293  }
294 
295  return false;
296  }
297 
301  bool processBinaryNumericConditions(std::string str)
302  {
303  unsigned long int pos = 0;
304  if ((pos = findIgnoringParenthesis(str, "<=")) != std::string::npos) {
305  m_operation = LE;
306  m_left = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(0, pos)));
307  m_right = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(pos + 2)));
308  return true;
309  }
310  if ((pos = findIgnoringParenthesis(str, "<")) != std::string::npos) {
311  m_operation = LT;
312  m_left = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(0, pos)));
313  m_right = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(pos + 1)));
314  return true;
315  }
316  if ((pos = findIgnoringParenthesis(str, ">=")) != std::string::npos) {
317  m_operation = GE;
318  m_left = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(0, pos)));
319  m_right = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(pos + 2)));
320  return true;
321  }
322  if ((pos = findIgnoringParenthesis(str, ">")) != std::string::npos) {
323  m_operation = GT;
324  m_left = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(0, pos)));
325  m_right = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(pos + 1)));
326  return true;
327  }
328  if ((pos = findIgnoringParenthesis(str, "==")) != std::string::npos) {
329  m_operation = EQ;
330  m_left = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(0, pos)));
331  m_right = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(pos + 2)));
332  return true;
333  }
334  if ((pos = findIgnoringParenthesis(str, "!=")) != std::string::npos) {
335  m_operation = NE;
336  m_left = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(0, pos)));
337  m_right = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(pos + 2)));
338  return true;
339  }
340 
341  return false;
342  }
343 
347  bool processTernaryNumericConditions(std::string str)
348  {
349  for (auto& c : {"<", ">", "!", "="}) {
350 
351  unsigned long int pos1 = 0;
352  unsigned long int pos2 = 0;
353 
354  if (((pos1 = findIgnoringParenthesis(str, c)) != std::string::npos) and
355  ((pos2 = findIgnoringParenthesis(str, "<", pos1 + 2)) != std::string::npos
356  or (pos2 = findIgnoringParenthesis(str, ">", pos1 + 2)) != std::string::npos
357  or (pos2 = findIgnoringParenthesis(str, "!", pos1 + 2)) != std::string::npos
358  or (pos2 = findIgnoringParenthesis(str, "=", pos1 + 2)) != std::string::npos)) {
359  m_operation = AND;
360  m_left = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(0, pos2)));
361  if (str[pos1 + 1] == '=')
362  pos1++;
363  m_right = std::unique_ptr<GeneralCut>(new GeneralCut(str.substr(pos1 + 1)));
364  return true;
365  }
366  }
367 
368  return false;
369  }
370 
375  void processVariable(const std::string& str)
376  {
377  AVariableManager& manager = AVariableManager::Instance();
378  m_var = manager.getVariable(str);
379  if (m_var == nullptr) {
380  throw std::runtime_error(
381  "Cut string has an invalid format: Variable not found: " + str);
382  }
383  }
384 
388  double get(const Object* p) const
389  {
390  if (m_isNumeric) {
391  return m_number;
392  } else if (m_var != nullptr) {
393  return m_var->function(p);
394  } else {
395  throw std::runtime_error("Cut string has an invalid format: Neither number nor variable name");
396  }
397  }
398 
402  enum Operation {
403  EMPTY = 0,
404  NONE,
405  AND,
406  OR,
407  LT,
408  LE,
409  GT,
410  GE,
411  EQ,
412  NE,
414  const Var* m_var;
415  double m_number;
416  bool m_isNumeric;
417  std::unique_ptr<GeneralCut> m_left;
418  std::unique_ptr<GeneralCut> m_right;
419  };
421 }
This class implements a common way to implement cut/selection functionality for arbitrary objects.
Definition: GeneralCut.h:91
std::unique_ptr< GeneralCut > m_left
Left-side cut.
Definition: GeneralCut.h:417
static std::unique_ptr< GeneralCut > compile(const std::string &cut)
Creates an instance of a cut and returns a unique_ptr to it, if you need a copy-able object instead y...
Definition: GeneralCut.h:104
AVariableManager::Var Var
Variable returned by the variable manager.
Definition: GeneralCut.h:95
enum Belle2::GeneralCut::Operation m_operation
Operation which connects left and right cut.
std::string preprocess(std::string str) const
Preprocess cut string.
Definition: GeneralCut.h:261
GeneralCut(std::string str)
Constructor of the cut.
Definition: GeneralCut.h:224
bool processTernaryNumericConditions(std::string str)
Look for numeric ternary conditions (e.g.
Definition: GeneralCut.h:347
bool processLogicConditions(std::string str)
Look for logical conditions in the given cut string.
Definition: GeneralCut.h:276
Operation
Enum with the allowed operations of the Cut Tree.
Definition: GeneralCut.h:402
std::unique_ptr< GeneralCut > m_right
Right-side cut.
Definition: GeneralCut.h:418
GeneralCut(const GeneralCut &)=delete
Delete Copy constructor.
double get(const Object *p) const
Returns stored number or Variable value for the given object.
Definition: GeneralCut.h:388
bool processBinaryNumericConditions(std::string str)
Look for numeric binary conditions (e.g.
Definition: GeneralCut.h:301
void print() const
Print cut tree.
Definition: GeneralCut.h:144
const Var * m_var
set if there was a valid variable in this cut
Definition: GeneralCut.h:414
AVariableManager::Object Object
Object, that can be checked. This depends on the VariableManager, as the returned variables from the ...
Definition: GeneralCut.h:93
std::string decompile() const
Do the compilation from a string in return.
Definition: GeneralCut.h:173
double m_number
literal number contained in the cut
Definition: GeneralCut.h:415
bool check(const Object *p) const
Check if the current cuts are passed by the given object.
Definition: GeneralCut.h:113
void processVariable(const std::string &str)
Get a variable with the given name from the variable manager using its getVariable(name) function.
Definition: GeneralCut.h:375
GeneralCut & operator=(const GeneralCut &)=delete
Delete assign operator.
bool m_isNumeric
if there was a literal number in this cut
Definition: GeneralCut.h:416
bool almostEqualFloat(const float &a, const float &b)
Helper function to test if two floats are almost equal.
Definition: GeneralCut.cc:17
unsigned long int findIgnoringParenthesis(std::string str, std::string pattern, unsigned int begin=0)
Returns the position of a pattern in a string ignoring everything that is in parenthesis.
Definition: GeneralCut.cc:47
unsigned long int findMatchedParenthesis(std::string str, char open='[', char close=']')
Returns position of the matched closing parenthesis if the first character in the given string contai...
Definition: GeneralCut.cc:31
std::vector< std::string > splitOnDelimiterAndConserveParenthesis(std::string str, char delimiter, char open, char close)
Split into std::vector on delimiter ignoring delimiters between parenthesis.
Definition: GeneralCut.cc:79
bool almostEqualDouble(const double &a, const double &b)
Helper function to test if two doubles are almost equal.
Definition: GeneralCut.cc:24
Abstract base class for different kinds of events.