HAL  v4.5.0-257-ge24279b4e
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
boolean_function.cpp
Go to the documentation of this file.
2 
3 #include <unordered_set>
4 
10 
11 #include <algorithm>
12 #include <bitset>
13 #include <boost/spirit/home/x3.hpp>
14 #include <chrono>
15 #include <map>
16 #include <iomanip>
17 
18 namespace hal
19 {
20  template<>
21  std::map<BooleanFunction::Value, std::string> EnumStrings<BooleanFunction::Value>::data = {{BooleanFunction::Value::ZERO, "0"},
22  {BooleanFunction::Value::ONE, "1"},
23  {BooleanFunction::Value::X, "X"},
24  {BooleanFunction::Value::Z, "Z"}};
25 
27  {
28  switch (v)
29  {
30  case ZERO:
31  return std::string("0");
32  case ONE:
33  return std::string("1");
34  case X:
35  return std::string("X");
36  case Z:
37  return std::string("Z");
38  }
39 
40  return std::string("X");
41  }
42 
43  namespace
44  {
45  static std::vector<char> char_map = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
46  // namespace
47 
48  static Result<std::string> to_bin(const std::vector<BooleanFunction::Value>& value)
49  {
50  if (value.size() == 0)
51  {
52  return ERR("could not convert bit-vector to binary string: bit-vector is empty");
53  }
54 
55  std::string res = "";
56  res.reserve(value.size());
57 
58  for (auto v : value)
59  {
60  res += enum_to_string<BooleanFunction::Value>(v);
61  }
62 
63  return OK(res);
64  }
65 
66  static Result<std::string> to_oct(const std::vector<BooleanFunction::Value>& value)
67  {
68  int bitsize = value.size();
69  if (bitsize == 0)
70  {
71  return ERR("could not convert bit-vector to octal string: bit-vector is empty");
72  }
73 
74  u8 first_bits = bitsize % 3;
75 
76  u8 index = 0;
77  u8 mask = 0;
78 
79  u8 v1, v2, v3;
80 
81  // result string prep
82  std::string res = "";
83  res.reserve((bitsize + 2) / 3);
84 
85  // deal with 1 or 2 leading bits
86  for (u8 i = 0; i < first_bits; i++)
87  {
88  v1 = value.at(i);
89  index = (index << 1) | v1;
90  mask |= v1;
91  }
92 
93  if (first_bits)
94  {
95  if ((mask & 0x80) > 0) // mask "sign" bit set if 'X' or 'Z' among first bits in vector
96  res += 'X';
97  else
98  res += (char_map[index]);
99  }
100 
101  // deal with 3-bit blocks (left to right)
102  for (int i = bitsize % 3; i < bitsize; i += 3)
103  {
104  v1 = value[i];
105  v2 = value[i + 1];
106  v3 = value[i + 2];
107 
108  index = (v1 << 2) | (v2 << 1) | v3; // cannot exceed char_map range as index always < 16, no further check required
109  mask = (v1 | v2 | v3);
110 
111  if ((mask & 0x80) > 0) // mask "sign" bit set if 'X' or 'Z' among tested 3 bits
112  res += 'X';
113  else
114  res += (char_map[index]);
115  }
116  return OK(res);
117  }
118 
119  static Result<std::string> to_dec(const std::vector<BooleanFunction::Value>& value)
120  {
121  int bitsize = value.size();
122  if (bitsize == 0)
123  {
124  return ERR("could not convert bit-vector to decimal string: bit-vector is empty");
125  }
126 
127  if (bitsize > 64)
128  {
129  return ERR("could not convert bit-vector to decimal string: bit-vector has length " + std::to_string(bitsize) + ", but only up to 64 bits are supported for decimal conversion");
130  }
131 
132  u64 tmp = 0;
133  u8 x_flag = 0;
134 
135  for (auto it = value.rbegin(); it != value.rend(); it++)
136  {
137  x_flag |= *it >> 1;
138  tmp <<= 1;
139  tmp |= *it;
140  }
141 
142  if (x_flag)
143  {
144  return OK(std::string("X"));
145  }
146  return OK(std::to_string(tmp));
147  }
148 
149  static Result<std::string> to_hex(const std::vector<BooleanFunction::Value>& value)
150  {
151  int bitsize = value.size();
152  if (bitsize == 0)
153  {
154  return ERR("could not convert bit-vector to hexadecimal string: bit-vector is empty");
155  }
156 
157  u8 first_bits = bitsize & 0x3;
158 
159  u8 index = 0;
160  u8 mask = 0;
161 
162  u8 v1, v2, v3, v4;
163 
164  // result string prep
165  std::string res = "";
166  res.reserve((bitsize + 3) / 4);
167 
168  // deal with 1-3 leading bits
169  for (u8 i = 0; i < first_bits; i++)
170  {
171  v1 = value.at(i);
172  index = (index << 1) | v1;
173  mask |= v1;
174  }
175 
176  if (first_bits)
177  {
178  if ((mask & 0x80) > 0) // mask "sign" bit set if 'X' or 'Z' among first bits in vector
179  res += 'X';
180  else
181  res += (char_map[index]);
182  }
183 
184  // deal with 4-bit blocks (left to right)
185  for (int i = bitsize & 0x3; i < bitsize; i += 4)
186  {
187  v1 = value[i];
188  v2 = value[i + 1];
189  v3 = value[i + 2];
190  v4 = value[i + 3];
191 
192  index = ((v1 << 3) | (v2 << 2) | (v3 << 1) | v4) & 0xF;
193  mask = (v1 | v2 | v3 | v4);
194 
195  if ((mask & 0x80) > 0) // mask "sign" bit set if 'X' or 'Z' among tested 4 bits
196  res += 'X';
197  else
198  res += (char_map[index]);
199  }
200 
201  return OK(res);
202  }
203  } // namespace
204 
205  Result<std::string> BooleanFunction::to_string(const std::vector<BooleanFunction::Value>& value, u8 base)
206  {
207  switch (base)
208  {
209  case 2:
210  return to_bin(value);
211  case 8:
212  return to_oct(value);
213  case 10:
214  return to_dec(value);
215  case 16:
216  return to_hex(value);
217  default:
218  return ERR("could not convert bit-vector to string: invalid value '" + std::to_string(base) + "' given for base");
219  }
220  }
221 
222  Result<u64> BooleanFunction::to_u64(const std::vector<BooleanFunction::Value>& value)
223  {
224  if (value.size() > 64)
225  {
226  return ERR("cannot translate vector of values to u64 numeral: can only support vectors up to 64 bits and got vector of size " + std::to_string(value.size()) + ".");
227  }
228 
229  u64 val = 0;
230  for (auto it = value.rbegin(); it != value.rend(); it++)
231  {
232  if ((*it != BooleanFunction::Value::ZERO) && (*it != BooleanFunction::Value::ONE))
233  {
234  return ERR("cannot translate vector of values to u64 numeral: found value other than ZERO or ONE: " + BooleanFunction::to_string(*it) + ".");
235  }
236 
237  val <<= 1;
238  val |= *it;
239  }
240 
241  return OK(val);
242  }
243 
244  std::ostream& operator<<(std::ostream& os, BooleanFunction::Value v)
245  {
246  return os << BooleanFunction::to_string(v);
247  }
248 
250  {
251  }
252 
253  Result<BooleanFunction> BooleanFunction::build(std::vector<BooleanFunction::Node>&& nodes)
254  {
255  if (auto res = BooleanFunction::validate(BooleanFunction(std::move(nodes))); res.is_error())
256  {
257  return ERR_APPEND(res.get_error(), "could not build Boolean function from vector of nodes: failed to validate Boolean function");
258  }
259  else
260  {
261  return res;
262  }
263  }
264 
266  {
268  }
269 
271  {
272  return BooleanFunction(Node::Constant({value}));
273  }
274 
275  BooleanFunction BooleanFunction::Const(const std::vector<BooleanFunction::Value>& values)
276  {
277  return BooleanFunction(Node::Constant(values));
278  }
279 
281  {
282  auto values = std::vector<BooleanFunction::Value>();
283  values.reserve(size);
284  for (auto i = 0; i < size; i++)
285  {
286  values.emplace_back(((value >> i) & 1) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO);
287  }
288 
289  return BooleanFunction::Const(values);
290  }
291 
293  {
295  }
296 
298  {
299  if ((p0.size() != p1.size()) || (p0.size() != size))
300  {
301  return ERR("could not join Boolean functions using AND operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
302  + ", size = " + std::to_string(size) + ")");
303  }
304 
305  return OK(BooleanFunction(Node::Operation(NodeType::And, size), std::move(p0), std::move(p1)));
306  }
307 
309  {
310  if ((p0.size() != p1.size()) || (p0.size() != size))
311  {
312  return ERR("could not join Boolean functions using OR operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
313  + ", size = " + std::to_string(size) + ").");
314  }
315 
316  return OK(BooleanFunction(Node::Operation(NodeType::Or, size), std::move(p0), std::move(p1)));
317  }
318 
320  {
321  if (p0.size() != size)
322  {
323  return ERR("could not invert Boolean function using NOT operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", size = " + std::to_string(size) + ").");
324  }
325 
326  return OK(BooleanFunction(Node::Operation(NodeType::Not, size), std::move(p0)));
327  }
328 
330  {
331  if ((p0.size() != p1.size()) || (p0.size() != size))
332  {
333  return ERR("could not join Boolean functions using XOR operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
334  + ", size = " + std::to_string(size) + ").");
335  }
336 
337  return OK(BooleanFunction(Node::Operation(NodeType::Xor, size), std::move(p0), std::move(p1)));
338  }
339 
341  {
342  if ((p0.size() != p1.size()) || (p0.size() != size))
343  {
344  return ERR("could not join Boolean functions using ADD operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
345  + ", size = " + std::to_string(size) + ").");
346  }
347 
348  return OK(BooleanFunction(Node::Operation(NodeType::Add, size), std::move(p0), std::move(p1)));
349  }
350 
352  {
353  if ((p0.size() != p1.size()) || (p0.size() != size))
354  {
355  return ERR("could not join Boolean functions using SUB operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
356  + ", size = " + std::to_string(size) + ").");
357  }
358 
359  return OK(BooleanFunction(Node::Operation(NodeType::Sub, size), std::move(p0), std::move(p1)));
360  }
361 
363  {
364  if ((p0.size() != p1.size()) || (p0.size() != size))
365  {
366  return ERR("could not join Boolean functions using MUL operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
367  + ", size = " + std::to_string(size) + ").");
368  }
369 
370  return OK(BooleanFunction(Node::Operation(NodeType::Mul, size), std::move(p0), std::move(p1)));
371  }
372 
374  {
375  if ((p0.size() != p1.size()) || (p0.size() != size))
376  {
377  return ERR("could not join Boolean functions using SDIV operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
378  + ", size = " + std::to_string(size) + ").");
379  }
380 
381  return OK(BooleanFunction(Node::Operation(NodeType::Sdiv, size), std::move(p0), std::move(p1)));
382  }
383 
385  {
386  if ((p0.size() != p1.size()) || (p0.size() != size))
387  {
388  return ERR("could not join Boolean functions using UDIV operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
389  + ", size = " + std::to_string(size) + ").");
390  }
391 
392  return OK(BooleanFunction(Node::Operation(NodeType::Udiv, size), std::move(p0), std::move(p1)));
393  }
394 
396  {
397  if ((p0.size() != p1.size()) || (p0.size() != size))
398  {
399  return ERR("could not join Boolean functions using SREM operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
400  + ", size = " + std::to_string(size) + ").");
401  }
402 
403  return OK(BooleanFunction(Node::Operation(NodeType::Srem, size), std::move(p0), std::move(p1)));
404  }
405 
407  {
408  if ((p0.size() != p1.size()) || (p0.size() != size))
409  {
410  return ERR("could not join Boolean functions using UREM operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
411  + ", size = " + std::to_string(size) + ").");
412  }
413 
414  return OK(BooleanFunction(Node::Operation(NodeType::Urem, size), std::move(p0), std::move(p1)));
415  }
416 
418  {
419  if (!p1.is_index() || !p2.is_index())
420  {
421  return ERR("could not apply slice operation: function types do not match (p1 and p2 must be of type 'BooleanFunction::Index')");
422  }
423  if ((p0.size() != p1.size()) || (p1.size() != p2.size()))
424  {
425  return ERR("could not apply slice operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size()) + ", p2 = " + std::to_string(p2.size())
426  + " - sizes must be equal)");
427  }
428 
429  auto start = p1.get_index_value().get();
430  auto end = p2.get_index_value().get();
431  if ((start > end) || (start >= p0.size()) || (end >= p0.size()) || (end - start + 1) != size)
432  {
433  return ERR("could not apply SLICE operation: bit indices are not valid, p1 must be larger or equal than p1 and smaller than p0 (p0 = " + std::to_string(p0.size())
434  + ", p1 = " + std::to_string(start) + ", p2 = " + std::to_string(end) + ")");
435  }
436 
437  return OK(BooleanFunction(Node::Operation(NodeType::Slice, size), std::move(p0), std::move(p1), std::move(p2)));
438  }
439 
441  {
442  if ((p0.size() + p1.size()) != size)
443  {
444  return ERR("could not apply CONCAT operation: function input widths do not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
445  + "-bit, size = " + std::to_string(size) + ").");
446  }
447 
448  return OK(BooleanFunction(Node::Operation(NodeType::Concat, size), std::move(p0), std::move(p1)));
449  }
450 
452  {
453  if (p0.size() > size || p1.size() != size)
454  {
455  return ERR("could not apply ZEXT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
456  + "-bit, size = " + std::to_string(size) + ").");
457  }
458 
459  if (!p1.has_index_value(size))
460  {
461  return ERR("could not apply ZEXT operation: p1 does not encode size (p1 = " + p1.to_string() + ", size = " + std::to_string(size) + ").");
462  }
463 
464  return OK(BooleanFunction(Node::Operation(NodeType::Zext, size), std::move(p0), std::move(p1)));
465  }
466 
468  {
469  if (p0.size() > size || p1.size() != size)
470  {
471  return ERR("could not apply SEXT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
472  + "-bit, size = " + std::to_string(size) + ").");
473  }
474 
475  if (!p1.has_index_value(size))
476  {
477  return ERR("could not apply SEXT operation: p1 does not encode size (p1 = " + p1.to_string() + ", size = " + std::to_string(size) + ").");
478  }
479 
480  return OK(BooleanFunction(Node::Operation(NodeType::Sext, size), std::move(p0), std::move(p1)));
481  }
482 
484  {
485  if (p0.size() != size || p1.size() != size)
486  {
487  return ERR("could not apply SHL operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
488  + "-bit, size = " + std::to_string(size) + ").");
489  }
490 
491  if (!p1.is_index())
492  {
493  return ERR("could not apply SHL operation: p1 is not an index.");
494  }
495 
496  return OK(BooleanFunction(Node::Operation(NodeType::Shl, size), std::move(p0), std::move(p1)));
497  }
498 
500  {
501  if (p0.size() != size || p1.size() != size)
502  {
503  return ERR("could not apply LSHR operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
504  + "-bit, size = " + std::to_string(size) + ").");
505  }
506 
507  if (!p1.is_index())
508  {
509  return ERR("could not apply LSHR operation: p1 is not an index.");
510  }
511 
512  return OK(BooleanFunction(Node::Operation(NodeType::Lshr, size), std::move(p0), std::move(p1)));
513  }
514 
516  {
517  if (p0.size() != size || p1.size() != size)
518  {
519  return ERR("could not apply ASHR operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
520  + "-bit, size = " + std::to_string(size) + ").");
521  }
522 
523  if (!p1.is_index())
524  {
525  return ERR("could not apply ASHR operation: p1 is not an index.");
526  }
527 
528  return OK(BooleanFunction(Node::Operation(NodeType::Ashr, size), std::move(p0), std::move(p1)));
529  }
530 
532  {
533  if (p0.size() != size || p1.size() != size)
534  {
535  return ERR("could not apply ROL operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
536  + "-bit, size = " + std::to_string(size) + ").");
537  }
538 
539  if (!p1.is_index())
540  {
541  return ERR("could not apply ROL operation: p1 is not an index.");
542  }
543 
544  return OK(BooleanFunction(Node::Operation(NodeType::Rol, size), std::move(p0), std::move(p1)));
545  }
546 
548  {
549  if (p0.size() != size || p1.size() != size)
550  {
551  return ERR("could not apply ROR operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
552  + "-bit, size = " + std::to_string(size) + ").");
553  }
554 
555  if (!p1.is_index())
556  {
557  return ERR("could not apply ROR operation: p1 is not an index.");
558  }
559 
560  return OK(BooleanFunction(Node::Operation(NodeType::Ror, size), std::move(p0), std::move(p1)));
561  }
562 
564  {
565  if (p0.size() != p1.size() || size != 1)
566  {
567  return ERR("could not apply EQ operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
568  + "-bit, size = " + std::to_string(size) + ").");
569  }
570 
571  return OK(BooleanFunction(Node::Operation(NodeType::Eq, size), std::move(p0), std::move(p1)));
572  }
573 
575  {
576  if (p0.size() != p1.size() || size != 1)
577  {
578  return ERR("could not apply SLE operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
579  + "-bit, size = " + std::to_string(size) + ").");
580  }
581 
582  return OK(BooleanFunction(Node::Operation(NodeType::Sle, size), std::move(p0), std::move(p1)));
583  }
584 
586  {
587  if (p0.size() != p1.size() || size != 1)
588  {
589  return ERR("could not apply SLT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
590  + "-bit, size = " + std::to_string(size) + ").");
591  }
592 
593  return OK(BooleanFunction(Node::Operation(NodeType::Slt, size), std::move(p0), std::move(p1)));
594  }
595 
597  {
598  if (p0.size() != p1.size() || size != 1)
599  {
600  return ERR("could not apply ULE operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
601  + "-bit, size = " + std::to_string(size) + ").");
602  }
603 
604  return OK(BooleanFunction(Node::Operation(NodeType::Ule, size), std::move(p0), std::move(p1)));
605  }
606 
608  {
609  if (p0.size() != p1.size() || size != 1)
610  {
611  return ERR("could not apply ULT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
612  + "-bit, size = " + std::to_string(size) + ").");
613  }
614 
615  return OK(BooleanFunction(Node::Operation(NodeType::Ult, size), std::move(p0), std::move(p1)));
616  }
617 
619  {
620  if (p0.size() != 1 || p1.size() != size || p2.size() != size)
621  {
622  return ERR("could not apply ITE operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
623  + "-bit, p2 = " + std::to_string(p2.size()) + "-bit, size = " + std::to_string(size) + ").");
624  }
625 
626  return OK(BooleanFunction(Node::Operation(NodeType::Ite, size), std::move(p0), std::move(p1), std::move(p2)));
627  }
628 
629  std::ostream& operator<<(std::ostream& os, const BooleanFunction& f)
630  {
631  return os << f.to_string();
632  }
633 
635  {
636  return BooleanFunction::And(this->clone(), other.clone(), this->size()).get();
637  }
638 
640  {
641  *this = BooleanFunction::And(this->clone(), other.clone(), this->size()).get();
642  return *this;
643  }
644 
646  {
647  return BooleanFunction::Not(this->clone(), this->size()).get();
648  }
649 
651  {
652  return BooleanFunction::Or(this->clone(), other.clone(), this->size()).get();
653  }
654 
656  {
657  *this = BooleanFunction::Or(this->clone(), other.clone(), this->size()).get();
658  return *this;
659  }
660 
662  {
663  return BooleanFunction::Xor(this->clone(), other.clone(), this->size()).get();
664  }
665 
667  {
668  *this = BooleanFunction::Xor(this->clone(), other.clone(), this->size()).get();
669  return *this;
670  }
671 
673  {
674  return BooleanFunction::Add(this->clone(), other.clone(), this->size()).get();
675  }
676 
678  {
679  *this = BooleanFunction::Add(this->clone(), other.clone(), this->size()).get();
680  return *this;
681  }
682 
684  {
685  return BooleanFunction::Sub(this->clone(), other.clone(), this->size()).get();
686  }
687 
689  {
690  *this = BooleanFunction::Sub(this->clone(), other.clone(), this->size()).get();
691  return *this;
692  }
693 
695  {
696  return BooleanFunction::Mul(this->clone(), other.clone(), this->size()).get();
697  }
698 
700  {
701  *this = BooleanFunction::Mul(this->clone(), other.clone(), this->size()).get();
702  return *this;
703  }
704 
706  {
707  if (this->m_nodes.size() != other.m_nodes.size())
708  {
709  return false;
710  }
711 
712  for (auto i = 0ul; i < this->m_nodes.size(); i++)
713  {
714  if (this->m_nodes[i] != other.m_nodes[i])
715  {
716  return false;
717  }
718  }
719  return true;
720  }
721 
723  {
724  return !(*this == other);
725  }
726 
728  {
729  if (this->m_nodes.size() < other.m_nodes.size())
730  {
731  return true;
732  }
733  if (this->m_nodes.size() > other.m_nodes.size())
734  {
735  return false;
736  }
737 
738  // compare the nodes directly instead of their string representation, this operator is on the hot path
739  // of every symbolic state lookup and formatting two strings per comparison dominated the evaluation
740  return std::lexicographical_compare(this->m_nodes.begin(), this->m_nodes.end(), other.m_nodes.begin(), other.m_nodes.end());
741  }
742 
744  {
745  return this->m_nodes.empty();
746  }
747 
749  {
750  auto function = BooleanFunction();
751  function.m_nodes.reserve(this->m_nodes.size());
752 
753  for (const auto& node : this->m_nodes)
754  {
755  function.m_nodes.emplace_back(node);
756  }
757 
758  return function;
759  }
760 
762  {
763  return this->get_top_level_node().size;
764  }
765 
767  {
768  return (this->is_empty()) ? false : this->get_top_level_node().is(type);
769  }
770 
772  {
773  return (this->is_empty()) ? false : this->get_top_level_node().is_variable();
774  }
775 
776  bool BooleanFunction::has_variable_name(const std::string& variable_name) const
777  {
778  return (this->is_empty()) ? false : this->get_top_level_node().has_variable_name(variable_name);
779  }
780 
782  {
783  if (this->is_empty())
784  {
785  return ERR("Boolean function is empty");
786  }
787 
788  return this->get_top_level_node().get_variable_name();
789  }
790 
792  {
793  return (this->is_empty()) ? false : this->get_top_level_node().is_constant();
794  }
795 
796  bool BooleanFunction::has_constant_value(const std::vector<Value>& value) const
797  {
798  return (this->is_empty()) ? false : this->get_top_level_node().has_constant_value(value);
799  }
800 
802  {
803  return (this->is_empty()) ? false : this->get_top_level_node().has_constant_value(value);
804  }
805 
807  {
808  if (this->is_empty())
809  {
810  return ERR("Boolean function is empty");
811  }
812 
813  return this->get_top_level_node().get_constant_value();
814  }
815 
817  {
818  if (this->is_empty())
819  {
820  return ERR("Boolean function is empty");
821  }
822 
824  }
825 
827  {
828  return (this->is_empty()) ? false : this->get_top_level_node().is_index();
829  }
830 
832  {
833  return (this->is_empty()) ? false : this->get_top_level_node().has_index_value(value);
834  }
835 
837  {
838  if (this->is_empty())
839  {
840  return ERR("Boolean function is empty");
841  }
842 
843  return this->get_top_level_node().get_index_value();
844  }
845 
847  {
848  return this->m_nodes.back();
849  }
850 
852  {
853  return this->m_nodes.size();
854  }
855 
856  const std::vector<BooleanFunction::Node>& BooleanFunction::get_nodes() const
857  {
858  return this->m_nodes;
859  }
860 
861  std::vector<BooleanFunction> BooleanFunction::get_parameters() const
862  {
869 
870  auto coverage = this->compute_node_coverage();
871  switch (this->get_top_level_node().get_arity())
872  {
873  case 0: {
874  return {};
875  }
876  case 1: {
877  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.end() - 1}))};
878  }
879  case 2: {
880  auto index = this->length() - coverage[this->length() - 2] - 1;
881 
882  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.begin() + index})),
883  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index, this->m_nodes.end() - 1}))};
884  }
885  case 3: {
886  auto index0 = this->length() - coverage[this->length() - 3] - coverage[this->length() - 2] - 1;
887  auto index1 = this->length() - coverage[this->length() - 2] - 1;
888 
889  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.begin() + index0})),
890  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index0, this->m_nodes.begin() + index1})),
891  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index1, this->m_nodes.end() - 1}))};
892  }
893 
894  default:
895  assert(false && "not implemented reached.");
896  }
897 
898  return {};
899  }
900 
901  std::set<std::string> BooleanFunction::get_variable_names() const
902  {
903  auto variable_names = std::set<std::string>();
904  for (const auto& node : this->m_nodes)
905  {
906  if (node.is_variable())
907  {
908  variable_names.insert(node.variable);
909  }
910  }
911  return variable_names;
912  }
913 
914  Result<std::string> BooleanFunction::default_printer(const BooleanFunction::Node& node, std::vector<std::string>&& operands)
915  {
916  if (node.get_arity() != operands.size())
917  {
918  return ERR("could not print Boolean function: node arity of " + std::to_string(node.get_arity()) + " does not match number of operands of " + std::to_string(operands.size()));
919  }
920 
921  switch (node.type)
922  {
926  return OK(node.to_string());
927 
929  return OK("(" + operands[0] + " & " + operands[1] + ")");
931  return OK("(! " + operands[0] + ")");
933  return OK("(" + operands[0] + " | " + operands[1] + ")");
935  return OK("(" + operands[0] + " ^ " + operands[1] + ")");
936 
938  return OK("(" + operands[0] + " + " + operands[1] + ")");
940  return OK("(" + operands[0] + " - " + operands[1] + ")");
942  return OK("(" + operands[0] + " * " + operands[1] + ")");
944  return OK("(" + operands[0] + " /s " + operands[1] + ")");
946  return OK("(" + operands[0] + " / " + operands[1] + ")");
948  return OK("(" + operands[0] + " \%s " + operands[1] + ")");
950  return OK("(" + operands[0] + " \% " + operands[1] + ")");
951 
953  return OK("(" + operands[0] + " ++ " + operands[1] + ")");
955  return OK("Slice(" + operands[0] + ", " + operands[1] + ", " + operands[2] + ")");
957  return OK("Zext(" + operands[0] + ", " + operands[1] + ")");
959  return OK("Sext(" + operands[0] + ", " + operands[1] + ")");
960 
962  return OK("(" + operands[0] + " << " + operands[1] + ")");
964  return OK("(" + operands[0] + " >> " + operands[1] + ")");
966  return OK("(" + operands[0] + " >>a " + operands[1] + ")");
968  return OK("(" + operands[0] + " <<r " + operands[1] + ")");
970  return OK("(" + operands[0] + " >>r " + operands[1] + ")");
971 
973  return OK("(" + operands[0] + " == " + operands[1] + ")");
975  return OK("(" + operands[0] + " <s " + operands[1] + ")");
977  return OK("(" + operands[0] + " <=s " + operands[1] + ")");
979  return OK("(" + operands[0] + " < " + operands[1] + ")");
981  return OK("(" + operands[0] + " <= " + operands[1] + ")");
983  return OK("(" + operands[0] + " ? " + operands[1] + " : " + operands[2] + ")");
984 
985  default:
986  return ERR("could not print Boolean function: unsupported node type '" + std::to_string(node.type) + "'");
987  }
988  }
989 
990  Result<std::string> BooleanFunction::algebraic_printer(const BooleanFunction::Node& node, std::vector<std::string>&& operands)
991  {
992  if (node.get_arity() != operands.size())
993  {
994  return ERR("could not print Boolean function: node arity of " + std::to_string(node.get_arity()) + " does not match number of operands of " + std::to_string(operands.size()));
995  }
996 
997  switch (node.type)
998  {
1001  return OK(node.to_string());
1002 
1004  return OK("CONST" + std::string(node.has_constant_value(0) ? "0" : "1"));
1005 
1007  return OK("(" + operands[0] + "*" + operands[1] + ")");
1009  return OK("(! " + operands[0] + ")");
1011  return OK("(" + operands[0] + "+" + operands[1] + ")");
1012 
1013  default:
1014  return ERR("could not print Boolean function: unsupported node type '" + std::to_string(node.type) + "'");
1015  }
1016  }
1017 
1018  std::string BooleanFunction::to_string(std::function<Result<std::string>(const BooleanFunction::Node& node, std::vector<std::string>&& operands)>&& printer) const
1019  {
1020  // (1) early termination in case the Boolean function is empty
1021  if (this->m_nodes.empty())
1022  {
1023  return "<empty>";
1024  }
1025 
1026  // (2) iterate the list of nodes and setup string from leafs to root
1027  std::vector<std::string> stack;
1028  for (const auto& node : this->m_nodes)
1029  {
1030  std::vector<std::string> operands;
1031 
1032  if (stack.size() < node.get_arity())
1033  {
1034  // log_error("netlist", "Cannot fetch {} nodes from the stack (= imbalanced stack with {} parts - {}).", node->get_arity(), stack.size(), this->to_string_in_reverse_polish_notation());
1035  return "";
1036  }
1037 
1038  std::move(stack.end() - static_cast<u64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1039  stack.erase(stack.end() - static_cast<u64>(node.get_arity()), stack.end());
1040 
1041  if (auto res = printer(node, std::move(operands)); res.is_ok())
1042  {
1043  stack.emplace_back(res.get());
1044  }
1045  else
1046  {
1047  log_error("netlist", "Cannot translate BooleanFunction::Node '{}' to a string: {}.", node.to_string(), res.get_error().get());
1048  return "";
1049  }
1050  }
1051 
1052  switch (stack.size())
1053  {
1054  case 1:
1055  return stack.back();
1056  default: {
1057  // log_error("netlist", "Cannot translate BooleanFunction (= imbalanced stack with {} remaining parts).", stack.size());
1058  return "";
1059  }
1060  }
1061  }
1062 
1064  {
1065  return BooleanFunction::from_string(expression, {});
1066  }
1067 
1068  Result<BooleanFunction> BooleanFunction::from_string(const std::string& expression, const std::map<std::string, u16>& var_sizes)
1069  {
1072 
1073  using ParserFn = std::function<Result<std::vector<Token>>(const std::string&, const std::map<std::string, u16>&)>;
1074  static const std::vector<std::tuple<ParserType, ParserFn>> parsers = {
1077  {ParserType::LibertyNoSpace, BooleanFunctionParser::parse_with_liberty_grammar}
1078  };
1079 
1080  for (const auto& [parser_type, parser] : parsers)
1081  {
1082  std::string sanitized_expression = expression;
1083  ParserType used_parser_type = parser_type;
1084 
1085  if (parser_type == ParserType::LibertyNoSpace)
1086  {
1087  sanitized_expression.erase(
1088  std::remove(sanitized_expression.begin(), sanitized_expression.end(), ' '),
1089  sanitized_expression.end()
1090  );
1091  used_parser_type = ParserType::Liberty;
1092  }
1093 
1094  auto tokens = parser(sanitized_expression, var_sizes);
1095  // (1) skip if parser cannot translate to tokens
1096  if (tokens.is_error())
1097  {
1098  continue;
1099  }
1100 
1101  // (2) skip if cannot translate to valid reverse-polish notation
1102  tokens = BooleanFunctionParser::reverse_polish_notation(tokens.get(), sanitized_expression, used_parser_type);
1103  if (tokens.is_error())
1104  {
1105  continue;
1106  }
1107  // (3) skip if reverse-polish notation tokens are no valid Boolean function
1108  auto function = BooleanFunctionParser::translate(tokens.get(), sanitized_expression);
1109  if (function.is_error())
1110  {
1111  continue;
1112  }
1113  return function;
1114  }
1115  return ERR("could not parse Boolean function from string: no parser available for '" + expression + "'");
1116  }
1117 
1119  {
1120  auto simplified = Simplification::local_simplification(*this).map<BooleanFunction>([](const auto& s) { return Simplification::abc_simplification(s); }).map<BooleanFunction>([](const auto& s) {
1122  });
1123 
1124  return (simplified.is_ok()) ? simplified.get() : this->clone();
1125  }
1126 
1128  {
1129  auto simplified = Simplification::local_simplification(*this);
1130 
1131  return (simplified.is_ok()) ? simplified.get() : this->clone();
1132  }
1133 
1134  BooleanFunction BooleanFunction::substitute(const std::string& old_variable_name, const std::string& new_variable_name) const
1135  {
1136  auto function = this->clone();
1137  for (auto i = 0u; i < this->m_nodes.size(); i++)
1138  {
1139  if (this->m_nodes[i].has_variable_name(old_variable_name))
1140  {
1141  function.m_nodes[i] = Node::Variable(new_variable_name, this->m_nodes[i].size);
1142  }
1143  }
1144 
1145  return function;
1146  }
1147 
1148  Result<BooleanFunction> BooleanFunction::substitute(const std::string& name, const BooleanFunction& replacement) const
1149  {
1150  // Helper function to substitute a variable with a Boolean function.
1151  auto substitute_variable = [](const auto& node, auto&& operands, auto var_name, auto repl) -> BooleanFunction {
1152  if (node.has_variable_name(var_name))
1153  {
1154  return repl.clone();
1155  }
1156  return BooleanFunction(node.clone(), std::move(operands));
1157  };
1158 
1159  std::vector<BooleanFunction> stack;
1160  for (const auto& node : this->m_nodes)
1161  {
1162  std::vector<BooleanFunction> operands;
1163  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1164  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1165 
1166  stack.emplace_back(substitute_variable(node, std::move(operands), name, replacement));
1167  }
1168 
1169  switch (stack.size())
1170  {
1171  case 1:
1172  return OK(stack.back());
1173  default:
1174  return ERR("could not replace variable '" + name + "' with Boolean function '" + replacement.to_string() + "': validation failed, the operations may be imbalanced");
1175  }
1176  }
1177 
1178  BooleanFunction BooleanFunction::substitute(const std::map<std::string, std::string>& substitutions) const
1179  {
1180  auto function = this->clone();
1181  for (auto i = 0u; i < this->m_nodes.size(); i++)
1182  {
1183  if (const auto var_name_res = this->m_nodes[i].get_variable_name(); var_name_res.is_ok())
1184  {
1185  if (const auto it = substitutions.find(var_name_res.get()); it != substitutions.end())
1186  {
1187  function.m_nodes[i] = Node::Variable(it->second, this->m_nodes[i].size);
1188  }
1189  }
1190  }
1191 
1192  return function;
1193  }
1194 
1195  Result<BooleanFunction> BooleanFunction::substitute(const std::map<std::string, BooleanFunction>& substitutions) const
1196  {
1197  // Helper function to find the replacement for a variable and substitute it with a Boolean function.
1198  //
1199  // node - Node.
1200  // operands - Operands of node.
1201  // returns the AST replacement.
1202  auto substitute_variable = [substitutions](const auto& node, auto&& operands) -> BooleanFunction {
1203  if (node.is_variable())
1204  {
1205  if (auto repl_it = substitutions.find(node.variable); repl_it != substitutions.end())
1206  {
1207  return repl_it->second.clone();
1208  }
1209  }
1210  return BooleanFunction(node.clone(), std::move(operands));
1211  };
1212 
1213  std::vector<BooleanFunction> stack;
1214  for (const auto& node : this->m_nodes)
1215  {
1216  std::vector<BooleanFunction> operands;
1217  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1218  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1219 
1220  stack.emplace_back(substitute_variable(node, std::move(operands)));
1221  }
1222 
1223  switch (stack.size())
1224  {
1225  case 1:
1226  return OK(stack.back());
1227  default:
1228  return ERR("could not carry out multiple substitutions: validation failed, the operations may be imbalanced");
1229  }
1230  }
1231 
1232  Result<BooleanFunction::Value> BooleanFunction::evaluate(const std::unordered_map<std::string, Value>& inputs) const
1233  {
1234  // (0) workaround to preserve the API functionality
1235  if (this->m_nodes.empty())
1236  {
1237  return OK(BooleanFunction::Value::X);
1238  }
1239 
1240  // (1) validate whether the input sizes match the boolean function
1241  if (this->size() != 1)
1242  {
1243  return ERR("could not evaluate Boolean function '" + this->to_string() + "': using single-bit evaluation on a Boolean function of size " + std::to_string(this->size()) + " is illegal");
1244  }
1245 
1246  // (2) translate the input to n-bit to use the generic function
1247  auto generic_inputs = std::unordered_map<std::string, std::vector<Value>>();
1248  for (const auto& [name, value] : inputs)
1249  {
1250  generic_inputs.emplace(name, std::vector<Value>({value}));
1251  }
1252 
1253  auto value = this->evaluate(generic_inputs);
1254  if (value.is_ok())
1255  {
1256  // TODO i find that this is incorrect behavior -> only because the variables are single bit does not mean the whole result is -> does not take concat into account
1257  return OK(value.get()[0]);
1258  }
1259 
1260  return ERR(value.get_error());
1261  }
1262 
1263  Result<std::vector<BooleanFunction::Value>> BooleanFunction::evaluate(const std::unordered_map<std::string, std::vector<Value>>& inputs) const
1264  {
1265  // (0) workaround to preserve the API functionality
1266  if (this->m_nodes.empty())
1267  {
1268  return OK(std::vector<BooleanFunction::Value>({BooleanFunction::Value::X}));
1269  }
1270 
1271  // (1) validate whether the input sizes match the boolean function.
1272  // Walk the nodes once and look up each variable, rather than comparing every input name against
1273  // every node, since compute_truth_table() calls this for each of its rows.
1274  for (const auto& node : this->m_nodes)
1275  {
1276  if (!node.is_variable())
1277  {
1278  continue;
1279  }
1280 
1281  if (const auto it = inputs.find(node.variable); it != inputs.end() && node.size != it->second.size())
1282  {
1283  return ERR("could not evaluate Boolean function '" + this->to_string() + "': as the size of vairbale " + node.variable + " with size " + std::to_string(node.size)
1284  + " does not match the size of the provided input (" + std::to_string(it->second.size()) + ")");
1285  }
1286  }
1287 
1288  // (2) initialize the symbolic state using the input variables
1289  auto symbolic_execution = SMT::SymbolicExecution();
1290  for (const auto& [name, value] : inputs)
1291  {
1292  symbolic_execution.state.set(BooleanFunction::Var(name, value.size()), BooleanFunction::Const(value));
1293  }
1294 
1295  // (3) analyze the evaluation result and check whether the result is a
1296  // constant boolean function
1297  auto result = symbolic_execution.evaluate(*this);
1298  if (result.is_ok())
1299  {
1300  if (auto value = result.get(); value.is_constant())
1301  {
1302  return OK(value.get_top_level_node().constant);
1303  }
1304  return OK(std::vector<BooleanFunction::Value>(this->size(), BooleanFunction::Value::X));
1305  }
1306  return ERR(result.get_error());
1307  }
1308 
1309  Result<std::vector<std::vector<BooleanFunction::Value>>> BooleanFunction::compute_truth_table_bitwise(const std::vector<std::string>& variables) const
1310  {
1311  // only single-bit bitwise logic is handled here, anything else falls back to the general implementation
1312  if (this->size() != 1)
1313  {
1314  return ERR("not a single-bit function");
1315  }
1316  // Every variable has to be part of the truth table and every constant has to be Boolean, so that no value is
1317  // ever unknown. The general implementation evaluates symbolically and therefore simplifies, which cancels
1318  // correlated unknowns: `x ^ x` is zero to it even for an unknown `x`, while evaluating three-valued logic
1319  // yields `X`. Refusing the cases that can produce an unknown keeps both implementations in agreement instead
1320  // of trading a correct answer for a faster one.
1321  const std::unordered_set<std::string> known_variables(variables.begin(), variables.end());
1322  for (const auto& node : this->m_nodes)
1323  {
1324  if (node.size != 1)
1325  {
1326  return ERR("not a single-bit function");
1327  }
1328  switch (node.type)
1329  {
1330  case NodeType::And:
1331  case NodeType::Or:
1332  case NodeType::Not:
1333  case NodeType::Xor:
1334  break;
1335  case NodeType::Constant:
1336  if (node.constant.size() != 1 || (node.constant[0] != Value::ZERO && node.constant[0] != Value::ONE))
1337  {
1338  return ERR("constant is not Boolean");
1339  }
1340  break;
1341  case NodeType::Variable:
1342  if (known_variables.find(node.variable) == known_variables.end())
1343  {
1344  return ERR("function has a variable that is not part of the truth table");
1345  }
1346  break;
1347  default:
1348  return ERR("not a bitwise function");
1349  }
1350  }
1351 
1352  // A row of the truth table is one assignment of the variables, and the value of variable i in row r is bit i
1353  // of r. Instead of evaluating the function once per row, evaluate it once per 64 rows: every intermediate
1354  // value becomes a 64-bit word holding that value for 64 consecutive rows at once, and a gate becomes a single
1355  // bitwise instruction. No value can ever be unknown, as the checks above rejected every function that holds a
1356  // variable outside the truth table or a constant that is not Boolean, so one word per value is enough and the
1357  // operations are plain two-valued logic.
1358 
1359  // the value of variable i within a chunk of 64 consecutive rows, which for i < 6 is a fixed pattern and for
1360  // larger i is constant across the whole chunk
1361  static constexpr u64 PATTERN[6] = {
1362  0xAAAAAAAAAAAAAAAAull,
1363  0xCCCCCCCCCCCCCCCCull,
1364  0xF0F0F0F0F0F0F0F0ull,
1365  0xFF00FF00FF00FF00ull,
1366  0xFFFF0000FFFF0000ull,
1367  0xFFFFFFFF00000000ull,
1368  };
1369 
1370  std::unordered_map<std::string, u32> variable_index;
1371  for (u32 i = 0; i < variables.size(); i++)
1372  {
1373  variable_index[variables[i]] = i;
1374  }
1375 
1376  const u64 num_rows = u64(1) << variables.size();
1377  std::vector<Value> result(num_rows, Value::ZERO);
1378 
1379  std::vector<u64> stack;
1380  stack.reserve(this->m_nodes.size());
1381 
1382  for (u64 base = 0; base < num_rows; base += 64)
1383  {
1384  const u64 rows_in_chunk = std::min<u64>(64, num_rows - base);
1385  const u64 chunk_mask = (rows_in_chunk == 64) ? ~u64(0) : ((u64(1) << rows_in_chunk) - 1);
1386 
1387  stack.clear();
1388  for (const auto& node : this->m_nodes)
1389  {
1390  if (node.type == NodeType::Variable)
1391  {
1392  const u32 i = variable_index.at(node.variable);
1393  stack.push_back((i < 6) ? PATTERN[i] : (((base >> i) & 1) ? ~u64(0) : u64(0)));
1394  continue;
1395  }
1396 
1397  if (node.type == NodeType::Constant)
1398  {
1399  stack.push_back((node.constant[0] == Value::ONE) ? ~u64(0) : u64(0));
1400  continue;
1401  }
1402 
1403  const u16 arity = node.get_arity();
1404  if (stack.size() < arity)
1405  {
1406  return ERR("could not compute truth table: malformed node list");
1407  }
1408 
1409  if (node.type == NodeType::Not)
1410  {
1411  const u64 a = stack.back();
1412  stack.pop_back();
1413  stack.push_back(~a);
1414  continue;
1415  }
1416 
1417  const u64 b = stack.back();
1418  stack.pop_back();
1419  const u64 a = stack.back();
1420  stack.pop_back();
1421 
1422  if (node.type == NodeType::And)
1423  {
1424  stack.push_back(a & b);
1425  }
1426  else if (node.type == NodeType::Or)
1427  {
1428  stack.push_back(a | b);
1429  }
1430  else // NodeType::Xor
1431  {
1432  stack.push_back(a ^ b);
1433  }
1434  }
1435 
1436  if (stack.size() != 1)
1437  {
1438  return ERR("could not compute truth table: malformed node list");
1439  }
1440 
1441  const u64 out = stack.back() & chunk_mask;
1442  for (u64 bit = 0; bit < rows_in_chunk; bit++)
1443  {
1444  result[base + bit] = (out & (u64(1) << bit)) ? Value::ONE : Value::ZERO;
1445  }
1446  }
1447 
1448  return OK(std::vector<std::vector<Value>>({std::move(result)}));
1449  }
1450 
1451  Result<std::vector<std::vector<BooleanFunction::Value>>> BooleanFunction::compute_truth_table(const std::vector<std::string>& ordered_variables, bool remove_unknown_variables) const
1452  {
1453  auto variable_names_in_function = this->get_variable_names();
1454 
1455  // (1) check that each variable is just a single bit, otherwise we do
1456  // not generate a truth-table
1457  for (const auto& node : this->m_nodes)
1458  {
1459  if (node.is_variable() && node.size != 1)
1460  {
1461  return ERR("could not compute truth table for Boolean function '" + this->to_string() + "': unable to generate a truth-table for Boolean function with variables of > 1-bit");
1462  }
1463  }
1464 
1465  // (2) select either parameter or the Boolean function variables
1466  auto variables = ordered_variables;
1467  if (variables.empty())
1468  {
1469  variables = std::vector<std::string>(variable_names_in_function.begin(), variable_names_in_function.end());
1470  }
1471 
1472  // (3) remove any unknown variables from the truth table
1473  if (remove_unknown_variables)
1474  {
1475  variables.erase(
1476  std::remove_if(variables.begin(), variables.end(), [&variable_names_in_function](const auto& s) { return variable_names_in_function.find(s) == variable_names_in_function.end(); }),
1477  variables.end());
1478  }
1479 
1480  // (4.1) check that the function is not empty, otherwise we return a
1481  // Boolean function with a truth-table with 'X' values
1482  if (this->m_nodes.empty())
1483  {
1484  return OK(std::vector<std::vector<Value>>(1, std::vector<Value>(1 << variables.size(), Value::X)));
1485  }
1486 
1487  // (4.2) safety-check in case the number of variables is too large to process. Every additional variable
1488  // doubles the number of rows, so the limit bounds both the runtime and the size of the result.
1489  if (variables.size() > MAX_TRUTH_TABLE_VARIABLES)
1490  {
1491  return ERR("could not compute truth table for Boolean function '" + this->to_string() + "': unable to generate truth-table with more than "
1492  + std::to_string(MAX_TRUTH_TABLE_VARIABLES) + " variables");
1493  }
1494 
1495  // (4.3) evaluate the whole truth table at once if the function only consists of bitwise operations on single
1496  // bits, which is what a function extracted from a gate-level subgraph looks like. The general path below
1497  // runs a symbolic execution per row, which walks and simplifies the entire node list every single time.
1498  if (const auto res = compute_truth_table_bitwise(variables); res.is_ok())
1499  {
1500  return res;
1501  }
1502 
1503  std::vector<std::vector<Value>> truth_table(this->size(), std::vector<Value>(1 << variables.size(), Value::ZERO));
1504 
1505  // (5) iterate the truth-table rows and set each column accordingly
1506  for (auto value = 0u; value < ((u32)1 << variables.size()); value++)
1507  {
1508  std::unordered_map<std::string, std::vector<Value>> input;
1509  auto tmp = value;
1510  for (const auto& variable : variables)
1511  {
1512  input[variable] = ((tmp & 1) == 0) ? std::vector<Value>({Value::ZERO}) : std::vector<Value>({Value::ONE});
1513  tmp >>= 1;
1514  }
1515  auto result = this->evaluate(input);
1516  if (result.is_error())
1517  {
1518  return ERR(result.get_error());
1519  }
1520  auto output = result.get();
1521  for (auto index = 0u; index < truth_table.size(); index++)
1522  {
1523  truth_table[index][value] = output[index];
1524  }
1525  }
1526 
1527  return OK(truth_table);
1528  }
1529 
1530  Result<std::string> BooleanFunction::get_truth_table_as_string(const std::vector<std::string>& ordered_inputs, std::string function_name, bool remove_unknown_inputs) const
1531  {
1532  std::vector<std::string> inputs;
1533  auto inputs_set = this->get_variable_names();
1534  if (ordered_inputs.empty())
1535  {
1536  inputs = std::vector<std::string>(inputs_set.begin(), inputs_set.end());
1537  }
1538  else
1539  {
1540  inputs = ordered_inputs;
1541  }
1542 
1543  if (remove_unknown_inputs)
1544  {
1545  inputs.erase(std::remove_if(inputs.begin(), inputs.end(), [&inputs_set](const auto& s) { return inputs_set.find(s) == inputs_set.end(); }), inputs.end());
1546  }
1547 
1548  const auto res = compute_truth_table(inputs, false);
1549  if (res.is_error())
1550  {
1551  return ERR_APPEND(res.get_error(), "could not print truth table for Boolean function '" + this->to_string() + "': unable to compute truth table");
1552  }
1553  const auto truth_table = res.get();
1554 
1555  std::stringstream str("");
1556 
1557  u32 num_inputs = inputs.size();
1558  u32 num_outputs = truth_table.size();
1559 
1560  // table headers
1561  std::vector<u32> in_widths;
1562  for (const auto& var : inputs)
1563  {
1564  in_widths.push_back(var.size());
1565  str << " " << var << " |";
1566  }
1567 
1568  std::vector<u32> out_widths;
1569  if (function_name.empty())
1570  {
1571  function_name = "O";
1572  }
1573  if (num_outputs == 1)
1574  {
1575  str << "| " << function_name << " ";
1576  out_widths.push_back(function_name.size());
1577  }
1578  else
1579  {
1580  for (u32 i = 0; i < num_outputs; i++)
1581  {
1582  std::string var = function_name + "(" + std::to_string(i) + ")";
1583  str << "| " << var << " ";
1584  out_widths.push_back(var.size());
1585  }
1586  }
1587  str << std::endl;
1588 
1589  // rule below headers
1590  for (u32 i = 0; i < num_inputs; i++)
1591  {
1592  str << std::setw(in_widths.at(i) + 3) << std::setfill('-') << "+";
1593  }
1594  for (u32 i = 0; i < num_outputs; i++)
1595  {
1596  str << "+" << std::setw(out_widths.at(i) + 2) << std::setfill('-') << "-";
1597  }
1598  str << std::endl;
1599 
1600  // table values
1601  for (u32 i = 0; i < (u32)(1 << num_inputs); i++)
1602  {
1603  for (u32 j = 0; j < num_inputs; j++)
1604  {
1605  str << " " << std::left << std::setw(in_widths.at(j)) << std::setfill(' ') << ((i >> j) & 1) << " |";
1606  }
1607 
1608  for (u32 k = 0; k < num_outputs; k++)
1609  {
1610  str << "| " << std::left << std::setw(out_widths.at(k)) << std::setfill(' ') << truth_table.at(k).at(i) << " ";
1611  }
1612  str << std::endl;
1613  }
1614  return OK(str.str());
1615  }
1616 
1617  z3::expr BooleanFunction::to_z3(z3::context& context, const std::map<std::string, z3::expr>& var2expr) const
1618  {
1619  // Helper function to reduce a abstract syntax subtree to z3 expressions
1620  //
1621  // node - Boolean function node.
1622  // p - Boolean function node parameters.
1623  // returns (1) status (true on success, false otherwise),
1624  // (2) SMT-LIB string representation of node and operands.
1625  auto reduce_to_z3 = [&context, &var2expr](const auto& node, auto&& p) -> std::tuple<bool, z3::expr> {
1626  if (node.get_arity() != p.size())
1627  {
1628  return {false, z3::expr(context)};
1629  }
1630 
1631  switch (node.type)
1632  {
1634  return {true, context.bv_val(node.index, node.size)};
1636  // since our constants are defined as arbitrary bit-vectors,
1637  // we have to concat each bit just to be on the safe side
1638  auto constant = context.bv_val(node.constant.front(), 1);
1639  for (u32 i = 1; i < node.constant.size(); i++)
1640  {
1641  const auto bit = node.constant.at(i);
1642  constant = z3::concat(context.bv_val(bit, 1), constant);
1643  }
1644  return {true, constant};
1645  }
1647  if (auto it = var2expr.find(node.variable); it != var2expr.end())
1648  {
1649  return {true, it->second};
1650  }
1651  return {true, context.bv_const(node.variable.c_str(), node.size)};
1652  }
1653 
1655  return {true, p[0] & p[1]};
1657  return {true, p[0] | p[1]};
1659  return {true, ~p[0]};
1661  return {true, p[0] ^ p[1]};
1663  return {true, p[0].extract(p[2].get_numeral_uint(), p[1].get_numeral_uint())};
1665  return {true, z3::concat(p[0], p[1])};
1667  return {true, z3::sext(p[0], p[1].get_numeral_uint())};
1668 
1669  default:
1670  log_error("netlist", "Not implemented reached for nodetype {} in z3 conversion", node.type);
1671  return {false, z3::expr(context)};
1672  }
1673  };
1674 
1675  std::vector<z3::expr> stack;
1676  for (const auto& node : this->m_nodes)
1677  {
1678  std::vector<z3::expr> operands;
1679  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1680  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1681 
1682  if (auto [ok, reduction] = reduce_to_z3(node, std::move(operands)); ok)
1683  {
1684  stack.emplace_back(reduction);
1685  }
1686  else
1687  {
1688  return z3::expr(context);
1689  }
1690  }
1691 
1692  switch (stack.size())
1693  {
1694  case 1:
1695  return stack.back();
1696  default:
1697  return z3::expr(context);
1698  }
1699  }
1700 
1701  BooleanFunction::BooleanFunction(std::vector<BooleanFunction::Node>&& nodes) : m_nodes(nodes)
1702  {
1703  }
1704 
1705  BooleanFunction::BooleanFunction(BooleanFunction::Node&& node, std::vector<BooleanFunction>&& p) : BooleanFunction()
1706  {
1707  auto size = 1;
1708  for (const auto& parameter : p)
1709  {
1710  size += parameter.size();
1711  }
1712  this->m_nodes.reserve(size);
1713 
1714  for (auto&& parameter : p)
1715  {
1716  this->m_nodes.insert(this->m_nodes.end(), parameter.m_nodes.begin(), parameter.m_nodes.end());
1717  }
1718  this->m_nodes.emplace_back(node);
1719  }
1720 
1721  std::string BooleanFunction::to_string_in_reverse_polish_notation() const
1722  {
1723  std::string s;
1724  for (const auto& node : this->m_nodes)
1725  {
1726  s += node.to_string() + " ";
1727  }
1728  return s;
1729  }
1730 
1731  Result<BooleanFunction> BooleanFunction::validate(BooleanFunction&& function)
1732  {
1737  const auto coverage = function.compute_node_coverage();
1738  if (coverage.back() != function.length())
1739  {
1740  auto str = function.to_string_in_reverse_polish_notation();
1741  return ERR("could not validate '" + str + "': imbalanced function with coverage '" + std::to_string(coverage.back()) + " != " + std::to_string(function.length()));
1742  }
1743 
1744  // Validate that operand bit-widths are consistent with each operator's expectations.
1745  // Operands of an operator at index i are stored in the implicit RPN tree: the last
1746  // operand's root is at i-1, the previous one's root is i-1 - coverage[op_root], etc.
1747  const auto& nodes = function.m_nodes;
1748  for (size_t i = 0; i < nodes.size(); ++i)
1749  {
1750  const auto& n = nodes[i];
1751  const u16 arity = n.get_arity();
1752  if (arity == 0)
1753  {
1754  continue;
1755  }
1756 
1757  std::vector<size_t> op_idx(arity);
1758  size_t cursor = i - 1;
1759  for (int k = static_cast<int>(arity) - 1; k >= 0; --k)
1760  {
1761  op_idx[k] = cursor;
1762  if (k > 0)
1763  {
1764  cursor -= coverage[cursor];
1765  }
1766  }
1767 
1768  const auto op_size = [&](size_t k) -> u16 { return nodes[op_idx[k]].size; };
1769 
1770  switch (n.type)
1771  {
1772  // Binary operators where both operands and result share the same bit-width.
1773  case NodeType::And:
1774  case NodeType::Or:
1775  case NodeType::Xor:
1776  case NodeType::Add:
1777  case NodeType::Sub:
1778  case NodeType::Mul:
1779  case NodeType::Sdiv:
1780  case NodeType::Udiv:
1781  case NodeType::Srem:
1782  case NodeType::Urem:
1783  if (op_size(0) != n.size || op_size(1) != n.size)
1784  {
1785  return ERR("could not validate Boolean function: bit-size mismatch in '" + std::to_string(n.type) + "' node (operands " + std::to_string(op_size(0)) + ", "
1786  + std::to_string(op_size(1)) + " vs result " + std::to_string(n.size) + ")");
1787  }
1788  break;
1789 
1790  // Unary operator whose operand and result share the same bit-width.
1791  case NodeType::Not:
1792  if (op_size(0) != n.size)
1793  {
1794  return ERR("could not validate Boolean function: bit-size mismatch in 'Not' node (operand " + std::to_string(op_size(0)) + " vs result " + std::to_string(n.size) + ")");
1795  }
1796  break;
1797 
1798  // Comparison operators: both operands must share a width; result is 1-bit.
1799  case NodeType::Eq:
1800  case NodeType::Sle:
1801  case NodeType::Slt:
1802  case NodeType::Ule:
1803  case NodeType::Ult:
1804  if (op_size(0) != op_size(1) || n.size != 1)
1805  {
1806  return ERR("could not validate Boolean function: bit-size mismatch in comparison node (operands " + std::to_string(op_size(0)) + ", " + std::to_string(op_size(1))
1807  + " vs result " + std::to_string(n.size) + ")");
1808  }
1809  break;
1810 
1811  // If-Then-Else: condition is 1-bit, then/else and result share a width.
1812  case NodeType::Ite:
1813  if (op_size(0) != 1 || op_size(1) != n.size || op_size(2) != n.size)
1814  {
1815  return ERR("could not validate Boolean function: bit-size mismatch in 'Ite' node (cond " + std::to_string(op_size(0)) + ", then " + std::to_string(op_size(1)) + ", else "
1816  + std::to_string(op_size(2)) + " vs result " + std::to_string(n.size) + ")");
1817  }
1818  break;
1819 
1820  // Other node types (Concat, Slice, Zext, Sext, shifts, ...) have specialized
1821  // size rules; their construction goes through dedicated factories that already
1822  // perform the relevant checks, so we don't re-validate them here.
1823  default:
1824  break;
1825  }
1826  }
1827 
1828  return OK(std::move(function));
1829  }
1830 
1831  std::vector<u32> BooleanFunction::compute_node_coverage() const
1832  {
1833  auto coverage = std::vector<u32>(this->m_nodes.size(), (u32)-1);
1834 
1840  auto get = [](const auto& cov, size_t index) -> u32 { return (index < cov.size()) ? cov[index] : -1; };
1841 
1849  auto set = [](auto& cov, size_t index, u32 x = 0, u32 y = 0, u32 z = 0) { cov[index] = ((x != (u32)-1) && (y != (u32)-1) && (z != (u32)-1)) ? (x + y + z + 1) : (u32)-1; };
1850 
1851  for (auto i = 0ul; i < this->m_nodes.size(); i++)
1852  {
1853  auto arity = this->m_nodes[i].get_arity();
1854 
1855  switch (arity)
1856  {
1857  case 0: {
1858  set(coverage, i);
1859  break;
1860  }
1861  case 1: {
1862  auto x = get(coverage, i - 1);
1863  set(coverage, i, x);
1864  break;
1865  }
1866  case 2: {
1867  auto x = get(coverage, i - 1);
1868  auto y = get(coverage, i - 1 - x);
1869  set(coverage, i, x, y);
1870  break;
1871  }
1872  case 3: {
1873  auto x = get(coverage, i - 1);
1874  auto y = get(coverage, i - 1 - x);
1875  auto z = get(coverage, i - 1 - x - y);
1876  set(coverage, i, x, y, z);
1877  break;
1878  }
1879  }
1880  }
1881 
1882  return coverage;
1883  }
1884 
1886  {
1887  return Node(_type, _size, {}, {}, {});
1888  }
1889 
1890  BooleanFunction::Node BooleanFunction::Node::Constant(const std::vector<BooleanFunction::Value> _constant)
1891  {
1892  return Node(NodeType::Constant, _constant.size(), _constant, {}, {});
1893  }
1894 
1896  {
1897  return Node(NodeType::Index, _size, {}, _index, {});
1898  }
1899 
1900  BooleanFunction::Node BooleanFunction::Node::Variable(const std::string _variable, u16 _size)
1901  {
1902  return Node(NodeType::Variable, _size, {}, {}, _variable);
1903  }
1904 
1905  bool BooleanFunction::Node::operator==(const Node& other) const
1906  {
1907  return std::tie(this->type, this->size, this->constant, this->index, this->variable) == std::tie(other.type, other.size, other.constant, other.index, other.variable);
1908  }
1909 
1910  bool BooleanFunction::Node::operator!=(const Node& other) const
1911  {
1912  return !(*this == other);
1913  }
1914 
1915  bool BooleanFunction::Node::operator<(const Node& other) const
1916  {
1917  return std::tie(this->type, this->size, this->constant, this->index, this->variable) < std::tie(other.type, other.size, other.constant, other.index, other.variable);
1918  }
1919 
1921  {
1922  return Node(this->type, this->size, this->constant, this->index, this->variable);
1923  }
1924 
1926  {
1927  switch (this->type)
1928  {
1929  case NodeType::Constant: {
1930  std::string str;
1931  for (const auto& value : this->constant)
1932  {
1933  str = enum_to_string(value) + str;
1934  }
1935  return "0b" + str;
1936  }
1937 
1938  case NodeType::Index:
1939  return std::to_string(this->index);
1940  case NodeType::Variable:
1941  return this->variable;
1942 
1943  case NodeType::And:
1944  return "&";
1945  case NodeType::Or:
1946  return "|";
1947  case NodeType::Not:
1948  return "~";
1949  case NodeType::Xor:
1950  return "^";
1951 
1952  case NodeType::Add:
1953  return "+";
1954  case NodeType::Sub:
1955  return "-";
1956  case NodeType::Mul:
1957  return "*";
1958  case NodeType::Sdiv:
1959  return "/s";
1960  case NodeType::Udiv:
1961  return "/";
1962  case NodeType::Srem:
1963  return "\%s";
1964  case NodeType::Urem:
1965  return "\%";
1966 
1967  case NodeType::Concat:
1968  return "++";
1969  case NodeType::Slice:
1970  return "Slice";
1971  case NodeType::Zext:
1972  return "Zext";
1973  case NodeType::Sext:
1974  return "Sext";
1975 
1976  case NodeType::Shl:
1977  return "<<";
1978  case NodeType::Lshr:
1979  return ">>";
1980  case NodeType::Ashr:
1981  return ">>a";
1982  case NodeType::Rol:
1983  return "<<r";
1984  case NodeType::Ror:
1985  return ">>r";
1986 
1987  case NodeType::Eq:
1988  return "==";
1989  case NodeType::Sle:
1990  return "<=s";
1991  case NodeType::Slt:
1992  return "<s";
1993  case NodeType::Ule:
1994  return "<=";
1995  case NodeType::Ult:
1996  return "<";
1997  case NodeType::Ite:
1998  return "Ite";
1999 
2000  default:
2001  return "unsupported node type '" + std::to_string(this->type) + "'.";
2002  }
2003  }
2004 
2006  {
2008  }
2009 
2011  {
2012  static const std::map<u16, u16> type2arity = {
2021  };
2022 
2023  return type2arity.at(type);
2024  }
2025 
2026  bool BooleanFunction::Node::is(u16 _type) const
2027  {
2028  return this->type == _type;
2029  }
2030 
2032  {
2034  }
2035 
2036  bool BooleanFunction::Node::has_constant_value(const std::vector<Value>& value) const
2037  {
2038  return this->is_constant() && (this->constant == value);
2039  }
2040 
2042  {
2043  if (!this->is_constant())
2044  {
2045  return false;
2046  }
2047 
2048  auto bv_value = std::vector<BooleanFunction::Value>({});
2049  bv_value.reserve(this->size);
2050  for (auto i = 0u; i < this->constant.size(); i++)
2051  {
2052  bv_value.emplace_back((value & (1 << i)) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO);
2053  }
2054  return this->constant == bv_value;
2055  }
2056 
2058  {
2059  if (!this->is_constant())
2060  {
2061  return ERR("Node is not a constant");
2062  }
2063 
2064  return OK(this->constant);
2065  }
2066 
2068  {
2069  if (!this->is_constant())
2070  {
2071  return ERR("Node is not a constant");
2072  }
2073 
2074  if (this->size > 64)
2075  {
2076  return ERR("Node constant has size > 64");
2077  }
2078 
2079  if (std::any_of(this->constant.begin(), this->constant.end(), [](auto v) { return v != BooleanFunction::Value::ONE && v != BooleanFunction::Value::ZERO; }))
2080  {
2081  return ERR("Node constant is undefined or high-impedance");
2082  }
2083 
2084  u64 val = 0;
2085  for (auto it = this->constant.rbegin(); it != this->constant.rend(); it++)
2086  {
2087  val <<= 1;
2088  val |= *it;
2089  }
2090 
2091  return OK(val);
2092  }
2093 
2095  {
2096  return this->is(BooleanFunction::NodeType::Index);
2097  }
2098 
2100  {
2101  return this->is_index() && (this->index == value);
2102  }
2103 
2105  {
2106  if (!this->is_index())
2107  {
2108  return ERR("Node is not an index");
2109  }
2110 
2111  return OK(this->index);
2112  }
2113 
2115  {
2117  }
2118 
2119  bool BooleanFunction::Node::has_variable_name(const std::string& value) const
2120  {
2121  return this->is_variable() && (this->variable == value);
2122  }
2123 
2125  {
2126  if (!this->is_variable())
2127  {
2128  return ERR("Node is not a variable");
2129  }
2130 
2131  return OK(this->variable);
2132  }
2133 
2135  {
2136  return !this->is_operand();
2137  }
2138 
2140  {
2141  return this->is_constant() || this->is_variable() || this->is_index();
2142  }
2143 
2145  {
2146  return (this->type == NodeType::And) || (this->type == NodeType::Or) || (this->type == NodeType::Xor) || (this->type == NodeType::Add) || (this->type == NodeType::Mul)
2147  || (this->type == NodeType::Eq);
2148  }
2149 
2150  BooleanFunction::Node::Node(u16 _type, u16 _size, std::vector<BooleanFunction::Value> _constant, u16 _index, std::string _variable)
2151  : type(_type), size(_size), constant(_constant), index(_index), variable(_variable)
2152  {
2153  }
2154 
2155 } // namespace hal
u32 size
static Result< BooleanFunction > Slt(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool has_index_value(u16 index) const
BooleanFunction operator+(const BooleanFunction &other) const
static Result< BooleanFunction > Ite(BooleanFunction &&p0, BooleanFunction &&p1, BooleanFunction &&p2, u16 size)
BooleanFunction operator&(const BooleanFunction &other) const
static Result< BooleanFunction > Eq(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static BooleanFunction Var(const std::string &name, u16 size=1)
static constexpr u32 MAX_TRUTH_TABLE_VARIABLES
static Result< BooleanFunction > Xor(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool operator==(const BooleanFunction &other) const
Result< std::string > get_variable_name() const
BooleanFunction operator^(const BooleanFunction &other) const
const BooleanFunction::Node & get_top_level_node() const
static Result< BooleanFunction > Add(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
std::set< std::string > get_variable_names() const
static Result< BooleanFunction > Lshr(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool has_constant_value(const std::vector< Value > &value) const
static Result< BooleanFunction > Zext(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
Result< std::vector< Value > > get_constant_value() const
static Result< BooleanFunction > Mul(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool has_variable_name(const std::string &variable_name) const
static Result< BooleanFunction > Ule(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Sub(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Sext(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Udiv(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction operator~() const
static Result< BooleanFunction > Concat(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Ult(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static BooleanFunction Index(u16 index, u16 size)
z3::expr to_z3(z3::context &context, const std::map< std::string, z3::expr > &var2expr={}) const
Result< std::vector< std::vector< Value > > > compute_truth_table(const std::vector< std::string > &ordered_variables={}, bool remove_unknown_variables=false) const
BooleanFunction & operator-=(const BooleanFunction &other)
Result< Value > evaluate(const std::unordered_map< std::string, Value > &inputs) const
static Result< BooleanFunction > Sle(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > build(std::vector< Node > &&nodes)
BooleanFunction operator|(const BooleanFunction &other) const
static Result< BooleanFunction > from_string(const std::string &expression)
static Result< BooleanFunction > Slice(BooleanFunction &&p0, BooleanFunction &&p1, BooleanFunction &&p2, u16 size)
const std::vector< BooleanFunction::Node > & get_nodes() const
BooleanFunction & operator*=(const BooleanFunction &other)
bool is(u16 type) const
BooleanFunction simplify() const
BooleanFunction operator-(const BooleanFunction &other) const
Result< std::string > get_truth_table_as_string(const std::vector< std::string > &ordered_variables={}, std::string function_name="", bool remove_unknown_variables=false) const
bool operator<(const BooleanFunction &other) const
BooleanFunction & operator&=(const BooleanFunction &other)
BooleanFunction clone() const
Value
represents the type of the node
static Result< BooleanFunction > Or(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool operator!=(const BooleanFunction &other) const
static Result< BooleanFunction > Urem(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction & operator^=(const BooleanFunction &other)
static Result< BooleanFunction > Ror(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction & operator+=(const BooleanFunction &other)
static Result< BooleanFunction > Sdiv(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction & operator|=(const BooleanFunction &other)
static std::string to_string(Value value)
BooleanFunction simplify_local() const
static BooleanFunction Const(const BooleanFunction::Value &value)
BooleanFunction operator*(const BooleanFunction &other) const
static Result< BooleanFunction > Ashr(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< u64 > to_u64(const std::vector< BooleanFunction::Value > &value)
static Result< BooleanFunction > Not(BooleanFunction &&p0, u16 size)
static Result< BooleanFunction > And(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
std::vector< BooleanFunction > get_parameters() const
Result< u16 > get_index_value() const
BooleanFunction substitute(const std::string &old_variable_name, const std::string &new_variable_name) const
static Result< BooleanFunction > Shl(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
Result< u64 > get_constant_value_u64() const
static Result< BooleanFunction > Rol(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Srem(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
uint64_t u64
Definition: defines.h:42
uint16_t u16
Definition: defines.h:40
uint32_t u32
Definition: defines.h:41
int64_t i64
Definition: defines.h:37
uint8_t u8
Definition: defines.h:39
#define log_error(channel,...)
Definition: log.h:78
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
Result< std::vector< Token > > parse_with_standard_grammar(const std::string &expression, const std::map< std::string, u16 > &var_sizes={})
ParserType
ParserType refers to the parser identifier.
Definition: parser.h:41
Result< BooleanFunction > translate(std::vector< Token > &&tokens, const std::string &expression)
Result< std::vector< Token > > reverse_polish_notation(std::vector< Token > &&tokens, const std::string &expression, const ParserType &parser)
Result< std::vector< Token > > parse_with_liberty_grammar(const std::string &expression, const std::map< std::string, u16 > &var_sizes={})
Result< BooleanFunction > local_simplification(const BooleanFunction &function)
void remove(std::filesystem::path file_path)
Definition: defines.h:45
std::ostream & operator<<(std::ostream &os, T e)
Definition: enums.h:136
std::string enum_to_string(T e)
Definition: enums.h:53
PinType type
std::string name
std::vector< BooleanFunction::Value > constant
The (optional) constant value of the node.
u16 type
The type of the node.
u16 size
The bit-size of the node.
static u16 get_arity_of_type(u16 type)
static Node Constant(const std::vector< BooleanFunction::Value > value)
bool has_index_value(u16 value) const
std::string variable
The (optional) variable name of the node.
bool has_variable_name(const std::string &variable_name) const
static Node Operation(u16 type, u16 size)
bool has_constant_value(const std::vector< Value > &value) const
Result< std::vector< Value > > get_constant_value() const
static Node Index(u16 index, u16 size)
Result< u16 > get_index_value() const
bool operator!=(const Node &other) const
bool operator==(const Node &other) const
bool operator<(const Node &other) const
u16 index
The (optional) index value of the node.
static Node Variable(const std::string variable, u16 size)
Result< std::string > get_variable_name() const
Result< u64 > get_constant_value_u64() const
Token refers to a token identifier and accompanied data.
Definition: parser.h:75