199 lines
5.3 KiB
Python
Executable file
199 lines
5.3 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
from heapq import heappush, heappop
|
|
|
|
X = 0b01010101
|
|
Y = 0b00110011
|
|
Z = 0b00001111
|
|
M = 0b11111111
|
|
|
|
B = 1 << 3
|
|
N = 1 << B
|
|
|
|
def calc_simps(A = None):
|
|
# truthtable -> ('var', name)
|
|
# truthtable -> ('not', inner truthtable)
|
|
# truthtable -> (binary op, left truthtable, right truthtable)
|
|
lookup = dict();
|
|
|
|
# truthtable -> cost
|
|
costs = dict();
|
|
|
|
todo = set();
|
|
|
|
def initial(exp, truthtable):
|
|
lookup[truthtable] = exp;
|
|
costs[truthtable] = 0;
|
|
todo.add(truthtable);
|
|
|
|
initial(('var', 'x'), X);
|
|
initial(('var', 'y'), Y);
|
|
initial(('var', 'z'), Z);
|
|
initial(('lit', '0'), 0);
|
|
initial(('lit', '1'), M);
|
|
|
|
if (A is not None):
|
|
initial(('var', 'a'), A);
|
|
|
|
done = list();
|
|
|
|
for iteration in range(1, N + 1):
|
|
mytruthtable = min(todo, key = lambda x: (costs[x], x));
|
|
|
|
mycost = costs[mytruthtable];
|
|
|
|
todo.remove(mytruthtable);
|
|
|
|
# consider NOT:
|
|
not_cost = mycost + 1
|
|
not_truthtable = ~mytruthtable & M;
|
|
if (not_truthtable not in costs) or (not_cost < costs[not_truthtable]):
|
|
todo.add(not_truthtable);
|
|
|
|
lookup[not_truthtable] = ("not", mytruthtable);
|
|
|
|
costs[not_truthtable] = not_cost;
|
|
|
|
# consider OR:
|
|
for othertruthtable in done:
|
|
or_cost = 1 + mycost + costs[othertruthtable];
|
|
|
|
or_truthtable = mytruthtable | othertruthtable;
|
|
|
|
if (or_truthtable not in costs) or (or_cost < costs[or_truthtable]):
|
|
todo.add(or_truthtable);
|
|
|
|
lookup[or_truthtable] = ("or", mytruthtable, othertruthtable);
|
|
|
|
costs[or_truthtable] = or_cost;
|
|
|
|
# consider AND:
|
|
for othertruthtable in done:
|
|
and_cost = 1 + mycost + costs[othertruthtable];
|
|
|
|
and_truthtable = mytruthtable & othertruthtable;
|
|
|
|
if (and_truthtable not in costs) or (and_cost < costs[and_truthtable]):
|
|
todo.add(and_truthtable);
|
|
|
|
lookup[and_truthtable] = ("and", mytruthtable, othertruthtable);
|
|
|
|
costs[and_truthtable] = and_cost;
|
|
|
|
# consider XOR:
|
|
if 0:
|
|
for othertruthtable in done:
|
|
xor_cost = 1 + mycost + costs[othertruthtable];
|
|
|
|
xor_truthtable = mytruthtable ^ othertruthtable;
|
|
|
|
if (xor_truthtable not in costs) or (xor_cost < costs[xor_truthtable]):
|
|
todo.add(xor_truthtable);
|
|
|
|
lookup[xor_truthtable] = ("xor", mytruthtable, othertruthtable);
|
|
|
|
costs[xor_truthtable] = xor_cost;
|
|
|
|
done.append(mytruthtable);
|
|
|
|
assert(sorted(done) == sorted(costs));
|
|
|
|
return costs, lookup;
|
|
|
|
def etostr(lookup, expr):
|
|
match expr:
|
|
case ('lit', x):
|
|
return x;
|
|
case ('var', x):
|
|
return x;
|
|
case ('not', inner):
|
|
return "(!" + tostr(lookup, inner) + ")";
|
|
case ('or', left, right):
|
|
return "(" + tostr(lookup, left) + " || " + tostr(lookup, right) + ")";
|
|
case ('and', left, right):
|
|
return "(" + tostr(lookup, left) + " && " + tostr(lookup, right) + ")";
|
|
case ('xor', left, right):
|
|
return "(" + tostr(lookup, left) + " != " + tostr(lookup, right) + ")";
|
|
case _:
|
|
assert(not "TODO");
|
|
|
|
def tostr(lookup, truthtable):
|
|
return etostr(lookup, lookup[truthtable]);
|
|
|
|
zdcosts, zdlookup = calc_simps();
|
|
|
|
# now that we've figured out the zero-depth trees, we need to figure out which
|
|
# trees could be done faster with a variable. We need to go through every
|
|
# possible truthtable, considering them the new variable, building up a new
|
|
# table of what expressions are the cheapiest with that variable.
|
|
# Then, if that figured out for all of them, we could read down the columns
|
|
# figuring out the best answers for anything the user gives.
|
|
|
|
# truthtable -> cost
|
|
costs = zdcosts;
|
|
|
|
# truthtable -> variable truthtable
|
|
varlookup = {x: None for x in range(1 << B)};
|
|
|
|
all_costs = dict(); # variable truthtable -> truthtable -> cost
|
|
all_lookups = dict(); # variable truthtable -> truthtable -> expr
|
|
|
|
for A in range(0, 1 << B):
|
|
print(f"what if the variable was 0b{A:08b}?");
|
|
|
|
var_costs, var_lookup = calc_simps(A);
|
|
|
|
for t in range(0, 1 << B):
|
|
mycost = var_costs[t] + 1 + zdcosts[A];
|
|
|
|
if mycost < costs[t]:
|
|
varlookup[t] = A;
|
|
|
|
costs[t] = mycost;
|
|
|
|
all_costs[A] = var_costs;
|
|
|
|
all_lookups[A] = var_lookup;
|
|
|
|
for t in range(0, 1 << B):
|
|
line = f"0b{t:08b}: ";
|
|
|
|
line += f"[{costs[t]}] ";
|
|
|
|
if varlookup[t] is None:
|
|
line += tostr(zdlookup, t);
|
|
else:
|
|
line += "("
|
|
|
|
a = varlookup[t];
|
|
|
|
line += "a = " + tostr(zdlookup, a);
|
|
|
|
line += ", "
|
|
|
|
line += tostr(all_lookups[a], t);
|
|
|
|
line += ")"
|
|
|
|
line += " [before: " + tostr(zdlookup, t) + "]";
|
|
|
|
print(line);
|
|
|
|
print();
|
|
print(f"we can do anything in {max(costs.values())} operations");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|