File indexing completed on 2026-04-17 08:35:35
0001 #include <VecGeom/surfaces/conv/LogicHelper.h>
0002 #include <VecGeom/surfaces/Model.h>
0003 #include <VecGeom/volumes/BooleanVolume.h>
0004 #include <VecGeom/management/Logger.h>
0005
0006
0007
0008 namespace vgbrep {
0009 namespace logichelper {
0010
0011 bool string_to_logic(std::string s, LogicExpressionCPU &logic)
0012 {
0013
0014 logic.clear();
0015 s.erase(std::remove_if(s.begin(), s.end(), [](unsigned char x) { return std::isspace(x); }), s.end());
0016 std::string buff;
0017 logic_int next_operand = 0;
0018 logic_int next_operator = 0;
0019 size_t pos = 0;
0020 int num_lplus = 0;
0021 int num_lminus = 0;
0022 while (pos < s.length()) {
0023 next_operator = 0;
0024 switch (s[pos]) {
0025 case '(':
0026 next_operator = lplus;
0027 num_lplus++;
0028 break;
0029 case ')':
0030 next_operator = lminus;
0031 num_lminus++;
0032 break;
0033 case '|':
0034 next_operator = lor;
0035 break;
0036 case '&':
0037 next_operator = land;
0038 break;
0039 case '!':
0040 next_operator = lnot;
0041 break;
0042 default:
0043 buff += s[pos];
0044 };
0045 pos++;
0046 if (next_operator > 0 || pos == s.length()) {
0047
0048 if (buff.length() > 0) {
0049 try {
0050 next_operand = std::stoi(buff);
0051 } catch (std::invalid_argument const &ex) {
0052 std::cout << "std::invalid_argument::what(): " << ex.what() << " \"" << buff << "\"\n ";
0053 return false;
0054 }
0055 logic.push_back(next_operand);
0056 buff.clear();
0057 }
0058 if (next_operator > 0) logic.push_back(next_operator);
0059 }
0060 }
0061 if (num_lplus != num_lminus) {
0062 std::cout << "logic parantheses mismatch\n";
0063 return false;
0064 }
0065 return true;
0066 }
0067
0068 char logic_to_char(logic_int item)
0069 {
0070 switch (item) {
0071 case ltrue:
0072 return '1';
0073 case lfalse:
0074 return '0';
0075 case lplus:
0076 return '(';
0077 case lminus:
0078 return ')';
0079 case lor:
0080 return '|';
0081 case land:
0082 return '&';
0083 case lnot:
0084 return '!';
0085 };
0086 return '0' + item;
0087 }
0088
0089 void print_item(logic_int item)
0090 {
0091 if (LogicExpression::is_operator_token(item)) {
0092 switch (item) {
0093 case ltrue:
0094 printf("ltrue ");
0095 break;
0096 case lfalse:
0097 printf("lfalse ");
0098 break;
0099 case lplus:
0100 printf("( ");
0101 break;
0102 case lminus:
0103 printf(") ");
0104 break;
0105 case lor:
0106 printf("| ");
0107 break;
0108 case land:
0109 printf("& ");
0110 break;
0111 case lnot:
0112 printf("!");
0113 };
0114 } else {
0115 printf("%d ", item);
0116 }
0117 }
0118
0119 void print_logic(LogicExpressionCPU const &logic, int istart, int iend, bool jumps)
0120 {
0121 size_t ilast = (iend >= 0) ? size_t(iend) : logic.size() - 1;
0122 for (size_t i = istart; i <= ilast; ++i) {
0123 print_item(logic[i]);
0124 if (jumps && (logic[i] == land || logic[i] == lor)) i++;
0125 }
0126 printf("\n");
0127 }
0128
0129 void print_logic(LogicExpression const &logic, int istart, int iend, bool jumps)
0130 {
0131 size_t ilast = (iend >= 0) ? size_t(iend) : logic.size() - 1;
0132 for (size_t i = istart; i <= ilast; ++i) {
0133 print_item(logic[i]);
0134 if (jumps && (logic[i] == land || logic[i] == lor)) i++;
0135 }
0136 printf("\n");
0137 }
0138
0139 void remove_range(LogicExpressionCPU &logic, size_t istart, size_t iend)
0140 {
0141 for (size_t i = iend + 1; i < logic.size(); ++i)
0142 logic[i - iend + istart - 1] = logic[i];
0143 for (size_t i = 0; i < iend - istart + 1; ++i)
0144 logic.pop_back();
0145 }
0146
0147 void remove_parentheses(LogicExpressionCPU &logic, size_t start, size_t end)
0148 {
0149 VECGEOM_ASSERT(logic[start] == lplus && logic[end] == lminus);
0150 for (size_t i = start + 1; i < end; ++i)
0151 logic[i - 1] = logic[i];
0152 for (size_t i = end + 1; i < logic.size(); ++i)
0153 logic[i - 2] = logic[i];
0154 logic.pop_back();
0155 logic.pop_back();
0156 }
0157
0158 void insert_jumps(LogicExpressionCPU &logic)
0159 {
0160 size_t i = 0;
0161 while (i < logic.size()) {
0162 if (logic[i] == land || logic[i] == lor) {
0163
0164 logic.insert(logic.begin() + i + 1, 0);
0165 }
0166 i++;
0167 }
0168 int depth = 0;
0169 for (i = 0; i < logic.size(); ++i) {
0170 if (logic[i] == lplus)
0171 depth++;
0172 else if (logic[i] == lminus)
0173 depth--;
0174 else if (logic[i] == land || logic[i] == lor) {
0175 auto d = depth;
0176 auto j = i + 1;
0177 VECGEOM_VALIDATE(j + 1 < logic.size(), << "cannot end expression with an operator");
0178
0179 while (++j < logic.size()) {
0180 if (logic[j] == lplus) {
0181 d++;
0182 } else if (d == depth && (logic[j] == lminus || logic[j] == land || logic[j] == lor))
0183 break;
0184 else if (logic[j] == lminus)
0185 d--;
0186 }
0187 logic[++i] = j;
0188 }
0189 }
0190 }
0191
0192 bool is_negated(int isurf, LogicExpressionCPU &logic)
0193 {
0194
0195 for (size_t i = 0; i < logic.size(); ++i) {
0196 if (logic[i] == isurf) return false;
0197 if (logic[i] == lnot && logic[++i] == isurf) return true;
0198 }
0199
0200 return false;
0201 }
0202
0203 size_t find_matching_parenthesis(LogicExpressionCPU &logic, size_t start)
0204 {
0205
0206 VECGEOM_ASSERT(logic[start] == lplus);
0207 int ldepth = 0;
0208 for (size_t i = start; i < logic.size(); ++i) {
0209 switch (logic[i]) {
0210 case lminus:
0211 ldepth--;
0212 VECGEOM_ASSERT(ldepth >= 0);
0213 if (ldepth == 0) return i;
0214 break;
0215 case lplus:
0216 ldepth++;
0217 break;
0218 }
0219 }
0220 return logic.size();
0221 }
0222
0223 LogicExpressionConstruct::LogicExpressionConstruct(LogicExpressionCPU const &logic)
0224 try : fLogic(logic) {
0225
0226
0227 int num_operands = 0;
0228 size_t start_operand = 0;
0229 size_t end_operand = 0;
0230 int ldepth = 0;
0231
0232 auto add_operand = [&]() {
0233 LogicExpressionCPU current_operand;
0234 for (size_t i = start_operand; i <= end_operand; ++i)
0235 current_operand.push_back(fLogic[i]);
0236 num_operands++;
0237
0238 fOperands.push_back({current_operand});
0239 };
0240
0241 auto throw_error = [&](const char *what, size_t index) {
0242 Print();
0243 VECGEOM_LOG(critical) << what << " at index " << index;
0244 throw std::runtime_error(what);
0245 };
0246
0247
0248 logic_int previous = -1;
0249 for (size_t i = 0; i < fLogic.size(); ++i) {
0250 auto item = fLogic[i];
0251 bool is_operator = (item == land) || (item == lor);
0252 if (i == 0) {
0253 if (is_operator || item == lminus) throw_error("illegal first item", i);
0254 previous = item;
0255 continue;
0256 }
0257 bool last_operator = (previous == land) || (previous == lor);
0258 bool last_operand = !LogicExpression::is_operator_token(previous);
0259 if (i == fLogic.size() - 1) {
0260 if (is_operator || item == lnot || item == lplus) throw_error("illegal last item", i);
0261 }
0262
0263 switch (item) {
0264 case lminus:
0265
0266 if (previous == lplus || previous == lnot || last_operator) throw_error("illegal follow-up item ", i);
0267 break;
0268 case land:
0269 case lor:
0270
0271 if (previous == lplus || previous == lnot || last_operator) throw_error("illegal follow-up item ", i);
0272 break;
0273 default:
0274
0275 if (previous == lminus || last_operand) throw_error("illegal follow-up item ", i);
0276 }
0277 previous = item;
0278 }
0279
0280
0281 for (auto item : fLogic) {
0282 if (item == land || item == lor) {
0283 fComplexity++;
0284 }
0285 }
0286 if (fComplexity == 0) fHasScope = false;
0287
0288 #ifdef SURF_DEBUG_LOGIC
0289 std::cout << "constructing logic with complexity " << fComplexity << ": ";
0290 print_logic(fLogic, 0, -1, false);
0291 #endif
0292
0293
0294 int noperands = 1;
0295 bool simplified = true;
0296 while (simplified && noperands == 1) {
0297 simplified = false;
0298 for (size_t i = 0; i < fLogic.size(); ++i) {
0299 switch (fLogic[i]) {
0300 case lminus:
0301 ldepth--;
0302 break;
0303 case lplus:
0304 ldepth++;
0305 break;
0306 case land:
0307 case lor:
0308 if (ldepth == 0) noperands++;
0309 }
0310 }
0311
0312 if (noperands == 1) {
0313
0314
0315 simplified = false;
0316 while (fLogic[0] == lnot) {
0317 fNegated = !fNegated;
0318 simplified = true;
0319 remove_range(fLogic, 0, 0);
0320 }
0321
0322 while (*fLogic.begin() == lplus && find_matching_parenthesis(fLogic, 0) == fLogic.size() - 1) {
0323 remove_parentheses(fLogic, 0, fLogic.size() - 1);
0324 simplified = true;
0325 fHasScope = true;
0326 }
0327 if (simplified) {
0328 #ifdef SURF_DEBUG_LOGIC
0329 printf("simplified operand: ");
0330 print_logic(fLogic, 0, -1, false);
0331 #endif
0332 }
0333 }
0334 }
0335
0336
0337 for (size_t i = 0; i < fLogic.size(); ++i) {
0338
0339 switch (fLogic[i]) {
0340 case lminus:
0341 ldepth--;
0342 if (ldepth < 0) {
0343 VECGEOM_LOG(critical) << "LogicExpressionConstruct: parentheses mismatch at index " << i;
0344 throw std::runtime_error("parentheses mismatch");
0345 }
0346 break;
0347 case lplus:
0348 ldepth++;
0349 break;
0350 case land:
0351 case lor:
0352 if (ldepth == 0) {
0353 fOperators.push_back(fLogic[i]);
0354 if (i > start_operand) {
0355 end_operand = i - 1;
0356 add_operand();
0357 start_operand = i + 1;
0358 }
0359 }
0360 break;
0361 };
0362 }
0363 if (ldepth != 0) {
0364 throw std::runtime_error("parentheses mismatch");
0365 }
0366 if (fComplexity > 0 && start_operand < fLogic.size()) {
0367 end_operand = fLogic.size() - 1;
0368 add_operand();
0369 }
0370 } catch (const std::exception &e) {
0371 VECGEOM_LOG(critical) << "exception " << e.what();
0372 }
0373
0374 LogicExpressionConstruct &LogicExpressionConstruct::operator&=(LogicExpressionConstruct const &other)
0375 {
0376 Concatenate(other, land);
0377 return *this;
0378 }
0379
0380 LogicExpressionConstruct &LogicExpressionConstruct::operator|=(LogicExpressionConstruct const &other)
0381 {
0382 Concatenate(other, lor);
0383 return *this;
0384 }
0385
0386 void LogicExpressionConstruct::Concatenate(LogicExpressionConstruct const &other, logic_int op_concat)
0387 {
0388
0389 #ifdef SURF_DEBUG_LOGIC
0390 printf("Concatenating : ");
0391 Print();
0392 printf(" with : ");
0393 other.Print();
0394 #endif
0395 if (!fOperands.size()) {
0396 if (fNegated) {
0397 fLogic.insert(fLogic.begin(), lnot);
0398 fNegated = false;
0399 }
0400 fOperands.push_back({fLogic});
0401 }
0402 fOperands.push_back(other);
0403 fOperators.push_back(op_concat);
0404 fComplexity++;
0405 #ifdef SURF_DEBUG_LOGIC
0406 printf(" result : ");
0407 Print();
0408 #endif
0409 }
0410
0411 void LogicExpressionConstruct::PushNegation(bool neg)
0412 {
0413 fNegated ^= neg;
0414 if (!fComplexity) {
0415 if (fNegated) fLogic.insert(fLogic.begin(), lnot);
0416 fNegated = false;
0417 }
0418 for (auto &operand : fOperands)
0419 operand.PushNegation(fNegated);
0420 for (size_t i = 0; i < fOperators.size(); ++i) {
0421 if (fNegated) {
0422 if (fOperators[i] == land)
0423 fOperators[i] = lor;
0424 else if (fOperators[i] == lor)
0425 fOperators[i] = land;
0426 }
0427 }
0428 fNegated = false;
0429 }
0430
0431 bool LogicExpressionConstruct::RemoveScope(logic_int logic_op)
0432 {
0433
0434 bool removed_scope = false;
0435 if (fHasScope) {
0436 #ifdef SURF_DEBUG_LOGIC
0437 printf("removing scope for operation ");
0438 print_item(logic_op);
0439 printf("in operand: ");
0440 Print();
0441 #endif
0442 removed_scope = true;
0443 for (auto op : fOperators) {
0444 if (op != logic_op) {
0445 removed_scope = false;
0446 break;
0447 }
0448 }
0449 if (removed_scope) fHasScope = false;
0450 #ifdef SURF_DEBUG_LOGIC
0451 printf("after scope removal: ");
0452 Print();
0453 #endif
0454 }
0455 RemoveChildrenScopes();
0456 return removed_scope;
0457 }
0458
0459 void LogicExpressionConstruct::RemoveChildrenScopes(bool top)
0460 {
0461
0462 #ifdef SURF_DEBUG_LOGIC
0463 printf("removing children scopes for: ");
0464 Print();
0465 #endif
0466 if (top) fHasScope = false;
0467 if (fOperators.size() == 0) return;
0468
0469 for (size_t i = 0; i < fOperands.size(); ++i) {
0470 size_t sjump = 0;
0471 auto &operand = fOperands[i];
0472 logic_int op_before = (i > 0) ? fOperators[i - 1] : fOperators[i];
0473 logic_int op_after = (i < fOperands.size() - 1) ? fOperators[i] : fOperators[i - 1];
0474 bool match_op = op_before == op_after;
0475 if (match_op) {
0476 bool removed_scope = operand.RemoveScope(op_before);
0477 if (removed_scope) {
0478
0479 fComplexity += operand.fComplexity;
0480 sjump = 0;
0481 if (operand.fComplexity > 0) {
0482 sjump = operand.fOperands.size() - 1;
0483 fOperators.insert(fOperators.begin() + i, operand.fOperators.begin(), operand.fOperators.end());
0484 fOperands.insert(fOperands.begin() + i + 1, operand.fOperands.begin(), operand.fOperands.end());
0485 fOperands.erase(fOperands.begin() + i);
0486 }
0487 }
0488 } else {
0489 operand.RemoveChildrenScopes();
0490 }
0491 #ifdef SURF_DEBUG_LOGIC
0492 printf(" after operand %ld : ", i);
0493 Print();
0494 #endif
0495 i += sjump;
0496 }
0497 #ifdef SURF_DEBUG_LOGIC
0498 printf("after removing children scopes: ");
0499 Print();
0500 #endif
0501 }
0502
0503 void LogicExpressionConstruct::Print() const
0504 {
0505 LogicExpressionCPU logic;
0506 GetLogicExpression(logic);
0507 print_logic(logic, 0, -1, false);
0508 }
0509
0510 void LogicExpressionConstruct::GivePrecedenceToAnd()
0511 {
0512
0513 bool has_and = std::find(fOperators.begin(), fOperators.end(), land) != fOperators.end();
0514 bool has_or = std::find(fOperators.begin(), fOperators.end(), lor) != fOperators.end();
0515 if (!has_and || !has_or) return;
0516
0517 while (fOperators[0] == lor) {
0518
0519 std::rotate(fOperators.begin(), fOperators.begin() + 1, fOperators.end());
0520 std::rotate(fOperands.begin(), fOperands.begin() + 1, fOperands.end());
0521 }
0522
0523
0524
0525 size_t ior = std::distance(fOperators.begin(), std::find(fOperators.begin(), fOperators.end(), lor));
0526 size_t iand = std::distance(fOperators.begin(), std::find(fOperators.begin() + ior + 1, fOperators.end(), land));
0527 while (iand < fOperators.size()) {
0528 size_t iand_last = iand;
0529 for (size_t i = iand + 1; i < fOperators.size() && fOperators[i] == land; ++i)
0530 iand_last = i;
0531
0532 for (size_t i = iand; i <= iand_last; ++i) {
0533 fOperands[iand] &= fOperands[i + 1];
0534 }
0535 VECGEOM_ASSERT(
0536 !fOperands[iand].fNegated);
0537 fOperands[iand].fHasScope = true;
0538 fOperands.erase(fOperands.begin() + iand + 1, fOperands.begin() + iand_last + 2);
0539 fOperators.erase(fOperators.begin() + iand, fOperators.begin() + iand_last + 1);
0540 ior = std::distance(fOperators.begin(), std::find(fOperators.begin() + iand + 1, fOperators.end(), lor));
0541 iand = std::distance(fOperators.begin(), std::find(fOperators.begin() + ior + 1, fOperators.end(), land));
0542 }
0543 }
0544
0545 void LogicExpressionConstruct::SwapByComplexity()
0546 {
0547
0548 if (fComplexity == 0) return;
0549 #ifdef SURF_DEBUG_LOGIC
0550 printf("SwapByComplexity start: ");
0551 Print();
0552 #endif
0553 size_t istart_op = 0;
0554 size_t iend_op = 0;
0555 auto op = fOperators[istart_op];
0556 while (istart_op < fOperators.size()) {
0557 for (size_t i = istart_op + 1; i < fOperators.size(); ++i) {
0558 if (fOperators[i] == op)
0559 iend_op = i;
0560 else
0561 break;
0562 }
0563 size_t istart = istart_op;
0564 size_t iend = (op == land) ? iend_op + 1 : iend_op;
0565 iend = std::min(iend, fOperands.size() - 1);
0566 if (iend > istart) {
0567
0568 #ifdef SURF_DEBUG_LOGIC
0569 printf("sorting operands [%ld, %ld]\n", istart, iend);
0570 #endif
0571 for (size_t iop = istart; iop < iend; ++iop) {
0572 for (size_t jop = iop + 1; jop <= iend; ++jop) {
0573 auto c1 = fOperands[iop].fComplexity;
0574 auto c2 = fOperands[jop].fComplexity;
0575 if (c1 > c2) {
0576 std::iter_swap(fOperands.begin() + iop, fOperands.begin() + jop);
0577 #ifdef SURF_DEBUG_LOGIC
0578 printf("after swapping %ld (compl=%d) <-> %ld (compl=%d): ", iop, c1, jop, c2);
0579 Print();
0580 #endif
0581 }
0582 }
0583 }
0584 }
0585 istart_op = iend_op + 1;
0586 if (istart_op < fOperators.size()) {
0587 op = fOperators[istart_op];
0588 if (op == lor) {
0589 istart_op++;
0590 if (istart_op < fOperators.size()) op = fOperators[istart_op];
0591 }
0592 }
0593 iend_op = istart_op;
0594 }
0595
0596 for (auto &operand : fOperands)
0597 operand.SwapByComplexity();
0598 }
0599
0600 void LogicExpressionConstruct::GetLogicExpression(LogicExpressionCPU &logic) const
0601 {
0602 if (fNegated) logic.push_back(lnot);
0603 if (fHasScope) logic.push_back(lplus);
0604 if (fComplexity == 0) {
0605 for (auto &item : fLogic)
0606 logic.push_back(item);
0607 } else {
0608 for (size_t i = 0; i < fOperands.size(); ++i) {
0609 fOperands[i].GetLogicExpression(logic);
0610 if (i < fOperators.size()) logic.push_back(fOperators[i]);
0611 }
0612 }
0613 if (fHasScope) logic.push_back(lminus);
0614 }
0615
0616 int LogicExpressionConstruct::GetNumOperands() const
0617 {
0618 int noperands = (fOperands.size() == 0) ? 1 : 0;
0619 for (auto const &operand : fOperands)
0620 noperands += operand.GetNumOperands();
0621 return noperands;
0622 }
0623
0624 void LogicExpressionConstruct::Simplify(LogicExpressionCPU &logic)
0625 {
0626 PushNegation();
0627 RemoveChildrenScopes(true);
0628 GivePrecedenceToAnd();
0629 SwapByComplexity();
0630 logic.clear();
0631 GetLogicExpression(logic);
0632 }
0633
0634 int max_operand(LogicExpressionCPU const &logic)
0635 {
0636 int max_op = -1;
0637 for (auto const &item : logic) {
0638 if (LogicExpression::is_operator_token(item)) continue;
0639 max_op = std::max(max_op, int(item));
0640 }
0641 return max_op;
0642 }
0643
0644 void substitute_logic(LogicExpressionCPU const &logic, LogicExpressionCPU &substutute_logic, const bool *values)
0645 {
0646 substutute_logic.clear();
0647 for (auto item : logic) {
0648 if (LogicExpression::is_operator_token(item))
0649 substutute_logic.push_back(item);
0650 else
0651 substutute_logic.push_back(logic_int(values[item]));
0652 }
0653 }
0654
0655 bool evaluate_logic(LogicExpressionCPU const &logic, const bool *values, int &num_eval)
0656 {
0657 auto test_bit = [](unsigned bset, int bit) { return (bset & (unsigned(1) << bit)) > 0; };
0658 auto set_bit = [](unsigned &bset, int bit) { bset |= unsigned(1) << bit; };
0659 auto reset_bit = [](unsigned &bset, int bit) { bset &= ~(unsigned(1) << bit); };
0660 auto swap_bit = [](unsigned &bset, int bit) { bset ^= unsigned(1) << bit; };
0661 unsigned stack = 0;
0662 unsigned negate = 0;
0663 int depth = 0;
0664 num_eval = 0;
0665 bool result = false;
0666 unsigned i;
0667 for (i = 0; i < logic.size(); ++i) {
0668 switch (logic[i]) {
0669 case lplus:
0670 depth++;
0671 break;
0672 case lminus:
0673 result = test_bit(stack, depth);
0674 reset_bit(negate, depth--);
0675 result ^= test_bit(negate, depth);
0676 if (result)
0677 set_bit(stack, depth);
0678 else
0679 reset_bit(stack, depth);
0680 break;
0681 case lnot:
0682 swap_bit(negate, depth);
0683 break;
0684 case lor:
0685 if (test_bit(stack, depth))
0686 i = logic[i + 1] - 1;
0687 else
0688 i++;
0689 break;
0690 case land:
0691 if (!test_bit(stack, depth))
0692 i = logic[i + 1] - 1;
0693 else
0694 i++;
0695 break;
0696 default:
0697
0698 result = values[int(logic[i])];
0699 result ^= test_bit(negate, depth);
0700 reset_bit(negate, depth);
0701 num_eval++;
0702
0703 if (result)
0704 set_bit(stack, depth);
0705 else
0706 reset_bit(stack, depth);
0707 }
0708 }
0709 VECGEOM_ASSERT(depth == 0);
0710 return (stack & 1) > 0;
0711 }
0712
0713 }
0714 }