HAL
boolean_function.cpp
Go to the documentation of this file.
2 
8 
9 #include <algorithm>
10 #include <bitset>
11 #include <boost/spirit/home/x3.hpp>
12 #include <chrono>
13 #include <map>
14 #include <iomanip>
15 
16 namespace hal
17 {
18  template<>
19  std::map<BooleanFunction::Value, std::string> EnumStrings<BooleanFunction::Value>::data = {{BooleanFunction::Value::ZERO, "0"},
20  {BooleanFunction::Value::ONE, "1"},
21  {BooleanFunction::Value::X, "X"},
22  {BooleanFunction::Value::Z, "Z"}};
23 
25  {
26  switch (v)
27  {
28  case ZERO:
29  return std::string("0");
30  case ONE:
31  return std::string("1");
32  case X:
33  return std::string("X");
34  case Z:
35  return std::string("Z");
36  }
37 
38  return std::string("X");
39  }
40 
41  namespace
42  {
43  static std::vector<char> char_map = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
44  // namespace
45 
46  static Result<std::string> to_bin(const std::vector<BooleanFunction::Value>& value)
47  {
48  if (value.size() == 0)
49  {
50  return ERR("could not convert bit-vector to binary string: bit-vector is empty");
51  }
52 
53  std::string res = "";
54  res.reserve(value.size());
55 
56  for (auto v : value)
57  {
58  res += enum_to_string<BooleanFunction::Value>(v);
59  }
60 
61  return OK(res);
62  }
63 
64  static Result<std::string> to_oct(const std::vector<BooleanFunction::Value>& value)
65  {
66  int bitsize = value.size();
67  if (bitsize == 0)
68  {
69  return ERR("could not convert bit-vector to octal string: bit-vector is empty");
70  }
71 
72  u8 first_bits = bitsize % 3;
73 
74  u8 index = 0;
75  u8 mask = 0;
76 
77  u8 v1, v2, v3;
78 
79  // result string prep
80  std::string res = "";
81  res.reserve((bitsize + 2) / 3);
82 
83  // deal with 0-3 leading bits
84  for (u8 i = 0; i < first_bits; i++)
85  {
86  v1 = value.at(i);
87  index = (index << 1) | v1;
88  mask |= v1;
89  }
90  mask = -((mask >> 1) & 0x1);
91  if (first_bits)
92  {
93  res += (char_map[index] & ~mask) | ('X' & mask);
94  }
95 
96  // deal with 4-bit blocks (left to right)
97  for (int i = bitsize % 3; i < bitsize; i += 3)
98  {
99  v1 = value[i];
100  v2 = value[i + 1];
101  v3 = value[i + 2];
102 
103  index = (v1 << 2) | (v2 << 1) | v3; // cannot exceed char_map range as index always < 16, no further check required
104  mask = -(((v1 | v2 | v3) >> 1) & 0x1);
105 
106  res += (char_map[index] & ~mask) | ('X' & mask);
107  }
108  return OK(res);
109  }
110 
111  static Result<std::string> to_dec(const std::vector<BooleanFunction::Value>& value)
112  {
113  int bitsize = value.size();
114  if (bitsize == 0)
115  {
116  return ERR("could not convert bit-vector to decimal string: bit-vector is empty");
117  }
118 
119  if (bitsize > 64)
120  {
121  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");
122  }
123 
124  u64 tmp = 0;
125  u8 x_flag = 0;
126 
127  for (auto it = value.rbegin(); it != value.rend(); it++)
128  {
129  x_flag |= *it >> 1;
130  tmp <<= 1;
131  tmp |= *it;
132  }
133 
134  if (x_flag)
135  {
136  return OK(std::string("X"));
137  }
138  return OK(std::to_string(tmp));
139  }
140 
141  static Result<std::string> to_hex(const std::vector<BooleanFunction::Value>& value)
142  {
143  int bitsize = value.size();
144  if (bitsize == 0)
145  {
146  return ERR("could not convert bit-vector to hexadecimal string: bit-vector is empty");
147  }
148 
149  u8 first_bits = bitsize & 0x3;
150 
151  u8 index = 0;
152  u8 mask = 0;
153 
154  u8 v1, v2, v3, v4;
155 
156  // result string prep
157  std::string res = "";
158  res.reserve((bitsize + 3) / 4);
159 
160  // deal with 0-3 leading bits
161  for (u8 i = 0; i < first_bits; i++)
162  {
163  v1 = value.at(i);
164  index = (index << 1) | v1;
165  mask |= v1;
166  }
167  mask = -((mask >> 1) & 0x1);
168  if (first_bits)
169  {
170  res += (char_map[index] & ~mask) | ('X' & mask);
171  }
172 
173  // deal with 4-bit blocks (left to right)
174  for (int i = bitsize & 0x3; i < bitsize; i += 4)
175  {
176  v1 = value[i];
177  v2 = value[i + 1];
178  v3 = value[i + 2];
179  v4 = value[i + 3];
180 
181  index = ((v1 << 3) | (v2 << 2) | (v3 << 1) | v4) & 0xF;
182  mask = -(((v1 | v2 | v3 | v4) >> 1) & 0x1);
183 
184  res += (char_map[index] & ~mask) | ('X' & mask);
185  }
186 
187  return OK(res);
188  }
189  } // namespace
190 
191  Result<std::string> BooleanFunction::to_string(const std::vector<BooleanFunction::Value>& value, u8 base)
192  {
193  switch (base)
194  {
195  case 2:
196  return to_bin(value);
197  case 8:
198  return to_oct(value);
199  case 10:
200  return to_dec(value);
201  case 16:
202  return to_hex(value);
203  default:
204  return ERR("could not convert bit-vector to string: invalid value '" + std::to_string(base) + "' given for base");
205  }
206  }
207 
208  Result<u64> BooleanFunction::to_u64(const std::vector<BooleanFunction::Value>& value)
209  {
210  if (value.size() > 64)
211  {
212  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()) + ".");
213  }
214 
215  u64 val = 0;
216  for (auto it = value.rbegin(); it != value.rend(); it++)
217  {
218  if ((*it != BooleanFunction::Value::ZERO) && (*it != BooleanFunction::Value::ONE))
219  {
220  return ERR("cannot translate vector of values to u64 numeral: found value other than ZERO or ONE: " + BooleanFunction::to_string(*it) + ".");
221  }
222 
223  val <<= 1;
224  val |= *it;
225  }
226 
227  return OK(val);
228  }
229 
230  std::ostream& operator<<(std::ostream& os, BooleanFunction::Value v)
231  {
232  return os << BooleanFunction::to_string(v);
233  }
234 
236  {
237  }
238 
239  Result<BooleanFunction> BooleanFunction::build(std::vector<BooleanFunction::Node>&& nodes)
240  {
241  if (auto res = BooleanFunction::validate(BooleanFunction(std::move(nodes))); res.is_error())
242  {
243  return ERR_APPEND(res.get_error(), "could not build Boolean function from vector of nodes: failed to validate Boolean function");
244  }
245  else
246  {
247  return res;
248  }
249  }
250 
251  BooleanFunction BooleanFunction::Var(const std::string& name, u16 size)
252  {
254  }
255 
257  {
258  return BooleanFunction(Node::Constant({value}));
259  }
260 
261  BooleanFunction BooleanFunction::Const(const std::vector<BooleanFunction::Value>& values)
262  {
263  return BooleanFunction(Node::Constant(values));
264  }
265 
267  {
268  auto values = std::vector<BooleanFunction::Value>();
269  values.reserve(size);
270  for (auto i = 0; i < size; i++)
271  {
272  values.emplace_back(((value >> i) & 1) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO);
273  }
274 
275  return BooleanFunction::Const(values);
276  }
277 
279  {
280  return BooleanFunction(Node::Index(index, size));
281  }
282 
284  {
285  if ((p0.size() != p1.size()) || (p0.size() != size))
286  {
287  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())
288  + ", size = " + std::to_string(size) + ")");
289  }
290 
291  return OK(BooleanFunction(Node::Operation(NodeType::And, size), std::move(p0), std::move(p1)));
292  }
293 
295  {
296  if ((p0.size() != p1.size()) || (p0.size() != size))
297  {
298  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())
299  + ", size = " + std::to_string(size) + ").");
300  }
301 
302  return OK(BooleanFunction(Node::Operation(NodeType::Or, size), std::move(p0), std::move(p1)));
303  }
304 
306  {
307  if (p0.size() != size)
308  {
309  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) + ").");
310  }
311 
312  return OK(BooleanFunction(Node::Operation(NodeType::Not, size), std::move(p0)));
313  }
314 
316  {
317  if ((p0.size() != p1.size()) || (p0.size() != size))
318  {
319  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())
320  + ", size = " + std::to_string(size) + ").");
321  }
322 
323  return OK(BooleanFunction(Node::Operation(NodeType::Xor, size), std::move(p0), std::move(p1)));
324  }
325 
327  {
328  if ((p0.size() != p1.size()) || (p0.size() != size))
329  {
330  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())
331  + ", size = " + std::to_string(size) + ").");
332  }
333 
334  return OK(BooleanFunction(Node::Operation(NodeType::Add, size), std::move(p0), std::move(p1)));
335  }
336 
338  {
339  if ((p0.size() != p1.size()) || (p0.size() != size))
340  {
341  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())
342  + ", size = " + std::to_string(size) + ").");
343  }
344 
345  return OK(BooleanFunction(Node::Operation(NodeType::Sub, size), std::move(p0), std::move(p1)));
346  }
347 
349  {
350  if ((p0.size() != p1.size()) || (p0.size() != size))
351  {
352  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())
353  + ", size = " + std::to_string(size) + ").");
354  }
355 
356  return OK(BooleanFunction(Node::Operation(NodeType::Mul, size), std::move(p0), std::move(p1)));
357  }
358 
360  {
361  if ((p0.size() != p1.size()) || (p0.size() != size))
362  {
363  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())
364  + ", size = " + std::to_string(size) + ").");
365  }
366 
367  return OK(BooleanFunction(Node::Operation(NodeType::Sdiv, size), std::move(p0), std::move(p1)));
368  }
369 
371  {
372  if ((p0.size() != p1.size()) || (p0.size() != size))
373  {
374  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())
375  + ", size = " + std::to_string(size) + ").");
376  }
377 
378  return OK(BooleanFunction(Node::Operation(NodeType::Udiv, size), std::move(p0), std::move(p1)));
379  }
380 
382  {
383  if ((p0.size() != p1.size()) || (p0.size() != size))
384  {
385  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())
386  + ", size = " + std::to_string(size) + ").");
387  }
388 
389  return OK(BooleanFunction(Node::Operation(NodeType::Srem, size), std::move(p0), std::move(p1)));
390  }
391 
393  {
394  if ((p0.size() != p1.size()) || (p0.size() != size))
395  {
396  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())
397  + ", size = " + std::to_string(size) + ").");
398  }
399 
400  return OK(BooleanFunction(Node::Operation(NodeType::Urem, size), std::move(p0), std::move(p1)));
401  }
402 
404  {
405  if (!p1.is_index() || !p2.is_index())
406  {
407  return ERR("could not apply slice operation: function types do not match (p1 and p2 must be of type 'BooleanFunction::Index')");
408  }
409  if ((p0.size() != p1.size()) || (p1.size() != p2.size()))
410  {
411  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())
412  + " - sizes must be equal)");
413  }
414 
415  auto start = p1.get_index_value().get();
416  auto end = p2.get_index_value().get();
417  if ((start > end) || (start >= p0.size()) || (end >= p0.size()) || (end - start + 1) != size)
418  {
419  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())
420  + ", p1 = " + std::to_string(start) + ", p2 = " + std::to_string(end) + ")");
421  }
422 
423  return OK(BooleanFunction(Node::Operation(NodeType::Slice, size), std::move(p0), std::move(p1), std::move(p2)));
424  }
425 
427  {
428  if ((p0.size() + p1.size()) != size)
429  {
430  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())
431  + "-bit, size = " + std::to_string(size) + ").");
432  }
433 
434  return OK(BooleanFunction(Node::Operation(NodeType::Concat, size), std::move(p0), std::move(p1)));
435  }
436 
438  {
439  if (p0.size() > size || p1.size() != size)
440  {
441  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())
442  + "-bit, size = " + std::to_string(size) + ").");
443  }
444 
445  if (!p1.has_index_value(size))
446  {
447  return ERR("could not apply ZEXT operation: p1 does not encode size (p1 = " + p1.to_string() + ", size = " + std::to_string(size) + ").");
448  }
449 
450  return OK(BooleanFunction(Node::Operation(NodeType::Zext, size), std::move(p0), std::move(p1)));
451  }
452 
454  {
455  if (p0.size() > size || p1.size() != size)
456  {
457  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())
458  + "-bit, size = " + std::to_string(size) + ").");
459  }
460 
461  if (!p1.has_index_value(size))
462  {
463  return ERR("could not apply SEXT operation: p1 does not encode size (p1 = " + p1.to_string() + ", size = " + std::to_string(size) + ").");
464  }
465 
466  return OK(BooleanFunction(Node::Operation(NodeType::Sext, size), std::move(p0), std::move(p1)));
467  }
468 
470  {
471  if (p0.size() != size || p1.size() != size)
472  {
473  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())
474  + "-bit, size = " + std::to_string(size) + ").");
475  }
476 
477  if (!p1.is_index())
478  {
479  return ERR("could not apply SHL operation: p1 is not an index.");
480  }
481 
482  return OK(BooleanFunction(Node::Operation(NodeType::Shl, size), std::move(p0), std::move(p1)));
483  }
484 
486  {
487  if (p0.size() != size || p1.size() != size)
488  {
489  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())
490  + "-bit, size = " + std::to_string(size) + ").");
491  }
492 
493  if (!p1.is_index())
494  {
495  return ERR("could not apply LSHR operation: p1 is not an index.");
496  }
497 
498  return OK(BooleanFunction(Node::Operation(NodeType::Lshr, size), std::move(p0), std::move(p1)));
499  }
500 
502  {
503  if (p0.size() != size || p1.size() != size)
504  {
505  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())
506  + "-bit, size = " + std::to_string(size) + ").");
507  }
508 
509  if (!p1.is_index())
510  {
511  return ERR("could not apply ASHR operation: p1 is not an index.");
512  }
513 
514  return OK(BooleanFunction(Node::Operation(NodeType::Ashr, size), std::move(p0), std::move(p1)));
515  }
516 
518  {
519  if (p0.size() != size || p1.size() != size)
520  {
521  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())
522  + "-bit, size = " + std::to_string(size) + ").");
523  }
524 
525  if (!p1.is_index())
526  {
527  return ERR("could not apply ROL operation: p1 is not an index.");
528  }
529 
530  return OK(BooleanFunction(Node::Operation(NodeType::Rol, size), std::move(p0), std::move(p1)));
531  }
532 
534  {
535  if (p0.size() != size || p1.size() != size)
536  {
537  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())
538  + "-bit, size = " + std::to_string(size) + ").");
539  }
540 
541  if (!p1.is_index())
542  {
543  return ERR("could not apply ROR operation: p1 is not an index.");
544  }
545 
546  return OK(BooleanFunction(Node::Operation(NodeType::Ror, size), std::move(p0), std::move(p1)));
547  }
548 
550  {
551  if (p0.size() != p1.size() || size != 1)
552  {
553  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())
554  + "-bit, size = " + std::to_string(size) + ").");
555  }
556 
557  return OK(BooleanFunction(Node::Operation(NodeType::Eq, size), std::move(p0), std::move(p1)));
558  }
559 
561  {
562  if (p0.size() != p1.size() || size != 1)
563  {
564  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())
565  + "-bit, size = " + std::to_string(size) + ").");
566  }
567 
568  return OK(BooleanFunction(Node::Operation(NodeType::Sle, size), std::move(p0), std::move(p1)));
569  }
570 
572  {
573  if (p0.size() != p1.size() || size != 1)
574  {
575  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())
576  + "-bit, size = " + std::to_string(size) + ").");
577  }
578 
579  return OK(BooleanFunction(Node::Operation(NodeType::Slt, size), std::move(p0), std::move(p1)));
580  }
581 
583  {
584  if (p0.size() != p1.size() || size != 1)
585  {
586  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())
587  + "-bit, size = " + std::to_string(size) + ").");
588  }
589 
590  return OK(BooleanFunction(Node::Operation(NodeType::Ule, size), std::move(p0), std::move(p1)));
591  }
592 
594  {
595  if (p0.size() != p1.size() || size != 1)
596  {
597  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())
598  + "-bit, size = " + std::to_string(size) + ").");
599  }
600 
601  return OK(BooleanFunction(Node::Operation(NodeType::Ult, size), std::move(p0), std::move(p1)));
602  }
603 
605  {
606  if (p0.size() != 1 || p1.size() != size || p2.size() != size)
607  {
608  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())
609  + "-bit, p2 = " + std::to_string(p2.size()) + "-bit, size = " + std::to_string(size) + ").");
610  }
611 
612  return OK(BooleanFunction(Node::Operation(NodeType::Ite, size), std::move(p0), std::move(p1), std::move(p2)));
613  }
614 
615  std::ostream& operator<<(std::ostream& os, const BooleanFunction& f)
616  {
617  return os << f.to_string();
618  }
619 
621  {
622  return BooleanFunction::And(this->clone(), other.clone(), this->size()).get();
623  }
624 
626  {
627  *this = BooleanFunction::And(this->clone(), other.clone(), this->size()).get();
628  return *this;
629  }
630 
632  {
633  return BooleanFunction::Not(this->clone(), this->size()).get();
634  }
635 
637  {
638  return BooleanFunction::Or(this->clone(), other.clone(), this->size()).get();
639  }
640 
642  {
643  *this = BooleanFunction::Or(this->clone(), other.clone(), this->size()).get();
644  return *this;
645  }
646 
648  {
649  return BooleanFunction::Xor(this->clone(), other.clone(), this->size()).get();
650  }
651 
653  {
654  *this = BooleanFunction::Xor(this->clone(), other.clone(), this->size()).get();
655  return *this;
656  }
657 
659  {
660  return BooleanFunction::Add(this->clone(), other.clone(), this->size()).get();
661  }
662 
664  {
665  *this = BooleanFunction::Add(this->clone(), other.clone(), this->size()).get();
666  return *this;
667  }
668 
670  {
671  return BooleanFunction::Sub(this->clone(), other.clone(), this->size()).get();
672  }
673 
675  {
676  *this = BooleanFunction::Sub(this->clone(), other.clone(), this->size()).get();
677  return *this;
678  }
679 
681  {
682  return BooleanFunction::Mul(this->clone(), other.clone(), this->size()).get();
683  }
684 
686  {
687  *this = BooleanFunction::Mul(this->clone(), other.clone(), this->size()).get();
688  return *this;
689  }
690 
692  {
693  if (this->m_nodes.size() != other.m_nodes.size())
694  {
695  return false;
696  }
697 
698  for (auto i = 0ul; i < this->m_nodes.size(); i++)
699  {
700  if (this->m_nodes[i] != other.m_nodes[i])
701  {
702  return false;
703  }
704  }
705  return true;
706  }
707 
709  {
710  return !(*this == other);
711  }
712 
714  {
715  if (this->m_nodes.size() < other.m_nodes.size())
716  {
717  return true;
718  }
719  if (this->m_nodes.size() > other.m_nodes.size())
720  {
721  return false;
722  }
723 
724  return this->to_string_in_reverse_polish_notation() < other.to_string_in_reverse_polish_notation();
725  }
726 
728  {
729  return this->m_nodes.empty();
730  }
731 
733  {
734  auto function = BooleanFunction();
735  function.m_nodes.reserve(this->m_nodes.size());
736 
737  for (const auto& node : this->m_nodes)
738  {
739  function.m_nodes.emplace_back(node);
740  }
741 
742  return function;
743  }
744 
746  {
747  return this->get_top_level_node().size;
748  }
749 
751  {
752  return (this->is_empty()) ? false : this->get_top_level_node().is(type);
753  }
754 
756  {
757  return (this->is_empty()) ? false : this->get_top_level_node().is_variable();
758  }
759 
760  bool BooleanFunction::has_variable_name(const std::string& variable_name) const
761  {
762  return (this->is_empty()) ? false : this->get_top_level_node().has_variable_name(variable_name);
763  }
764 
766  {
767  if (this->is_empty())
768  {
769  return ERR("Boolean function is empty");
770  }
771 
772  return this->get_top_level_node().get_variable_name();
773  }
774 
776  {
777  return (this->is_empty()) ? false : this->get_top_level_node().is_constant();
778  }
779 
780  bool BooleanFunction::has_constant_value(const std::vector<Value>& value) const
781  {
782  return (this->is_empty()) ? false : this->get_top_level_node().has_constant_value(value);
783  }
784 
786  {
787  return (this->is_empty()) ? false : this->get_top_level_node().has_constant_value(value);
788  }
789 
791  {
792  if (this->is_empty())
793  {
794  return ERR("Boolean function is empty");
795  }
796 
797  return this->get_top_level_node().get_constant_value();
798  }
799 
801  {
802  if (this->is_empty())
803  {
804  return ERR("Boolean function is empty");
805  }
806 
808  }
809 
811  {
812  return (this->is_empty()) ? false : this->get_top_level_node().is_index();
813  }
814 
816  {
817  return (this->is_empty()) ? false : this->get_top_level_node().has_index_value(value);
818  }
819 
821  {
822  if (this->is_empty())
823  {
824  return ERR("Boolean function is empty");
825  }
826 
827  return this->get_top_level_node().get_index_value();
828  }
829 
831  {
832  return this->m_nodes.back();
833  }
834 
836  {
837  return this->m_nodes.size();
838  }
839 
840  const std::vector<BooleanFunction::Node>& BooleanFunction::get_nodes() const
841  {
842  return this->m_nodes;
843  }
844 
845  std::vector<BooleanFunction> BooleanFunction::get_parameters() const
846  {
853 
854  auto coverage = this->compute_node_coverage();
855  switch (this->get_top_level_node().get_arity())
856  {
857  case 0: {
858  return {};
859  }
860  case 1: {
861  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.end() - 1}))};
862  }
863  case 2: {
864  auto index = this->length() - coverage[this->length() - 2] - 1;
865 
866  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.begin() + index})),
867  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index, this->m_nodes.end() - 1}))};
868  }
869  case 3: {
870  auto index0 = this->length() - coverage[this->length() - 3] - coverage[this->length() - 2] - 1;
871  auto index1 = this->length() - coverage[this->length() - 2] - 1;
872 
873  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.begin() + index0})),
874  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index0, this->m_nodes.begin() + index1})),
875  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index1, this->m_nodes.end() - 1}))};
876  }
877 
878  default:
879  assert(false && "not implemented reached.");
880  }
881 
882  return {};
883  }
884 
885  std::set<std::string> BooleanFunction::get_variable_names() const
886  {
887  auto variable_names = std::set<std::string>();
888  for (const auto& node : this->m_nodes)
889  {
890  if (node.is_variable())
891  {
892  variable_names.insert(node.variable);
893  }
894  }
895  return variable_names;
896  }
897 
898  Result<std::string> BooleanFunction::default_printer(const BooleanFunction::Node& node, std::vector<std::string>&& operands)
899  {
900  if (node.get_arity() != operands.size())
901  {
902  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()));
903  }
904 
905  switch (node.type)
906  {
910  return OK(node.to_string());
911 
913  return OK("(" + operands[0] + " & " + operands[1] + ")");
915  return OK("(! " + operands[0] + ")");
917  return OK("(" + operands[0] + " | " + operands[1] + ")");
919  return OK("(" + operands[0] + " ^ " + operands[1] + ")");
920 
922  return OK("(" + operands[0] + " + " + operands[1] + ")");
924  return OK("(" + operands[0] + " - " + operands[1] + ")");
926  return OK("(" + operands[0] + " * " + operands[1] + ")");
928  return OK("(" + operands[0] + " /s " + operands[1] + ")");
930  return OK("(" + operands[0] + " / " + operands[1] + ")");
932  return OK("(" + operands[0] + " \%s " + operands[1] + ")");
934  return OK("(" + operands[0] + " \% " + operands[1] + ")");
935 
937  return OK("(" + operands[0] + " ++ " + operands[1] + ")");
939  return OK("Slice(" + operands[0] + ", " + operands[1] + ", " + operands[2] + ")");
941  return OK("Zext(" + operands[0] + ", " + operands[1] + ")");
943  return OK("Sext(" + operands[0] + ", " + operands[1] + ")");
944 
946  return OK("(" + operands[0] + " << " + operands[1] + ")");
948  return OK("(" + operands[0] + " >> " + operands[1] + ")");
950  return OK("(" + operands[0] + " >>a " + operands[1] + ")");
952  return OK("(" + operands[0] + " <<r " + operands[1] + ")");
954  return OK("(" + operands[0] + " >>r " + operands[1] + ")");
955 
957  return OK("(" + operands[0] + " == " + operands[1] + ")");
959  return OK("(" + operands[0] + " <s " + operands[1] + ")");
961  return OK("(" + operands[0] + " <=s " + operands[1] + ")");
963  return OK("(" + operands[0] + " < " + operands[1] + ")");
965  return OK("(" + operands[0] + " <= " + operands[1] + ")");
967  return OK("Ite(" + operands[0] + ", " + operands[1] + ", " + operands[2] + ")");
968 
969  default:
970  return ERR("could not print Boolean function: unsupported node type '" + std::to_string(node.type) + "'");
971  }
972  }
973 
974  Result<std::string> BooleanFunction::algebraic_printer(const BooleanFunction::Node& node, std::vector<std::string>&& operands)
975  {
976  if (node.get_arity() != operands.size())
977  {
978  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()));
979  }
980 
981  switch (node.type)
982  {
985  return OK(node.to_string());
986 
988  return OK("CONST" + std::string(node.has_constant_value(0) ? "0" : "1"));
989 
991  return OK("(" + operands[0] + "*" + operands[1] + ")");
993  return OK("(! " + operands[0] + ")");
995  return OK("(" + operands[0] + "+" + operands[1] + ")");
996 
997  default:
998  return ERR("could not print Boolean function: unsupported node type '" + std::to_string(node.type) + "'");
999  }
1000  }
1001 
1002  std::string BooleanFunction::to_string(std::function<Result<std::string>(const BooleanFunction::Node& node, std::vector<std::string>&& operands)>&& printer) const
1003  {
1004  // (1) early termination in case the Boolean function is empty
1005  if (this->m_nodes.empty())
1006  {
1007  return "<empty>";
1008  }
1009 
1010  // (2) iterate the list of nodes and setup string from leafs to root
1011  std::vector<std::string> stack;
1012  for (const auto& node : this->m_nodes)
1013  {
1014  std::vector<std::string> operands;
1015 
1016  if (stack.size() < node.get_arity())
1017  {
1018  // 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());
1019  return "";
1020  }
1021 
1022  std::move(stack.end() - static_cast<u64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1023  stack.erase(stack.end() - static_cast<u64>(node.get_arity()), stack.end());
1024 
1025  if (auto res = printer(node, std::move(operands)); res.is_ok())
1026  {
1027  stack.emplace_back(res.get());
1028  }
1029  else
1030  {
1031  log_error("netlist", "Cannot translate BooleanFunction::Node '{}' to a string: {}.", node.to_string(), res.get_error().get());
1032  return "";
1033  }
1034  }
1035 
1036  switch (stack.size())
1037  {
1038  case 1:
1039  return stack.back();
1040  default: {
1041  // log_error("netlist", "Cannot translate BooleanFunction (= imbalanced stack with {} remaining parts).", stack.size());
1042  return "";
1043  }
1044  }
1045  }
1046 
1048  {
1051 
1052  static const std::vector<std::tuple<ParserType, std::function<Result<std::vector<Token>>(const std::string&)>>> parsers = {
1055  };
1056 
1057  for (const auto& [parser_type, parser] : parsers)
1058  {
1059  auto tokens = parser(expression);
1060  // (1) skip if parser cannot translate to tokens
1061  if (tokens.is_error())
1062  {
1063  continue;
1064  }
1065 
1066  // (2) skip if cannot translate to valid reverse-polish notation
1067  tokens = BooleanFunctionParser::reverse_polish_notation(tokens.get(), expression, parser_type);
1068  if (tokens.is_error())
1069  {
1070  continue;
1071  }
1072  // (3) skip if reverse-polish notation tokens are no valid Boolean function
1073  auto function = BooleanFunctionParser::translate(tokens.get(), expression);
1074  if (function.is_error())
1075  {
1076  continue;
1077  }
1078  return function;
1079  }
1080  return ERR("could not parse Boolean function from string: no parser available for '" + expression + "'");
1081  }
1082 
1084  {
1085  auto simplified = Simplification::local_simplification(*this).map<BooleanFunction>([](const auto& s) { return Simplification::abc_simplification(s); }).map<BooleanFunction>([](const auto& s) {
1087  });
1088 
1089  return (simplified.is_ok()) ? simplified.get() : this->clone();
1090  }
1091 
1093  {
1094  auto simplified = Simplification::local_simplification(*this);
1095 
1096  return (simplified.is_ok()) ? simplified.get() : this->clone();
1097  }
1098 
1099  BooleanFunction BooleanFunction::substitute(const std::string& old_variable_name, const std::string& new_variable_name) const
1100  {
1101  auto function = this->clone();
1102  for (auto i = 0u; i < this->m_nodes.size(); i++)
1103  {
1104  if (this->m_nodes[i].has_variable_name(old_variable_name))
1105  {
1106  function.m_nodes[i] = Node::Variable(new_variable_name, this->m_nodes[i].size);
1107  }
1108  }
1109 
1110  return function;
1111  }
1112 
1113  Result<BooleanFunction> BooleanFunction::substitute(const std::string& name, const BooleanFunction& replacement) const
1114  {
1115  // Helper function to substitute a variable with a Boolean function.
1116  auto substitute_variable = [](const auto& node, auto&& operands, auto var_name, auto repl) -> BooleanFunction {
1117  if (node.has_variable_name(var_name))
1118  {
1119  return repl.clone();
1120  }
1121  return BooleanFunction(node.clone(), std::move(operands));
1122  };
1123 
1124  std::vector<BooleanFunction> stack;
1125  for (const auto& node : this->m_nodes)
1126  {
1127  std::vector<BooleanFunction> operands;
1128  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1129  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1130 
1131  stack.emplace_back(substitute_variable(node, std::move(operands), name, replacement));
1132  }
1133 
1134  switch (stack.size())
1135  {
1136  case 1:
1137  return OK(stack.back());
1138  default:
1139  return ERR("could not replace variable '" + name + "' with Boolean function '" + replacement.to_string() + "': validation failed, the operations may be imbalanced");
1140  }
1141  }
1142 
1143  BooleanFunction BooleanFunction::substitute(const std::map<std::string, std::string>& substitutions) const
1144  {
1145  auto function = this->clone();
1146  for (auto i = 0u; i < this->m_nodes.size(); i++)
1147  {
1148  if (const auto var_name_res = this->m_nodes[i].get_variable_name(); var_name_res.is_ok())
1149  {
1150  if (const auto it = substitutions.find(var_name_res.get()); it != substitutions.end())
1151  {
1152  function.m_nodes[i] = Node::Variable(it->second, this->m_nodes[i].size);
1153  }
1154  }
1155  }
1156 
1157  return function;
1158  }
1159 
1160  Result<BooleanFunction> BooleanFunction::substitute(const std::map<std::string, BooleanFunction>& substitutions) const
1161  {
1167  auto substitute_variable = [substitutions](const auto& node, auto&& operands) -> BooleanFunction {
1168  if (node.is_variable())
1169  {
1170  if (auto repl_it = substitutions.find(node.variable); repl_it != substitutions.end())
1171  {
1172  return repl_it->second.clone();
1173  }
1174  }
1175  return BooleanFunction(node.clone(), std::move(operands));
1176  };
1177 
1178  std::vector<BooleanFunction> stack;
1179  for (const auto& node : this->m_nodes)
1180  {
1181  std::vector<BooleanFunction> operands;
1182  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1183  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1184 
1185  stack.emplace_back(substitute_variable(node, std::move(operands)));
1186  }
1187 
1188  switch (stack.size())
1189  {
1190  case 1:
1191  return OK(stack.back());
1192  default:
1193  return ERR("could not carry out multiple substitutions: validation failed, the operations may be imbalanced");
1194  }
1195  }
1196 
1197  Result<BooleanFunction::Value> BooleanFunction::evaluate(const std::unordered_map<std::string, Value>& inputs) const
1198  {
1199  // (0) workaround to preserve the API functionality
1200  if (this->m_nodes.empty())
1201  {
1202  return OK(BooleanFunction::Value::X);
1203  }
1204 
1205  // (1) validate whether the input sizes match the boolean function
1206  if (this->size() != 1)
1207  {
1208  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");
1209  }
1210 
1211  // (2) translate the input to n-bit to use the generic function
1212  auto generic_inputs = std::unordered_map<std::string, std::vector<Value>>();
1213  for (const auto& [name, value] : inputs)
1214  {
1215  generic_inputs.emplace(name, std::vector<Value>({value}));
1216  }
1217 
1218  auto value = this->evaluate(generic_inputs);
1219  if (value.is_ok())
1220  {
1221  // 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
1222  return OK(value.get()[0]);
1223  }
1224 
1225  return ERR(value.get_error());
1226  }
1227 
1228  Result<std::vector<BooleanFunction::Value>> BooleanFunction::evaluate(const std::unordered_map<std::string, std::vector<Value>>& inputs) const
1229  {
1230  // (0) workaround to preserve the API functionality
1231  if (this->m_nodes.empty())
1232  {
1233  return OK(std::vector<BooleanFunction::Value>({BooleanFunction::Value::X}));
1234  }
1235 
1236  // (1) validate whether the input sizes match the boolean function
1237  for (const auto& [name, value] : inputs)
1238  {
1239  for (const auto& node : this->m_nodes)
1240  {
1241  if (node.has_variable_name(name) && node.size != value.size())
1242  {
1243  // TODO the error message does not reflect what is being checked
1244  return ERR("could not evaluate Boolean function '" + this->to_string() + "': as the number of variables (" + std::to_string(node.size)
1245  + ") does not match the number of provided inputs (" + std::to_string(value.size()) + ")");
1246  }
1247  }
1248  }
1249 
1250  // (2) initialize the symbolic state using the input variables
1251  auto symbolic_execution = SMT::SymbolicExecution();
1252  for (const auto& [name, value] : inputs)
1253  {
1254  symbolic_execution.state.set(BooleanFunction::Var(name, value.size()), BooleanFunction::Const(value));
1255  }
1256 
1257  // (3) analyze the evaluation result and check whether the result is a
1258  // constant boolean function
1259  auto result = symbolic_execution.evaluate(*this);
1260  if (result.is_ok())
1261  {
1262  if (auto value = result.get(); value.is_constant())
1263  {
1264  return OK(value.get_top_level_node().constant);
1265  }
1266  return OK(std::vector<BooleanFunction::Value>(this->size(), BooleanFunction::Value::X));
1267  }
1268  return ERR(result.get_error());
1269  }
1270 
1271  Result<std::vector<std::vector<BooleanFunction::Value>>> BooleanFunction::compute_truth_table(const std::vector<std::string>& ordered_variables, bool remove_unknown_variables) const
1272  {
1273  auto variable_names_in_function = this->get_variable_names();
1274 
1275  // (1) check that each variable is just a single bit, otherwise we do
1276  // not generate a truth-table
1277  for (const auto& node : this->m_nodes)
1278  {
1279  if (node.is_variable() && node.size != 1)
1280  {
1281  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");
1282  }
1283  }
1284 
1285  // (2) select either parameter or the Boolean function variables
1286  auto variables = ordered_variables;
1287  if (variables.empty())
1288  {
1289  variables = std::vector<std::string>(variable_names_in_function.begin(), variable_names_in_function.end());
1290  }
1291 
1292  // (3) remove any unknown variables from the truth table
1293  if (remove_unknown_variables)
1294  {
1295  variables.erase(
1296  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(); }),
1297  variables.end());
1298  }
1299 
1300  // (4.1) check that the function is not empty, otherwise we return a
1301  // Boolean function with a truth-table with 'X' values
1302  if (this->m_nodes.empty())
1303  {
1304  return OK(std::vector<std::vector<Value>>(1, std::vector<Value>(1 << variables.size(), Value::X)));
1305  }
1306 
1307  // (4.2) safety-check in case the number of variables is too large to process
1308  if (variables.size() > 10)
1309  {
1310  return ERR("could not compute truth table for Boolean function '" + this->to_string() + "': unable to generate truth-table with more than 10 variables");
1311  }
1312 
1313  std::vector<std::vector<Value>> truth_table(this->size(), std::vector<Value>(1 << variables.size(), Value::ZERO));
1314 
1315  // (5) iterate the truth-table rows and set each column accordingly
1316  for (auto value = 0u; value < ((u32)1 << variables.size()); value++)
1317  {
1318  std::unordered_map<std::string, std::vector<Value>> input;
1319  auto tmp = value;
1320  for (const auto& variable : variables)
1321  {
1322  input[variable] = ((tmp & 1) == 0) ? std::vector<Value>({Value::ZERO}) : std::vector<Value>({Value::ONE});
1323  tmp >>= 1;
1324  }
1325  auto result = this->evaluate(input);
1326  if (result.is_error())
1327  {
1328  return ERR(result.get_error());
1329  }
1330  auto output = result.get();
1331  for (auto index = 0u; index < truth_table.size(); index++)
1332  {
1333  truth_table[index][value] = output[index];
1334  }
1335  }
1336 
1337  return OK(truth_table);
1338  }
1339 
1340  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
1341  {
1342  std::vector<std::string> inputs;
1343  auto inputs_set = this->get_variable_names();
1344  if (ordered_inputs.empty())
1345  {
1346  inputs = std::vector<std::string>(inputs_set.begin(), inputs_set.end());
1347  }
1348  else
1349  {
1350  inputs = ordered_inputs;
1351  }
1352 
1353  if (remove_unknown_inputs)
1354  {
1355  inputs.erase(std::remove_if(inputs.begin(), inputs.end(), [&inputs_set](const auto& s) { return inputs_set.find(s) == inputs_set.end(); }), inputs.end());
1356  }
1357 
1358  const auto res = compute_truth_table(inputs, false);
1359  if (res.is_error())
1360  {
1361  return ERR_APPEND(res.get_error(), "could not print truth table for Boolean function '" + this->to_string() + "': unable to compute truth table");
1362  }
1363  const auto truth_table = res.get();
1364 
1365  std::stringstream str("");
1366 
1367  u32 num_inputs = inputs.size();
1368  u32 num_outputs = truth_table.size();
1369 
1370  // table headers
1371  std::vector<u32> in_widths;
1372  for (const auto& var : inputs)
1373  {
1374  in_widths.push_back(var.size());
1375  str << " " << var << " |";
1376  }
1377 
1378  std::vector<u32> out_widths;
1379  if (function_name.empty())
1380  {
1381  function_name = "O";
1382  }
1383  if (num_outputs == 1)
1384  {
1385  str << "| " << function_name << " ";
1386  out_widths.push_back(function_name.size());
1387  }
1388  else
1389  {
1390  for (u32 i = 0; i < num_outputs; i++)
1391  {
1392  std::string var = function_name + "(" + std::to_string(i) + ")";
1393  str << "| " << var << " ";
1394  out_widths.push_back(var.size());
1395  }
1396  }
1397  str << std::endl;
1398 
1399  // rule below headers
1400  for (u32 i = 0; i < num_inputs; i++)
1401  {
1402  str << std::setw(in_widths.at(i) + 3) << std::setfill('-') << "+";
1403  }
1404  for (u32 i = 0; i < num_outputs; i++)
1405  {
1406  str << "+" << std::setw(out_widths.at(i) + 2) << std::setfill('-') << "-";
1407  }
1408  str << std::endl;
1409 
1410  // table values
1411  for (u32 i = 0; i < (u32)(1 << num_inputs); i++)
1412  {
1413  for (u32 j = 0; j < num_inputs; j++)
1414  {
1415  str << " " << std::left << std::setw(in_widths.at(j)) << std::setfill(' ') << ((i >> j) & 1) << " |";
1416  }
1417 
1418  for (u32 k = 0; k < num_outputs; k++)
1419  {
1420  str << "| " << std::left << std::setw(out_widths.at(k)) << std::setfill(' ') << truth_table.at(k).at(i) << " ";
1421  }
1422  str << std::endl;
1423  }
1424  return OK(str.str());
1425  }
1426 
1427  z3::expr BooleanFunction::to_z3(z3::context& context, const std::map<std::string, z3::expr>& var2expr) const
1428  {
1435  auto reduce_to_z3 = [&context, &var2expr](const auto& node, auto&& p) -> std::tuple<bool, z3::expr> {
1436  if (node.get_arity() != p.size())
1437  {
1438  return {false, z3::expr(context)};
1439  }
1440 
1441  switch (node.type)
1442  {
1444  return {true, context.bv_val(node.index, node.size)};
1446  // since our constants are defined as arbitrary bit-vectors,
1447  // we have to concat each bit just to be on the safe side
1448  auto constant = context.bv_val(node.constant.front(), 1);
1449  for (u32 i = 1; i < node.constant.size(); i++)
1450  {
1451  const auto bit = node.constant.at(i);
1452  constant = z3::concat(context.bv_val(bit, 1), constant);
1453  }
1454  return {true, constant};
1455  }
1457  if (auto it = var2expr.find(node.variable); it != var2expr.end())
1458  {
1459  return {true, it->second};
1460  }
1461  return {true, context.bv_const(node.variable.c_str(), node.size)};
1462  }
1463 
1465  return {true, p[0] & p[1]};
1467  return {true, p[0] | p[1]};
1469  return {true, ~p[0]};
1471  return {true, p[0] ^ p[1]};
1473  return {true, p[0].extract(p[2].get_numeral_uint(), p[1].get_numeral_uint())};
1475  return {true, z3::concat(p[0], p[1])};
1477  return {true, z3::sext(p[0], p[1].get_numeral_uint())};
1478 
1479  default:
1480  log_error("netlist", "Not implemented reached for nodetype {} in z3 conversion", node.type);
1481  return {false, z3::expr(context)};
1482  }
1483  };
1484 
1485  std::vector<z3::expr> stack;
1486  for (const auto& node : this->m_nodes)
1487  {
1488  std::vector<z3::expr> operands;
1489  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1490  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1491 
1492  if (auto [ok, reduction] = reduce_to_z3(node, std::move(operands)); ok)
1493  {
1494  stack.emplace_back(reduction);
1495  }
1496  else
1497  {
1498  return z3::expr(context);
1499  }
1500  }
1501 
1502  switch (stack.size())
1503  {
1504  case 1:
1505  return stack.back();
1506  default:
1507  return z3::expr(context);
1508  }
1509  }
1510 
1511  BooleanFunction::BooleanFunction(std::vector<BooleanFunction::Node>&& nodes) : m_nodes(nodes)
1512  {
1513  }
1514 
1515  BooleanFunction::BooleanFunction(BooleanFunction::Node&& node, std::vector<BooleanFunction>&& p) : BooleanFunction()
1516  {
1517  auto size = 1;
1518  for (const auto& parameter : p)
1519  {
1520  size += parameter.size();
1521  }
1522  this->m_nodes.reserve(size);
1523 
1524  for (auto&& parameter : p)
1525  {
1526  this->m_nodes.insert(this->m_nodes.end(), parameter.m_nodes.begin(), parameter.m_nodes.end());
1527  }
1528  this->m_nodes.emplace_back(node);
1529  }
1530 
1531  std::string BooleanFunction::to_string_in_reverse_polish_notation() const
1532  {
1533  std::string s;
1534  for (const auto& node : this->m_nodes)
1535  {
1536  s += node.to_string() + " ";
1537  }
1538  return s;
1539  }
1540 
1541  Result<BooleanFunction> BooleanFunction::validate(BooleanFunction&& function)
1542  {
1547  if (auto coverage = function.compute_node_coverage(); coverage.back() != function.length())
1548  {
1549  auto str = function.to_string_in_reverse_polish_notation();
1550  return ERR("could not validate '" + str + "': imbalanced function with coverage '" + std::to_string(coverage.back()) + " != " + std::to_string(function.length()));
1551  }
1552 
1553  return OK(std::move(function));
1554  }
1555 
1556  std::vector<u32> BooleanFunction::compute_node_coverage() const
1557  {
1558  auto coverage = std::vector<u32>(this->m_nodes.size(), (u32)-1);
1559 
1565  auto get = [](const auto& cov, size_t index) -> u32 { return (index < cov.size()) ? cov[index] : -1; };
1566 
1574  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; };
1575 
1576  for (auto i = 0ul; i < this->m_nodes.size(); i++)
1577  {
1578  auto arity = this->m_nodes[i].get_arity();
1579 
1580  switch (arity)
1581  {
1582  case 0: {
1583  set(coverage, i);
1584  break;
1585  }
1586  case 1: {
1587  auto x = get(coverage, i - 1);
1588  set(coverage, i, x);
1589  break;
1590  }
1591  case 2: {
1592  auto x = get(coverage, i - 1);
1593  auto y = get(coverage, i - 1 - x);
1594  set(coverage, i, x, y);
1595  break;
1596  }
1597  case 3: {
1598  auto x = get(coverage, i - 1);
1599  auto y = get(coverage, i - 1 - x);
1600  auto z = get(coverage, i - 1 - x - y);
1601  set(coverage, i, x, y, z);
1602  break;
1603  }
1604  }
1605  }
1606 
1607  return coverage;
1608  }
1609 
1611  {
1612  return Node(_type, _size, {}, {}, {});
1613  }
1614 
1615  BooleanFunction::Node BooleanFunction::Node::Constant(const std::vector<BooleanFunction::Value> _constant)
1616  {
1617  return Node(NodeType::Constant, _constant.size(), _constant, {}, {});
1618  }
1619 
1621  {
1622  return Node(NodeType::Index, _size, {}, _index, {});
1623  }
1624 
1625  BooleanFunction::Node BooleanFunction::Node::Variable(const std::string _variable, u16 _size)
1626  {
1627  return Node(NodeType::Variable, _size, {}, {}, _variable);
1628  }
1629 
1630  bool BooleanFunction::Node::operator==(const Node& other) const
1631  {
1632  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);
1633  }
1634 
1635  bool BooleanFunction::Node::operator!=(const Node& other) const
1636  {
1637  return !(*this == other);
1638  }
1639 
1640  bool BooleanFunction::Node::operator<(const Node& other) const
1641  {
1642  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);
1643  }
1644 
1646  {
1647  return Node(this->type, this->size, this->constant, this->index, this->variable);
1648  }
1649 
1651  {
1652  switch (this->type)
1653  {
1654  case NodeType::Constant: {
1655  std::string str;
1656  for (const auto& value : this->constant)
1657  {
1658  str = enum_to_string(value) + str;
1659  }
1660  return "0b" + str;
1661  }
1662 
1663  case NodeType::Index:
1664  return std::to_string(this->index);
1665  case NodeType::Variable:
1666  return this->variable;
1667 
1668  case NodeType::And:
1669  return "&";
1670  case NodeType::Or:
1671  return "|";
1672  case NodeType::Not:
1673  return "~";
1674  case NodeType::Xor:
1675  return "^";
1676 
1677  case NodeType::Add:
1678  return "+";
1679  case NodeType::Sub:
1680  return "-";
1681  case NodeType::Mul:
1682  return "*";
1683  case NodeType::Sdiv:
1684  return "/s";
1685  case NodeType::Udiv:
1686  return "/";
1687  case NodeType::Srem:
1688  return "\%s";
1689  case NodeType::Urem:
1690  return "\%";
1691 
1692  case NodeType::Concat:
1693  return "++";
1694  case NodeType::Slice:
1695  return "Slice";
1696  case NodeType::Zext:
1697  return "Zext";
1698  case NodeType::Sext:
1699  return "Sext";
1700 
1701  case NodeType::Shl:
1702  return "<<";
1703  case NodeType::Lshr:
1704  return ">>";
1705  case NodeType::Ashr:
1706  return ">>a";
1707  case NodeType::Rol:
1708  return "<<r";
1709  case NodeType::Ror:
1710  return ">>r";
1711 
1712  case NodeType::Eq:
1713  return "==";
1714  case NodeType::Sle:
1715  return "<=s";
1716  case NodeType::Slt:
1717  return "<s";
1718  case NodeType::Ule:
1719  return "<=";
1720  case NodeType::Ult:
1721  return "<";
1722  case NodeType::Ite:
1723  return "Ite";
1724 
1725  default:
1726  return "unsupported node type '" + std::to_string(this->type) + "'.";
1727  }
1728  }
1729 
1731  {
1733  }
1734 
1736  {
1737  static const std::map<u16, u16> type2arity = {
1746  };
1747 
1748  return type2arity.at(type);
1749  }
1750 
1751  bool BooleanFunction::Node::is(u16 _type) const
1752  {
1753  return this->type == _type;
1754  }
1755 
1757  {
1759  }
1760 
1761  bool BooleanFunction::Node::has_constant_value(const std::vector<Value>& value) const
1762  {
1763  return this->is_constant() && (this->constant == value);
1764  }
1765 
1767  {
1768  if (!this->is_constant())
1769  {
1770  return false;
1771  }
1772 
1773  auto bv_value = std::vector<BooleanFunction::Value>({});
1774  bv_value.reserve(this->size);
1775  for (auto i = 0u; i < this->constant.size(); i++)
1776  {
1777  bv_value.emplace_back((value & (1 << i)) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO);
1778  }
1779  return this->constant == bv_value;
1780  }
1781 
1783  {
1784  if (!this->is_constant())
1785  {
1786  return ERR("Node is not a constant");
1787  }
1788 
1789  return OK(this->constant);
1790  }
1791 
1793  {
1794  if (!this->is_constant())
1795  {
1796  return ERR("Node is not a constant");
1797  }
1798 
1799  if (this->size > 64)
1800  {
1801  return ERR("Node constant has size > 64");
1802  }
1803 
1804  if (std::any_of(this->constant.begin(), this->constant.end(), [](auto v) { return v != BooleanFunction::Value::ONE && v != BooleanFunction::Value::ZERO; }))
1805  {
1806  return ERR("Node constant is undefined or high-impedance");
1807  }
1808 
1809  u64 val = 0;
1810  for (auto it = this->constant.rbegin(); it != this->constant.rend(); it++)
1811  {
1812  val <<= 1;
1813  val |= *it;
1814  }
1815 
1816  return OK(val);
1817  }
1818 
1820  {
1821  return this->is(BooleanFunction::NodeType::Index);
1822  }
1823 
1825  {
1826  return this->is_index() && (this->index == value);
1827  }
1828 
1830  {
1831  if (!this->is_index())
1832  {
1833  return ERR("Node is not an index");
1834  }
1835 
1836  return OK(this->index);
1837  }
1838 
1840  {
1842  }
1843 
1844  bool BooleanFunction::Node::has_variable_name(const std::string& value) const
1845  {
1846  return this->is_variable() && (this->variable == value);
1847  }
1848 
1850  {
1851  if (!this->is_variable())
1852  {
1853  return ERR("Node is not a variable");
1854  }
1855 
1856  return OK(this->variable);
1857  }
1858 
1860  {
1861  return !this->is_operand();
1862  }
1863 
1865  {
1866  return this->is_constant() || this->is_variable() || this->is_index();
1867  }
1868 
1870  {
1871  return (this->type == NodeType::And) || (this->type == NodeType::Or) || (this->type == NodeType::Xor) || (this->type == NodeType::Add) || (this->type == NodeType::Mul)
1872  || (this->type == NodeType::Eq);
1873  }
1874 
1875  BooleanFunction::Node::Node(u16 _type, u16 _size, std::vector<BooleanFunction::Value> _constant, u16 _index, std::string _variable)
1876  : type(_type), size(_size), constant(_constant), index(_index), variable(_variable)
1877  {
1878  }
1879 
1880 } // namespace hal
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 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
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:53
#define OK(...)
Definition: result.h:49
#define ERR_APPEND(prev_error, message)
Definition: result.h:57
parser
Definition: control.py:13
Result< std::vector< Token > > parse_with_standard_grammar(const std::string &expression)
Result< std::vector< Token > > parse_with_liberty_grammar(const std::string &expression)
ParserType
ParserType refers to the parser identifier.
Definition: parser.h:36
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< BooleanFunction > local_simplification(const BooleanFunction &function)
std::ostream & operator<<(std::ostream &os, BooleanFunction::Value v)
std::string enum_to_string(T e)
Definition: enums.h:52
quint32 u32
PinType type
std::string name
include set(SRCROOT ${CMAKE_CURRENT_SOURCE_DIR}/src) set(UIROOT $
Definition: CMakeLists.txt:45
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:62