162 lines
3.4 KiB
Python
162 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import functools
|
|
import sys
|
|
import typing
|
|
|
|
import matplotlib.pyplot as plt
|
|
import networkx as nx
|
|
|
|
input_file = sys.argv[1]
|
|
|
|
with open(input_file) as fd:
|
|
lines = [line.rstrip() for line in fd.readlines()]
|
|
|
|
gates: dict[str, tuple[str, typing.Callable, str]] = dict()
|
|
varis: dict[str, int] = dict()
|
|
funs = {
|
|
"AND": int.__and__, # orange
|
|
"OR": int.__or__, # green
|
|
"XOR": int.__xor__, # purple
|
|
}
|
|
G = nx.DiGraph()
|
|
|
|
swaps = [
|
|
("ncd", "nfj"),
|
|
("z37", "vkg"),
|
|
("z20", "cqr"),
|
|
("z15", "qnw"),
|
|
]
|
|
swapdict: dict[str, str] = dict()
|
|
for a, b in swaps:
|
|
swapdict[a] = b
|
|
swapdict[b] = a
|
|
|
|
step = False
|
|
for line in lines:
|
|
if not line:
|
|
step = True
|
|
elif step:
|
|
a, op, b, _, dest = line.split()
|
|
dest = swapdict.get(dest, dest)
|
|
fun = funs[op]
|
|
gates[dest] = (a, fun, b)
|
|
G.add_node(dest, op=op)
|
|
G.add_edge(a, dest)
|
|
G.add_edge(b, dest)
|
|
else:
|
|
dest, val = line.split(":")
|
|
varis[dest] = int(val)
|
|
|
|
|
|
def swap(a: str, b: str) -> None:
|
|
temp = gates[a]
|
|
gates[a] = gates[b]
|
|
gates[b] = temp
|
|
|
|
|
|
@functools.cache
|
|
def get_var(var: str) -> int:
|
|
if var in varis:
|
|
return varis[var]
|
|
a, fun, b = gates[var]
|
|
avar = get_var(a)
|
|
bvar = get_var(b)
|
|
return fun(avar, bvar)
|
|
|
|
|
|
all_keys = list(gates.keys()) + list(varis.keys())
|
|
all_keys.sort(reverse=True)
|
|
|
|
|
|
def get_number(prefix: str) -> int:
|
|
tot = 0
|
|
keys = [key for key in all_keys if key.startswith(prefix)]
|
|
for key in keys:
|
|
tot <<= 1
|
|
tot |= get_var(key)
|
|
return tot
|
|
|
|
|
|
X = get_number("x")
|
|
Y = get_number("y")
|
|
Z = get_number("z")
|
|
print(f"{X+Y=} = {X=} + {Y=}")
|
|
print(f" {Z=} {Z == X + Y=}")
|
|
|
|
print(",".join(sorted(swapdict.keys())))
|
|
|
|
# Viz
|
|
|
|
|
|
@functools.cache
|
|
def get_node_pos(node: str) -> tuple[float, float]:
|
|
x: float
|
|
y: float
|
|
if node.startswith("x"):
|
|
x = -int(node[1:]) * 2
|
|
y = 0
|
|
elif node.startswith("y"):
|
|
x = -int(node[1:]) * 2 - 1
|
|
y = 0
|
|
elif node.startswith("z"):
|
|
x = -int(node[1:]) * 2
|
|
y = 100
|
|
else:
|
|
a, _, b = gates[node]
|
|
ax, ay = get_node_pos(a)
|
|
bx, by = get_node_pos(b)
|
|
x = (ax + bx) / 2
|
|
y = max(ay, by) + 1
|
|
return x, y
|
|
|
|
|
|
colors = {
|
|
"AND": "orange",
|
|
"OR": "green",
|
|
"XOR": "purple",
|
|
}
|
|
node_colors = []
|
|
node_pos: dict[str, tuple[float, float]] = dict()
|
|
node_fixed: set[str] = set()
|
|
for node in G.nodes():
|
|
op = G.nodes[node].get("op")
|
|
node_colors.append(colors.get(op, "cyan" if node.startswith("x") else "blue"))
|
|
|
|
x: float
|
|
y: float
|
|
fixed = True
|
|
if node.startswith("x"):
|
|
x = -int(node[1:]) * 2
|
|
y = 0
|
|
elif node.startswith("y"):
|
|
x = -int(node[1:]) * 2 - 1
|
|
y = 0
|
|
elif node.startswith("z"):
|
|
x = -int(node[1:]) * 2
|
|
y = 50
|
|
else:
|
|
fixed = False
|
|
x = -23
|
|
y = 25
|
|
|
|
node_pos[node] = x, y
|
|
if fixed:
|
|
node_fixed.add(node)
|
|
|
|
# My own layout
|
|
for i in range(50):
|
|
for node in G.nodes():
|
|
if node in node_fixed:
|
|
continue
|
|
neighs = list(G.succ[node]) + list(G.pred[node])
|
|
x = sum(node_pos[neigh][0] for neigh in neighs) / len(neighs)
|
|
y = sum(node_pos[neigh][1] for neigh in neighs) / len(neighs)
|
|
node_pos[node] = x, y
|
|
node_fixed = set(G.nodes())
|
|
|
|
node_layout = nx.layout.spring_layout(G, pos=node_pos, fixed=node_fixed)
|
|
|
|
nx.draw(G, pos=node_layout, node_color=node_colors, with_labels=True)
|
|
plt.show()
|