24_logicgates.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. #!/usr/bin/env python3
  2. import sys
  3. from functools import reduce
  4. from itertools import takewhile
  5. from operator import and_, or_, xor
  6. def parse(f):
  7. initial = {line[:3]: int(line[5])
  8. for line in takewhile(lambda line: line != '\n', f)}
  9. rules = {}
  10. for line in f:
  11. left, op, right, _, out = line.split()
  12. rules[out] = {'AND': and_, 'OR': or_, 'XOR': xor}[op], left, right
  13. return initial, rules
  14. def evaluate(initial, rules):
  15. def value(name):
  16. if name in initial:
  17. return initial[name]
  18. op, left, right = rules[name]
  19. return op(value(left), value(right))
  20. return reduce(or_, (value(z) << int(z[1:]) for z in rules if z[0] == 'z'))
  21. def construct_xy(op, i):
  22. return op, 'x%02d' % i, 'y%02d' % i
  23. def construct_carry(i):
  24. carry = construct_xy(and_, i)
  25. if i == 0:
  26. return carry
  27. lhs = and_, construct_carry(i - 1), construct_xy(xor, i)
  28. return or_, lhs, construct_xy(and_, i)
  29. def construct_z(i, initial):
  30. adder = construct_xy(xor, i)
  31. if i == 0:
  32. return adder
  33. carry = construct_carry(i - 1)
  34. if 'x%02d' % i not in initial:
  35. return carry
  36. return xor, carry, adder
  37. def tostr(construct, rules):
  38. match construct:
  39. case op, left, right:
  40. op_str = {and_: '&', or_: '|', xor: '^'}[op]
  41. left, right = sorted((tostr(left, rules), tostr(right, rules)))
  42. return f'({left} {op_str} {right})'
  43. case str(node):
  44. return tostr(rules[node], rules) if node in rules else node
  45. def find_incorrect(initial, rules):
  46. def expect(name, expected):
  47. if name in rules:
  48. op, left, right = rules[name]
  49. eop, eleft, eright = expected
  50. if op != eop:
  51. return {name}
  52. (lstr, left), (rstr, right) = sorted(((tostr(left, rules), left),
  53. (tostr(right, rules), right)))
  54. if left in rules and right in rules:
  55. return expect(left, eleft) | expect(right, eright)
  56. if lstr != tostr(eleft, rules):
  57. return {left}
  58. if rstr != tostr(eright, rules):
  59. return {right}
  60. return set()
  61. return reduce(set.union, (expect(name, construct_z(int(name[1:]), initial))
  62. for name in rules if name[0] == 'z'))
  63. initial, rules = parse(sys.stdin)
  64. print(evaluate(initial, rules))
  65. print(','.join(sorted(find_incorrect(initial, rules))))