added argparse and operator selection
This commit is contained in:
parent
9ff2d22e51
commit
43c25d701f
1 changed files with 281 additions and 161 deletions
442
main.py
442
main.py
|
|
@ -1,62 +1,138 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os;
|
||||||
import readline
|
import readline
|
||||||
|
import argparse
|
||||||
import pickle;
|
import pickle;
|
||||||
|
import sys;
|
||||||
|
|
||||||
from heapq import heappush, heappop
|
def parse_args(argv):
|
||||||
|
parser = argparse.ArgumentParser(prog = '4-variable-simplifier');
|
||||||
|
|
||||||
|
parser.add_argument('-c', '--command', help = '''
|
||||||
|
Provide a boolean expression to simpliy on the command-line.
|
||||||
|
Without this option, boolean expressions are read stdin.
|
||||||
|
''')
|
||||||
|
|
||||||
|
parser.add_argument('-e', '--extended-operators', action='store_true', help='''
|
||||||
|
Use more than just the standard three boolean operators (not, or, and).
|
||||||
|
This options allows use of: nor, nand, xor, nxor, andn, orn.
|
||||||
|
''')
|
||||||
|
|
||||||
|
parser.add_argument('-o', '--custom-operators', help='''
|
||||||
|
Pick and choose which operators the simplifier can use. Comma seperated.
|
||||||
|
You can refer to them using their names or their symbols.
|
||||||
|
''')
|
||||||
|
|
||||||
|
parser.add_argument("-n", '--use-names', help='''
|
||||||
|
Print the operator's names in the expression tree, rather than the
|
||||||
|
symbols.
|
||||||
|
''')
|
||||||
|
|
||||||
|
parser.add_argument('-p', "--printout", action = 'store_true', help='''
|
||||||
|
Print out all simplified expressions for the given expressions and
|
||||||
|
quit.
|
||||||
|
''')
|
||||||
|
|
||||||
|
parser.add_argument("--simplifications-file", \
|
||||||
|
default = ".simplifications.bin", help='''
|
||||||
|
Path to pickle file that stores the cached simplifications.
|
||||||
|
''')
|
||||||
|
|
||||||
|
parser.add_argument("-y", "--yes", action = 'store_true', help='''
|
||||||
|
Do not ask before generating simplification cache.
|
||||||
|
''')
|
||||||
|
|
||||||
|
parser.add_argument("-q", "--quiet", action = 'store_true', help='''
|
||||||
|
Do not print anything but the simplification. Assumes '--yes'.
|
||||||
|
''')
|
||||||
|
|
||||||
|
parser.add_argument("-C", "--color", default = 'auto', \
|
||||||
|
choices = ["off", "on", "auto"], help='''
|
||||||
|
Select whether to use terminal colors. Default is to use if it is
|
||||||
|
supported. use '--color=on' to force it on. '--color=off' otherwise.
|
||||||
|
''')
|
||||||
|
|
||||||
|
return parser.parse_args(argv[1:]);
|
||||||
|
|
||||||
|
symbol_to_name = {
|
||||||
|
"!": "not",
|
||||||
|
"||": "or",
|
||||||
|
"&&": "and",
|
||||||
|
"!|": "nor",
|
||||||
|
"!&": "nand",
|
||||||
|
"!=": "xor",
|
||||||
|
"==": "nxor",
|
||||||
|
"|!": "orn",
|
||||||
|
"&!": "andn",
|
||||||
|
};
|
||||||
|
|
||||||
|
def determine_available_operators(args):
|
||||||
|
standard = ("!", "||", "&&");
|
||||||
|
|
||||||
|
extended = ("!|", "!&", "!=", "==", "|!", "&!");
|
||||||
|
|
||||||
|
if args.extended_operators:
|
||||||
|
return set(standard + extended);
|
||||||
|
elif args.custom_operators:
|
||||||
|
available_operators = set();
|
||||||
|
|
||||||
|
for o in args.custom_operators.split(","):
|
||||||
|
if o in symbol_to_name:
|
||||||
|
available_operators.add(o);
|
||||||
|
elif o in symbol_to_name.values():
|
||||||
|
for k, v in symbol_to_name.items():
|
||||||
|
if v == o:
|
||||||
|
available_operators.add(k);
|
||||||
|
else:
|
||||||
|
raise BaseException(f"not an operator: '{o}'");
|
||||||
|
|
||||||
|
return available_operators;
|
||||||
|
else:
|
||||||
|
return set(standard);
|
||||||
|
|
||||||
|
def pretty(exp):
|
||||||
|
match exp:
|
||||||
|
case ("literal", x):
|
||||||
|
return str(x);
|
||||||
|
|
||||||
|
case ("variable", x):
|
||||||
|
return x
|
||||||
|
|
||||||
|
case (op, inner):
|
||||||
|
return f"({op}{pretty(inner)})"
|
||||||
|
|
||||||
|
case (op, left, right):
|
||||||
|
return f"({pretty(left)} {op} {pretty(right)})"
|
||||||
|
|
||||||
|
case _:
|
||||||
|
print(exp);
|
||||||
|
assert(not "TODO");
|
||||||
|
|
||||||
W = 0b0101_0101_0101_0101
|
W = 0b0101_0101_0101_0101
|
||||||
X = 0b0011_0011_0011_0011
|
X = 0b0011_0011_0011_0011
|
||||||
Y = 0b0000_1111_0000_1111
|
Y = 0b0000_1111_0000_1111
|
||||||
Z = 0b0000_0000_1111_1111
|
Z = 0b0000_0000_1111_1111
|
||||||
|
|
||||||
MASK = 0b1111_1111_1111_1111
|
M = 0b1111_1111_1111_1111
|
||||||
|
|
||||||
def pretty(exp):
|
def calculate_simplifications(args, available_operators):
|
||||||
match exp:
|
print(f'available_operators = {available_operators}')
|
||||||
case ("literal", x):
|
|
||||||
return str(x);
|
|
||||||
case ("variable", x):
|
|
||||||
return x
|
|
||||||
case ("not", inner):
|
|
||||||
return f"(!{pretty(inner)})"
|
|
||||||
case ("or", left, right):
|
|
||||||
return f"({pretty(left)} || {pretty(right)})"
|
|
||||||
case ("nor", left, right):
|
|
||||||
return f"({pretty(left)} !| {pretty(right)})"
|
|
||||||
case ("and", left, right):
|
|
||||||
return f"({pretty(left)} && {pretty(right)})"
|
|
||||||
case ("nand", left, right):
|
|
||||||
return f"({pretty(left)} !& {pretty(right)})"
|
|
||||||
case ("less-than", left, right):
|
|
||||||
return f"({pretty(left)} < {pretty(right)})"
|
|
||||||
case ("less-than-equal", left, right):
|
|
||||||
return f"({pretty(left)} <= {pretty(right)})"
|
|
||||||
case ("greater-than", left, right):
|
|
||||||
return f"({pretty(left)} > {pretty(right)})"
|
|
||||||
case ("greater-than-equal", left, right):
|
|
||||||
return f"({pretty(left)} >= {pretty(right)})"
|
|
||||||
case ("equal", left, right):
|
|
||||||
return f"({pretty(left)} == {pretty(right)})"
|
|
||||||
case ("not-equal", left, right):
|
|
||||||
return f"({pretty(left)} != {pretty(right)})"
|
|
||||||
case _:
|
|
||||||
print(exp);
|
|
||||||
assert(not "TODO");
|
|
||||||
|
|
||||||
def calculate_simplifications():
|
|
||||||
lookup = dict() # truthtable -> expression
|
lookup = dict() # truthtable -> expression
|
||||||
|
|
||||||
costs = dict() # truthtable -> cost
|
costs = dict() # truthtable -> cost
|
||||||
|
|
||||||
todo = [set() for _ in range(100)] # indexed by cost, set of truthtables.
|
todo = [set() for _ in range(100)] # indexed by cost, set of truthtables.
|
||||||
|
|
||||||
|
todo_count = [0]; # I have to wrap in an array because python is dumb.
|
||||||
|
|
||||||
def prequeue(truthtable, expression, cost):
|
def prequeue(truthtable, expression, cost):
|
||||||
lookup[truthtable] = expression;
|
lookup[truthtable] = expression;
|
||||||
costs[truthtable] = cost;
|
costs[truthtable] = cost;
|
||||||
todo[cost].add(truthtable);
|
todo[cost].add(truthtable);
|
||||||
|
todo_count[0] += 1;
|
||||||
|
|
||||||
# literals:
|
# literals:
|
||||||
prequeue(0b1111_1111_1111_1111, ("literal", 1), cost = 1);
|
prequeue(0b1111_1111_1111_1111, ("literal", 1), cost = 1);
|
||||||
prequeue(0b0000_0000_0000_0000, ("literal", 0), cost = 1);
|
prequeue(0b0000_0000_0000_0000, ("literal", 0), cost = 1);
|
||||||
|
|
@ -66,9 +142,9 @@ def calculate_simplifications():
|
||||||
prequeue(X, ("variable", "x"), cost = 0);
|
prequeue(X, ("variable", "x"), cost = 0);
|
||||||
prequeue(Y, ("variable", "y"), cost = 0);
|
prequeue(Y, ("variable", "y"), cost = 0);
|
||||||
prequeue(Z, ("variable", "z"), cost = 0);
|
prequeue(Z, ("variable", "z"), cost = 0);
|
||||||
|
|
||||||
# completely unnecessary critera, alphabetical variables is more
|
# Completely unnecessary critera, alphabetical variables is more
|
||||||
# aesthetically pleasing, says Benson.
|
# aesthetically pleasing. Thanks Benson.
|
||||||
def is_aesthetically_better(this, that):
|
def is_aesthetically_better(this, that):
|
||||||
def extract_variables(exp):
|
def extract_variables(exp):
|
||||||
match exp:
|
match exp:
|
||||||
|
|
@ -76,7 +152,7 @@ def calculate_simplifications():
|
||||||
return ();
|
return ();
|
||||||
case ("variable", x):
|
case ("variable", x):
|
||||||
return (x, )
|
return (x, )
|
||||||
case ("not", inner):
|
case (_, inner):
|
||||||
return extract_variables(inner);
|
return extract_variables(inner);
|
||||||
case (_, left, right):
|
case (_, left, right):
|
||||||
return extract_variables(left) + extract_variables(right);
|
return extract_variables(left) + extract_variables(right);
|
||||||
|
|
@ -87,62 +163,66 @@ def calculate_simplifications():
|
||||||
return extract_variables(this) < extract_variables(that);
|
return extract_variables(this) < extract_variables(that);
|
||||||
|
|
||||||
unary_operators = {
|
unary_operators = {
|
||||||
'not': lambda x: ~x,
|
'!': lambda x: ~x,
|
||||||
}
|
}
|
||||||
|
|
||||||
binary_operators = {
|
binary_operators = {
|
||||||
'or': lambda x, y: x | y,
|
'||': lambda x, y: (x | y) & M,
|
||||||
'and': lambda x, y: x & y,
|
'&&': lambda x, y: (x & y) & M,
|
||||||
|
|
||||||
'nor': lambda x, y: ~(x | y) & MASK,
|
'!|': lambda x, y: ~(x | y) & M,
|
||||||
'nand': lambda x, y: ~(x & y) & MASK,
|
'!&': lambda x, y: ~(x & y) & M,
|
||||||
|
|
||||||
'less-than': lambda x, y: ~x & y,
|
'&!': lambda x, y: (~x & y) & M,
|
||||||
'less-than-equal': lambda x, y: ~x | y,
|
'|!': lambda x, y: (~x | y) & M,
|
||||||
|
|
||||||
'greater-than': lambda x, y: x & ~y,
|
'!=': lambda x, y: (x ^ y) & M,
|
||||||
'greater-than-equal': lambda x, y: x | ~y,
|
'==': lambda x, y: (~(x ^ y)) & M,
|
||||||
|
|
||||||
'equal': lambda x, y: ~(x ^ y),
|
|
||||||
'not-equal': lambda x, y: (x ^ y),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def print_status():
|
||||||
|
numerator = 65536 - todo_count[0];
|
||||||
|
denominator = 65536
|
||||||
|
|
||||||
|
line = f'{numerator} of 65536';
|
||||||
|
line += f' ({numerator / denominator * 100:.2f}%):'
|
||||||
|
line += f' [{my_cost}]';
|
||||||
|
line += f' {pretty(my_expression)}';
|
||||||
|
|
||||||
|
print(line);
|
||||||
|
|
||||||
min_cost = 0;
|
min_cost = 0;
|
||||||
|
|
||||||
while sum(len(x) for x in todo):
|
while todo_count[0]:
|
||||||
truthtables = todo[min_cost];
|
truthtables = todo[min_cost];
|
||||||
|
|
||||||
if not truthtables:
|
if not truthtables:
|
||||||
min_cost += 1;
|
min_cost += 1;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
todo_count = sum(len(x) for x in todo);
|
assert(todo_count[0] <= 65536);
|
||||||
|
|
||||||
assert(todo_count <= 65536);
|
|
||||||
|
|
||||||
my_truthtable = min(truthtables);
|
my_truthtable = min(truthtables);
|
||||||
truthtables.discard(my_truthtable);
|
truthtables.discard(my_truthtable);
|
||||||
my_cost = min_cost;
|
my_cost = min_cost;
|
||||||
my_expression = lookup[my_truthtable];
|
my_expression = lookup[my_truthtable];
|
||||||
|
todo_count[0] -= 1;
|
||||||
# print(f'{65536 - todo_count} of 65536 ({(65536 - todo_count) / 65536 * 100:.2f}%)');
|
|
||||||
|
if not args.quiet:
|
||||||
print(f'{65536 - todo_count} of 65536 ({(65536 - todo_count) / 65536 * 100:.2f}%): [{my_cost}] {pretty(my_expression)}');
|
print_status();
|
||||||
|
|
||||||
# print([len(x) for x in todo])
|
|
||||||
|
|
||||||
# print(f'{len(todo)}; {my_cost}; {len(lookup)}')
|
|
||||||
|
|
||||||
# print(f'{my_truthtable:016b}: {my_expression}')
|
|
||||||
# print(f'{my_truthtable:016b}: {pretty(my_expression)}')
|
|
||||||
|
|
||||||
def consider(new_truthtable, new_expression, new_cost):
|
def consider(new_truthtable, new_expression, new_cost):
|
||||||
if new_truthtable not in costs:
|
if new_truthtable not in costs:
|
||||||
todo[new_cost].add(new_truthtable);
|
todo[new_cost].add(new_truthtable);
|
||||||
|
|
||||||
costs[new_truthtable] = new_cost
|
costs[new_truthtable] = new_cost
|
||||||
lookup[new_truthtable] = new_expression
|
lookup[new_truthtable] = new_expression
|
||||||
elif new_cost < costs[new_truthtable] or (new_cost == costs[new_truthtable] and is_aesthetically_better(new_expression, lookup[new_truthtable])):
|
|
||||||
|
todo_count[0] += 1;
|
||||||
|
elif new_cost < costs[new_truthtable] or \
|
||||||
|
(new_cost == costs[new_truthtable] and \
|
||||||
|
is_aesthetically_better(new_expression, \
|
||||||
|
lookup[new_truthtable])):
|
||||||
current_cost = costs[new_truthtable];
|
current_cost = costs[new_truthtable];
|
||||||
|
|
||||||
assert(new_cost >= min_cost);
|
assert(new_cost >= min_cost);
|
||||||
|
|
@ -155,63 +235,76 @@ def calculate_simplifications():
|
||||||
|
|
||||||
# consider unary operators:
|
# consider unary operators:
|
||||||
for name, function in sorted(unary_operators.items()):
|
for name, function in sorted(unary_operators.items()):
|
||||||
unary_truthtable = function(my_truthtable) & MASK;
|
if name in available_operators:
|
||||||
unary_expression = (name, my_expression);
|
unary_truthtable = function(my_truthtable) & M;
|
||||||
unary_cost = my_cost + 1;
|
unary_expression = (name, my_expression);
|
||||||
|
unary_cost = my_cost + 1;
|
||||||
|
|
||||||
consider(unary_truthtable, unary_expression, unary_cost);
|
consider(unary_truthtable, unary_expression, unary_cost);
|
||||||
|
|
||||||
# consider binary operators:
|
# consider binary operators:
|
||||||
for name, function in sorted(binary_operators.items()):
|
for name, function in sorted(binary_operators.items()):
|
||||||
for other_truthtable, other_expression in sorted(lookup.items()):
|
if name in available_operators:
|
||||||
# x + y
|
for other_truthtable, other_expression in sorted(lookup.items()):
|
||||||
binary_truthtable = function(my_truthtable, other_truthtable);
|
# x + y
|
||||||
binary_truthtable = binary_truthtable & MASK
|
binary_truthtable = function(my_truthtable, other_truthtable);
|
||||||
binary_expression = (name, my_expression, other_expression);
|
binary_truthtable = binary_truthtable & M
|
||||||
binary_cost = my_cost + 1 + costs[other_truthtable];
|
binary_expression = (name, my_expression, other_expression);
|
||||||
|
binary_cost = my_cost + 1 + costs[other_truthtable];
|
||||||
|
|
||||||
consider(binary_truthtable, binary_expression, binary_cost);
|
consider(binary_truthtable, binary_expression, binary_cost);
|
||||||
|
|
||||||
# y + x
|
# y + x
|
||||||
binary_truthtable = function(other_truthtable, my_truthtable);
|
binary_truthtable = function(other_truthtable, my_truthtable);
|
||||||
binary_truthtable = binary_truthtable & MASK
|
binary_truthtable = binary_truthtable & M
|
||||||
binary_expression = (name, other_expression, my_expression);
|
binary_expression = (name, other_expression, my_expression);
|
||||||
binary_cost = my_cost + 1 + costs[other_truthtable];
|
binary_cost = my_cost + 1 + costs[other_truthtable];
|
||||||
|
|
||||||
consider(binary_truthtable, binary_expression, binary_cost);
|
consider(binary_truthtable, binary_expression, binary_cost);
|
||||||
|
|
||||||
return costs, lookup
|
return costs, lookup
|
||||||
|
|
||||||
pathname = "simplifications.bin"
|
pathname = "simplifications.bin"
|
||||||
|
|
||||||
try:
|
def get_simplifications(args, available_operators):
|
||||||
with open(pathname, "rb") as stream:
|
available_operators = tuple(sorted(available_operators));
|
||||||
costs, lookup = pickle.load(stream);
|
|
||||||
except FileNotFoundError:
|
try:
|
||||||
print("Oh! looks like you're running this for the first time");
|
with open(pathname, "rb") as stream:
|
||||||
print("I'll have to build up my cache of simplifications");
|
cache = pickle.load(stream);
|
||||||
print("This may take a while.");
|
except FileNotFoundError:
|
||||||
print("I'll only have to do this once.");
|
cache = dict();
|
||||||
|
|
||||||
|
if available_operators not in cache:
|
||||||
|
if not args.quiet:
|
||||||
|
print("Oh! looks like you're running this for the first time");
|
||||||
|
print("I'll have to build up my cache of simplifications");
|
||||||
|
print("This may take a while.");
|
||||||
|
print("I'll only have to do this once.");
|
||||||
|
|
||||||
print();
|
if not args.yes:
|
||||||
input("any input to start:");
|
print();
|
||||||
print();
|
input("any input to start:");
|
||||||
|
print();
|
||||||
costs, lookup = calculate_simplifications();
|
|
||||||
|
bundle = calculate_simplifications(args, available_operators);
|
||||||
print();
|
|
||||||
print("done!");
|
if not args.quiet:
|
||||||
print();
|
print();
|
||||||
|
print("done!");
|
||||||
with open(pathname, "wb") as stream:
|
print();
|
||||||
pickle.dump((costs, lookup), stream);
|
|
||||||
|
cache[available_operators] = bundle;
|
||||||
print();
|
|
||||||
print("saved!");
|
with open(pathname, "wb") as stream:
|
||||||
print();
|
pickle.dump(cache, stream);
|
||||||
|
|
||||||
# for truthtable, exp in lookup.items():
|
if not args.quiet:
|
||||||
# print(f'{truthtable:016b}: {pretty(exp)}')
|
print();
|
||||||
|
print("saved!");
|
||||||
|
print();
|
||||||
|
|
||||||
|
return cache[available_operators];
|
||||||
|
|
||||||
def create_parser():
|
def create_parser():
|
||||||
import pyparsing as pp
|
import pyparsing as pp
|
||||||
|
|
@ -241,36 +334,30 @@ def create_parser():
|
||||||
logical_and_expression <<= \
|
logical_and_expression <<= \
|
||||||
Group(relational_expression + Literal('&&') + logical_and_expression) \
|
Group(relational_expression + Literal('&&') + logical_and_expression) \
|
||||||
| Group(relational_expression + Literal('!&') + logical_and_expression) \
|
| Group(relational_expression + Literal('!&') + logical_and_expression) \
|
||||||
|
| Group(relational_expression + Literal('&!') + logical_and_expression) \
|
||||||
| relational_expression;
|
| relational_expression;
|
||||||
|
|
||||||
logical_or_expression = Forward()
|
logical_or_expression = Forward()
|
||||||
logical_or_expression <<= \
|
logical_or_expression <<= \
|
||||||
Group(logical_and_expression + Literal('||') + logical_or_expression) \
|
Group(logical_and_expression + Literal('||') + logical_or_expression) \
|
||||||
| Group(logical_and_expression + Literal('!|') + logical_or_expression) \
|
| Group(logical_and_expression + Literal('!|') + logical_or_expression) \
|
||||||
|
| Group(logical_and_expression + Literal('|!') + logical_or_expression) \
|
||||||
| logical_and_expression;
|
| logical_and_expression;
|
||||||
|
|
||||||
root <<= logical_or_expression;
|
root <<= logical_or_expression;
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
|
|
||||||
parser = create_parser();
|
|
||||||
|
|
||||||
print("""
|
def parse(parser, text):
|
||||||
Please give a C-style expression using only variables 'w', 'x', 'y' and 'z'.
|
return parser.parseString(text, parseAll = True).asList()[0]
|
||||||
You can use any of the following operators: '!' (not), '&&' (and), '||' (or),
|
|
||||||
'<' ('and' with left argument negated), '>' ('and' with right argument negated),
|
|
||||||
'<=' ('or' with left argument negated), '>=' ('or' with right argument negated),
|
|
||||||
'!=' (xor), '==' (negated xor), '!|' (nor), '!&' (nand).
|
|
||||||
The "simpliest" expression is the one that uses the fewest number of operators.
|
|
||||||
It should be noted that more than one expression could be considered the
|
|
||||||
"simpliest". This program chooses one arbitrarily (alphabetical order).
|
|
||||||
""");
|
|
||||||
|
|
||||||
def evaluate(exp):
|
def evaluate(expr):
|
||||||
match exp:
|
match expr:
|
||||||
case "0":
|
case "0":
|
||||||
return 0;
|
return 0;
|
||||||
case "1":
|
case "1":
|
||||||
return MASK;
|
return M;
|
||||||
case "w":
|
case "w":
|
||||||
return W;
|
return W;
|
||||||
case "x":
|
case "x":
|
||||||
|
|
@ -280,56 +367,89 @@ def evaluate(exp):
|
||||||
case "z":
|
case "z":
|
||||||
return Z;
|
return Z;
|
||||||
case ("!", subexp):
|
case ("!", subexp):
|
||||||
return ~evaluate(subexp) & MASK;
|
return ~evaluate(subexp) & M;
|
||||||
case (left, "||", right):
|
case (left, "||", right):
|
||||||
return evaluate(left) | evaluate(right);
|
return evaluate(left) | evaluate(right);
|
||||||
case (left, "!|", right):
|
case (left, "!|", right):
|
||||||
return ~(evaluate(left) | evaluate(right)) & MASK;
|
return ~(evaluate(left) | evaluate(right)) & M;
|
||||||
|
case (left, "|!", right):
|
||||||
|
return (~evaluate(left) | evaluate(right)) & M;
|
||||||
case (left, "&&", right):
|
case (left, "&&", right):
|
||||||
return evaluate(left) & evaluate(right);
|
return evaluate(left) & evaluate(right);
|
||||||
case (left, "!&", right):
|
case (left, "!&", right):
|
||||||
return ~(evaluate(left) & evaluate(right)) & MASK;
|
return ~(evaluate(left) & evaluate(right)) & M;
|
||||||
|
case (left, "&!", right):
|
||||||
|
return (~evaluate(left) & evaluate(right)) & M;
|
||||||
case (left, "<", right):
|
case (left, "<", right):
|
||||||
return (~evaluate(left) & evaluate(right)) & MASK;
|
return (~evaluate(left) & evaluate(right)) & M;
|
||||||
case (left, "<=", right):
|
case (left, "<=", right):
|
||||||
return (~evaluate(left) | evaluate(right)) & MASK;
|
return (~evaluate(left) | evaluate(right)) & M;
|
||||||
case (left, ">", right):
|
case (left, ">", right):
|
||||||
return (evaluate(left) & ~evaluate(right)) & MASK;
|
return (evaluate(left) & ~evaluate(right)) & M;
|
||||||
case (left, ">=", right):
|
case (left, ">=", right):
|
||||||
return (evaluate(left) | ~evaluate(right)) & MASK;
|
return (evaluate(left) | ~evaluate(right)) & M;
|
||||||
case (left, "==", right):
|
case (left, "==", right):
|
||||||
return ~(evaluate(left) ^ evaluate(right)) & MASK;
|
return ~(evaluate(left) ^ evaluate(right)) & M;
|
||||||
case (left, "!=", right):
|
case (left, "!=", right):
|
||||||
return evaluate(left) ^ evaluate(right);
|
return evaluate(left) ^ evaluate(right);
|
||||||
case _:
|
case _:
|
||||||
print(exp);
|
print(exp);
|
||||||
assert(not "TODO");
|
assert(not "TODO");
|
||||||
|
|
||||||
while True:
|
def repl(args, cost, lookup):
|
||||||
try:
|
import readline;
|
||||||
raw_exp = input(">>> ");
|
|
||||||
except EOFError:
|
print("""
|
||||||
print("exit");
|
Give a boolean expression using the variables 'w', 'x', 'y' and 'z'.
|
||||||
break;
|
Operators: not ('!'), or ('||'), and ('&&').
|
||||||
|
More operators: nor ('!|'), orn ('|!'), nand ('!&'), andn ('&!'), xor ('!='),
|
||||||
try:
|
More operators: nxor ('==')
|
||||||
exp = parser.parseString(raw_exp, parseAll = True).asList()[0]
|
""");
|
||||||
except:
|
print(f'I can do anything in {max(cost.values())} operations.')
|
||||||
print("syntax error");
|
print();
|
||||||
continue;
|
|
||||||
|
parser = create_parser();
|
||||||
truthtable = evaluate(exp);
|
|
||||||
|
while True:
|
||||||
print(f"done in {costs[truthtable]} operations:");
|
try:
|
||||||
|
line = input(">>> ");
|
||||||
print(f"0b{truthtable:016b}: {pretty(lookup[truthtable])}");
|
except EOFError:
|
||||||
|
return;
|
||||||
# print(pretty(lookup[truthtable]));
|
|
||||||
|
truthtable = evaluate(parse(parser, line));
|
||||||
|
|
||||||
|
if truthtable in lookup:
|
||||||
|
print(f'{cost[truthtable]}: {pretty(lookup[truthtable])}')
|
||||||
|
else:
|
||||||
|
print('unreachable.')
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
available_operators = determine_available_operators(args);
|
||||||
|
|
||||||
|
match args.color:
|
||||||
|
case 'off': args.color = False;
|
||||||
|
case 'on': args.color = True;
|
||||||
|
case 'auto': args.color = os.isatty(1);
|
||||||
|
|
||||||
|
cost, lookup = get_simplifications(args, available_operators);
|
||||||
|
|
||||||
|
if args.printout:
|
||||||
|
for truthtable, exp in sorted(lookup.items()):
|
||||||
|
print(f'{truthtable:016b}: {pretty(exp)}');
|
||||||
|
elif args.command:
|
||||||
|
truthtable = evaluate(parse(create_parser(), args.command));
|
||||||
|
|
||||||
|
if truthtable in lookup:
|
||||||
|
print(pretty(lookup[truthtable]));
|
||||||
|
else:
|
||||||
|
print('unreachable.')
|
||||||
|
else:
|
||||||
|
repl(args, cost, lookup);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
|
exit(main(parse_args(sys.argv)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue