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