broke main into many source files, added spike about assignment support that actually works pretty well

This commit is contained in:
Zander Thannhauser 2025-06-26 22:41:11 -05:00
parent 99e5f2d8e6
commit 8152650e06
24 changed files with 2092 additions and 2283 deletions

2
.gitignore vendored
View file

@ -1,4 +1,6 @@
bin
.simplifications.bin
.~lock.stats.ods#
stats.ods

13
buildtypes/debug.txt Normal file
View file

@ -0,0 +1,13 @@
-D _GNU_SOURCE
-D ZDEBUG=1
-I .
-Werror -Wall -Wextra -Wstrict-prototypes -Wfatal-errors
-Wno-unused
-lreadline

11
buildtypes/release.txt Normal file
View file

@ -0,0 +1,11 @@
-D _GNU_SOURCE
-D ZDEBUG=0
-I .
-Werror -Wall -Wextra -Wstrict-prototypes -Wfatal-errors
-lreadline

486
calculate_simplifications.c Normal file
View file

@ -0,0 +1,486 @@
#include <stdio.h>
#include <limits.h>
#include <assert.h>
#include <debug.h>
#include <cmdln.h>
#include <print.h>
#include "calculate_simplifications.h"
struct simplifications calculate_simplifications(
const struct cmdln_flags* flags)
{
struct simplifications simps = {};
// init 'simps.data':
{
for (int i = 0; i < N; i++)
{
simps.data[i].kind = ek_unreachable;
simps.data[i].cost = INT_MAX;
}
}
// heap of truthtables; key = cost
struct {
uint16_t data[N];
int n;
// truthtable -> index in 'todo'
int reverse[N];
} todo;
// init 'todo':
{
todo.n = 0;
for (int i = 0; i < N; i++)
{
todo.reverse[i] = -1;
}
}
uint16_t pop(void)
{
assert(todo.n > 0);
uint16_t retval = todo.data[0];
todo.reverse[retval] = -1;
uint16_t moving = todo.data[todo.n-- - 1];
int cost = simps.data[moving].cost;
int index = 0;
again:
{
int left = index * 2 + 1;
int right = index * 2 + 2;
uint16_t smallest = moving;
if (left < todo.n && simps.data[todo.data[left]].cost < cost)
smallest = todo.data[left];
if (right < todo.n && simps.data[todo.data[right]].cost < simps.data[smallest].cost)
smallest = todo.data[right];
if (smallest == moving)
{
todo.data[index] = moving;
todo.reverse[moving] = index;
}
else
{
int new = todo.reverse[smallest];
todo.data[index] = smallest;
todo.reverse[smallest] = index;
index = new;
goto again;
}
}
return retval;
}
void append(uint16_t truthtable, int cost)
{
assert(todo.reverse[truthtable] == -1);
int index = todo.n++, new_index;
while (index > 0 && simps.data[todo.data[new_index = (index - 1) / 2]].cost > cost)
{
todo.data[index] = todo.data[new_index];
todo.reverse[todo.data[new_index]] = index;
index = new_index;
}
todo.data[index] = truthtable;
todo.reverse[truthtable] = index;
simps.data[truthtable].cost = cost;
}
void update(uint16_t truthtable, int cost)
{
assert(todo.reverse[truthtable] != -1);
assert(cost < simps.data[truthtable].cost);
int index =todo. reverse[truthtable], new_index;
while (index > 0 && simps.data[todo.data[new_index = (index - 1) / 2]].cost > cost)
{
todo.data[index] = todo.data[new_index];
todo.reverse[todo.data[new_index]] = index;
index = new_index;
}
todo.data[index] = truthtable;
todo.reverse[truthtable] = index;
simps.data[truthtable].cost = cost;
}
// create a list of the "done" truthtables
struct {
int headtails[N], next[N], head;
// for debugging:
#if ZDEBUG
bool in[N];
#endif
} done = {};
// init 'done':
{
done.head = -1;
for (int i = 0; i < N; i++)
{
done.headtails[i] = -1;
#if ZDEBUG
done.in[i] = false;
#endif
}
}
void insert(int index)
{
#if ZDEBUG
assert(!done.in[index]);
#endif
int head = index & 1 ? index & ~(index & -index) : index, prevhead = -1;
while (done.headtails[head] == -1 || index < done.headtails[head])
{
done.headtails[head] = index;
prevhead = head, head = head & ~(head & -head);
}
if (done.headtails[head] < index)
{
if (prevhead == -1)
{
assert(done.headtails[head] == head);
done.next[head] = index;
}
else
{
int tophalftail = done.headtails[prevhead - 1];
assert(tophalftail != -1);
done.next[tophalftail] = index;
}
}
else
{
done.head = index;
}
int n = ~index & M;
int tail = index & 1 ? index : index | (n & -n), prevtail = -1;
while (done.headtails[tail] == -1 || done.headtails[tail] < index)
{
done.headtails[tail] = index;
prevtail = tail, tail = tail | (n = ~tail & M, n & -n);
}
if (index < done.headtails[tail])
{
if (prevtail == -1)
{
assert(done.headtails[tail] == tail);
#if ZDEBUG
assert(done.in[tail]);
#endif
done.next[index] = tail;
}
else
{
int bottomhalfhead = done.headtails[prevtail + 1];
assert(bottomhalfhead != -1);
done.next[index] = bottomhalfhead;
}
}
else
{
done.next[index] = -1;
}
#if ZDEBUG
done.in[index] = true;
#endif
}
append(W, 0), simps.data[W].kind = ek_W;
append(X, 0), simps.data[X].kind = ek_X;
append(Y, 0), simps.data[Y].kind = ek_Y;
append(Z, 0), simps.data[Z].kind = ek_Z;
append(0, 1), simps.data[0].kind = ek_0;
append(M, 1), simps.data[M].kind = ek_1;
// disable terminal autowrap:
if (flags->verbose && flags->print_with_color)
{
printf("\e[?7l");
}
for (int iterations = 1; todo.n && iterations <= N; iterations++)
{
uint16_t truthtable = pop();
insert(truthtable);
int cost = simps.data[truthtable].cost;
if (flags->verbose)
{
if (flags->print_with_color)
{
printf("\e[2K");
}
printf("%i of %i (%.2f%%): [%i] ",
iterations, N, (100.0 * iterations / N), cost);
print(flags, &simps, truthtable), puts("");
if (flags->print_with_color)
{
printf("\e[1A");
}
}
// consider NOT:
if (flags->use_operators.not)
{
uint16_t not_truthtable = ~truthtable & M;
int not_cost = cost + 1;
if (not_cost < simps.data[not_truthtable].cost)
{
if (todo.reverse[not_truthtable] == -1)
{
append(not_truthtable, not_cost);
}
else
{
update(not_truthtable, not_cost);
}
simps.data[not_truthtable].kind = ek_not;
simps.data[not_truthtable].left = truthtable;
}
}
#define BINARY_OPERATOR(ekind, function) \
{ \
for (int i = done.head; i != -1; i = done.next[i]) \
{ \
{ \
uint16_t bin_truthtable = function(truthtable, i) & M; \
\
int bin_cost = 1 + cost + simps.data[i].cost; \
\
if (bin_cost < simps.data[bin_truthtable].cost) \
{ \
if (todo.reverse[bin_truthtable] == -1) \
append(bin_truthtable, bin_cost); \
else \
update(bin_truthtable, bin_cost); \
\
simps.data[bin_truthtable].kind = ekind; \
simps.data[bin_truthtable].left = truthtable; \
simps.data[bin_truthtable].right = i; \
} \
} \
\
{ \
uint16_t bin_truthtable = function(i, truthtable) & M; \
\
int bin_cost = 1 + cost + simps.data[i].cost; \
\
if (bin_cost < simps.data[bin_truthtable].cost) \
{ \
if (todo.reverse[bin_truthtable] == -1) \
append(bin_truthtable, bin_cost); \
else \
update(bin_truthtable, bin_cost); \
\
simps.data[bin_truthtable].kind = ekind; \
simps.data[bin_truthtable].left = i; \
simps.data[bin_truthtable].right = truthtable; \
} \
} \
} \
}
#define OR(a, b) ((a) | (b))
#define AND(a, b) ((a) & (b))
#define ORN(a, b) (~(a) | (b))
#define NOR(a, b) ~( (a) | (b))
#define ANDN(a, b) (~(a) & (b))
#define NAND(a, b) ~( (a) & (b))
#define XOR(a, b) (( a) ^ (b))
#define NXOR(a, b) ~( (a) ^ (b))
#define LT(a, b) ((~a) & (b))
#define LTE(a, b) ((~a) | (b))
#define GT(a, b) (( a) & ~(b))
#define GTE(a, b) (( a) | ~(b))
if (flags->use_operators.or)
{
BINARY_OPERATOR(ek_or, OR);
}
if (flags->use_operators.and)
{
BINARY_OPERATOR(ek_and, AND);
}
if (flags->use_operators.orn)
{
BINARY_OPERATOR(ek_orn, ORN);
}
if (flags->use_operators.nor)
{
BINARY_OPERATOR(ek_nor, NOR);
}
if (flags->use_operators.andn)
{
BINARY_OPERATOR(ek_andn, ANDN);
}
if (flags->use_operators.nand)
{
BINARY_OPERATOR(ek_nand, NAND);
}
if (flags->use_operators.xor)
{
BINARY_OPERATOR(ek_xor, XOR);
}
if (flags->use_operators.nxor)
{
BINARY_OPERATOR(ek_nxor, NXOR);
}
if (flags->use_operators.lt)
{
BINARY_OPERATOR(ek_lt, LT);
}
if (flags->use_operators.lte)
{
BINARY_OPERATOR(ek_lte, LTE);
}
if (flags->use_operators.gt)
{
BINARY_OPERATOR(ek_gt, GT);
}
if (flags->use_operators.gte)
{
BINARY_OPERATOR(ek_gte, GTE);
}
if (flags->use_operators.ternary)
{
for (int i = done.head; i != -1; i = done.next[i])
{
for (int j = done.head; j != -1; j = done.next[j])
{
int ternary_cost = 1 + cost + simps.data[i].cost + simps.data[j].cost;
#define TERNARY(C, T, F) \
{ \
uint16_t ternary_truthtable = \
((C) & (T)) | (~(C) & (F)); \
\
if (ternary_cost < simps.data[ternary_truthtable].cost) \
{ \
if (todo.reverse[ternary_truthtable] == -1) \
{ \
append(ternary_truthtable, ternary_cost); \
} \
else \
{ \
update(ternary_truthtable, ternary_cost); \
} \
\
simps.data[ternary_truthtable].kind = ek_ternary; \
simps.data[ternary_truthtable].cond = (C); \
simps.data[ternary_truthtable].left = (T); \
simps.data[ternary_truthtable].right = (F); \
} \
} \
TERNARY(truthtable, i, j);
TERNARY(i, truthtable, j);
TERNARY(i, j, truthtable);
}
}
}
}
// clear progress line, enable terminal autowrap:
if (flags->verbose && flags->print_with_color)
{
printf("\e[2K"), printf("\e[?7h");
}
return simps;
}

View file

@ -0,0 +1,8 @@
#include "simplifications.h"
struct cmdln_flags;
struct simplifications calculate_simplifications(
const struct cmdln_flags* flags);

210
cmdln.c Normal file
View file

@ -0,0 +1,210 @@
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <assert.h>
#include "cmdln.h"
#include "debug.h"
struct cmdln_flags parse_args(int argc, char* const* argv)
{
struct cmdln_flags flags = {};
flags.argv0 = argv[0];
bool unset_operators = true;
bool unset_print_with_color = true;
for (int opt; (opt = getopt(argc, argv, "pyqmvc:eEo:C:B")) != -1; )
{
switch (opt)
{
case 'p':
{
flags.print_all_and_quit = true;
break;
}
case 'y':
{
flags.assume_yes = true;
break;
}
case 'q':
{
flags.quiet = true;
break;
}
case 'm':
{
flags.print_max_operators_needed_and_quit = true;
break;
}
case 'v':
{
flags.verbose = true;
break;
}
case 'c':
{
flags.command = optarg;
break;
}
case 'E':
{
flags.use_operators.ternary = true;
}
// fallthrough
case 'e':
{
flags.use_operators.not = true;
flags.use_operators.or = true;
flags.use_operators.and = true;
flags.use_operators.orn = true;
flags.use_operators.nor = true;
flags.use_operators.andn = true;
flags.use_operators.nand = true;
flags.use_operators. xor = true;
flags.use_operators.nxor = true;
unset_operators = false;
break;
}
case 'o':
{
unset_operators = false;
for (char* moving; (moving = strtok_r(NULL, ",", &optarg)); )
{
if (!strcmp(moving, "not") || !strcmp(moving, "!"))
{
flags.use_operators.not = true;
}
else if (!strcmp(moving, "or") || !strcmp(moving, "||"))
{
flags.use_operators.or = true;
}
else if (!strcmp(moving, "orn") || !strcmp(moving, "|!"))
{
flags.use_operators.orn = true;
}
else if (!strcmp(moving, "nor") || !strcmp(moving, "!|"))
{
flags.use_operators.nor = true;
}
else if (!strcmp(moving, "and") || !strcmp(moving, "&&"))
{
flags.use_operators.and = true;
}
else if (!strcmp(moving, "andn") || !strcmp(moving, "&!"))
{
flags.use_operators.andn = true;
}
else if (!strcmp(moving, "nand") || !strcmp(moving, "!&"))
{
flags.use_operators.nand = true;
}
else if (!strcmp(moving, "xor") || !strcmp(moving, "!="))
{
flags.use_operators.xor = true;
}
else if (!strcmp(moving, "nxor") || !strcmp(moving, "=="))
{
flags.use_operators.nxor = true;
}
else if (!strcmp(moving, "lt") || !strcmp(moving, "<"))
{
flags.use_operators.lt = true;
}
else if (!strcmp(moving, "lte") || !strcmp(moving, "<="))
{
flags.use_operators.lte = true;
}
else if (!strcmp(moving, "gt") || !strcmp(moving, ">"))
{
flags.use_operators.gt = true;
}
else if (!strcmp(moving, "gte") || !strcmp(moving, ">="))
{
flags.use_operators.gte = true;
}
else if (!strcmp(moving, "ternary") || !strcmp(moving, "?:"))
{
flags.use_operators.ternary = true;
}
else
{
assert(!"TODO");
}
}
break;
}
case 'C':
{
unset_print_with_color = false;
if (!strcmp(optarg, "yes") || !strcmp(optarg, "on"))
{
flags.print_with_color = true;
}
else if (!strcmp(optarg, "no") || !strcmp(optarg, "off"))
{
flags.print_with_color = false;
}
else if (!strcmp(optarg, "auto"))
{
flags.print_with_color = isatty(1);
}
else
{
assert(!"TODO");
}
break;
}
case 'B':
{
flags.force_rebuild = true;
break;
}
default:
{
assert(!"TODO");
break;
}
}
}
if (unset_operators)
{
flags.use_operators.not = true;
flags.use_operators.or = true;
flags.use_operators.and = true;
}
if (unset_print_with_color)
{
flags.print_with_color = isatty(1);
}
return flags;
}

40
cmdln.h Normal file
View file

@ -0,0 +1,40 @@
#include <stdbool.h>
struct cmdln_flags
{
const char* argv0;
const char* command;
struct use_operators {
bool not;
bool or, orn, nor;
bool and, andn, nand;
bool xor, nxor;
bool lt, lte, gt, gte;
bool ternary;
} use_operators;
bool print_all_and_quit;
bool print_max_operators_needed_and_quit;
bool assume_yes;
bool verbose;
bool print_with_color;
bool force_rebuild;
bool quiet;
};
struct cmdln_flags parse_args(int argc, char* const* argv);

18
debug.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef ZDEBUG
#define ZDEBUG 1
#endif
#if ZDEBUG
#define zprintf(fmt, ...) \
printf(fmt, ## __VA_ARGS__);
#else
#define zprintf(...);
#endif
#define TODO \
assert(!"TODO");
#define CHECK \
assert(!"CHECK");

9
defines.h Normal file
View file

@ -0,0 +1,9 @@
#define W 0b0101010101010101
#define X 0b0011001100110011
#define Y 0b0000111100001111
#define Z 0b0000000011111111
#define M 0b1111111111111111
#define N (65536)

500
evaluate.c Normal file
View file

@ -0,0 +1,500 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <defines.h>
#include "evaluate.h"
uint16_t evaluate(const char* text)
{
enum {
tk_uninitialized,
tk_0,
tk_1,
tk_w,
tk_x,
tk_y,
tk_z,
tk_oparen,
tk_cparen,
tk_emark,
tk_emarkequals,
tk_emarkvbar,
tk_emarkampersand,
tk_equalsequals,
tk_vbarvbar,
tk_vbaremark,
tk_qmark,
tk_less_than,
tk_less_than_eq,
tk_greater_than,
tk_greater_than_eq,
tk_ampersandemark,
tk_ampersandampersand,
tk_colon,
tk_EOF,
} tokenkind = tk_uninitialized;
const char* moving = text;
void next_token(void)
{
while (*moving && *moving == ' ')
moving++;
switch (*moving)
{
case 0:
tokenkind = tk_EOF;
break;
case '0':
tokenkind = tk_0, moving++;
break;
case '1':
tokenkind = tk_1, moving++;
break;
case 'w':
tokenkind = tk_w, moving++;
break;
case 'x':
tokenkind = tk_x, moving++;
break;
case 'y':
tokenkind = tk_y, moving++;
break;
case 'z':
tokenkind = tk_z, moving++;
break;
case '(':
tokenkind = tk_oparen, moving++;
break;
case ')':
tokenkind = tk_cparen, moving++;
break;
case '?':
tokenkind = tk_qmark, moving++;
break;
case ':':
tokenkind = tk_colon, moving++;
break;
// either '||' or '|!':
case '|':
{
moving++;
switch (*moving)
{
case '|':
tokenkind = tk_vbarvbar, moving++;
break;
case '!':
tokenkind = tk_vbaremark, moving++;
break;
default:
{
puts("syntax error");
exit(1);
break;
}
}
break;
}
// either '&&' or '&!':
case '&':
{
moving++;
switch (*moving)
{
case '&':
tokenkind = tk_ampersandampersand, moving++;
break;
case '!':
tokenkind = tk_ampersandemark, moving++;
break;
default:
{
puts("syntax error");
exit(1);
break;
}
}
break;
}
// either '!' or '!=' or '!&' or '!|'
case '!':
{
moving++;
switch (*moving)
{
case '=':
tokenkind = tk_emarkequals, moving++;
break;
case '|':
tokenkind = tk_emarkvbar, moving++;
break;
case '&':
tokenkind = tk_emarkampersand, moving++;
break;
default:
tokenkind = tk_emark;
break;
}
break;
}
case '<':
{
moving++;
switch (*moving)
{
case '=':
tokenkind = tk_less_than_eq, moving++;
break;
default:
{
tokenkind = tk_less_than;
break;
}
}
break;
}
case '>':
{
moving++;
switch (*moving)
{
case '=':
tokenkind = tk_greater_than_eq, moving++;
break;
default:
{
tokenkind = tk_greater_than;
break;
}
}
break;
}
// could only be '==':
case '=':
{
moving++;
switch (*moving)
{
case '=':
tokenkind = tk_equalsequals, moving++;
break;
default:
{
puts("syntax error");
exit(1);
break;
}
}
break;
}
default:
assert(!"TODO");
break;
}
}
next_token();
uint16_t parse_root(void)
{
uint16_t parse_ternary(void)
{
uint16_t parse_ors(void)
{
uint16_t parse_ands(void)
{
uint16_t parse_equals(void)
{
uint16_t parse_compares(void)
{
uint16_t parse_prefix(void)
{
uint16_t parse_primary(void)
{
uint16_t retval;
switch (tokenkind)
{
case tk_0:
retval = 0, next_token();
break;
case tk_1:
retval = M, next_token();
break;
case tk_w:
retval = W, next_token();
break;
case tk_x:
retval = X, next_token();
break;
case tk_y:
retval = Y, next_token();
break;
case tk_z:
retval = Z, next_token();
break;
case tk_oparen:
{
next_token();
retval = parse_root();
if (tokenkind != tk_cparen)
{
assert(!"NOPE");
}
next_token();
break;
}
default:
assert(!"TODO");
break;
}
return retval;
}
if (tokenkind == tk_emark)
{
next_token();
return ~parse_prefix();
}
else
{
return parse_primary();
}
}
uint16_t left = parse_prefix();
again: switch (tokenkind)
{
case tk_less_than:
{
next_token();
left = (~left & parse_equals()) & M;
goto again;
}
case tk_less_than_eq:
{
next_token();
left = (~left | parse_equals()) & M;
goto again;
}
case tk_greater_than_eq:
{
next_token();
left = ( left | ~parse_equals()) & M;
goto again;
}
case tk_greater_than:
{
next_token();
left = ( left & ~parse_equals()) & M;
goto again;
}
default:
break;
}
return left;
}
uint16_t left = parse_compares();
again: switch (tokenkind)
{
case tk_equalsequals:
{
next_token();
left = ~(left ^ parse_equals()) & M;
goto again;
}
case tk_emarkequals:
{
next_token();
left = left ^ parse_equals();
goto again;
}
default:
break;
}
return left;
}
uint16_t left = parse_equals();
again: switch (tokenkind)
{
case tk_ampersandampersand:
{
next_token();
left = left & parse_equals();
goto again;
}
case tk_ampersandemark:
{
next_token();
left = ~left & parse_equals();
goto again;
}
case tk_emarkampersand:
{
next_token();
left = ~(left & parse_equals());
goto again;
}
default:
break;
}
return left;
}
uint16_t left = parse_ands();
again: switch (tokenkind)
{
case tk_vbarvbar:
{
next_token();
left = left | parse_ands();
goto again;
}
case tk_vbaremark:
{
next_token();
left = (~left | parse_ands()) & M;
goto again;
}
case tk_emarkvbar:
{
next_token();
left = ~(left | parse_ands()) & M;
goto again;
}
default:
break;
}
return left;
}
uint16_t cond = parse_ors();
if (tokenkind == tk_qmark)
{
next_token();
uint16_t left = parse_ors();
if (tokenkind != tk_colon)
{
puts("syntax error!");
exit(1);
}
next_token();
uint16_t right = parse_ternary();
return (cond & left) | (~cond & right);
}
else
{
return cond;
}
}
return parse_ternary();
}
uint16_t truthtable = parse_root();
if (tokenkind != tk_EOF)
{
puts("syntax error!");
exit(1);
}
return truthtable;
}

5
evaluate.h Normal file
View file

@ -0,0 +1,5 @@
#include <stdint.h>
uint16_t evaluate(const char* text);

53
expr.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef STRUCT_EXPR
#define STRUCT_EXPR
#include <stdint.h>
enum ekind
{
ek_unreachable,
ek_0,
ek_1,
ek_W,
ek_X,
ek_Y,
ek_Z,
ek_not,
ek_or,
ek_and,
ek_orn,
ek_nor,
ek_andn,
ek_nand,
ek_xor,
ek_nxor,
ek_lt,
ek_lte,
ek_gt,
ek_gte,
ek_ternary,
};
struct expr
{
enum ekind kind;
uint16_t cond, left, right;
int cost;
};
#endif

View file

@ -2,7 +2,7 @@
# vim: set sw=2 ts=2 et: #
{
description = "An example project using flutter";
description = "description";
inputs.nixpkgs = { url = "github:NixOS/nixpkgs"; };
@ -29,8 +29,29 @@
gnumake
readline.dev
python3
valgrind
# for my sake:
gedit
];
};
});
}

View file

@ -0,0 +1,172 @@
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include <defines.h>
#include <debug.h>
#include <cmdln.h>
#include "get_cached_simplifications.h"
#include "calculate_simplifications.h"
static struct path { char data[PATH_MAX]; } get_path(
const struct cmdln_flags* flags)
{
struct path path = {};
strcat(path.data, ".simplifier-cache-1");
if (flags->use_operators.not)
strcat(path.data, "-not");
if (flags->use_operators.or)
strcat(path.data, "-or");
if (flags->use_operators.orn)
strcat(path.data, "-orn");
if (flags->use_operators.nor)
strcat(path.data, "-nor");
if (flags->use_operators.and)
strcat(path.data, "-and");
if (flags->use_operators.andn)
strcat(path.data, "-andn");
if (flags->use_operators.nand)
strcat(path.data, "-nand");
if (flags->use_operators.xor)
strcat(path.data, "-xor");
if (flags->use_operators.nxor)
strcat(path.data, "-nxor");
if (flags->use_operators.lt)
strcat(path.data, "-lt");
if (flags->use_operators.lte)
strcat(path.data, "-lte");
if (flags->use_operators.gt)
strcat(path.data, "-gt");
if (flags->use_operators.gte)
strcat(path.data, "-gte");
if (flags->use_operators.ternary)
strcat(path.data, "-ternary");
strcat(path.data, ".bin");
return path;
}
struct simplifications get_cached_simplifications(
const struct cmdln_flags* flags)
{
struct path path = get_path(flags);
int fd = -1;
bool rebuild = flags->force_rebuild;
struct simplifications simps = {};
if (!rebuild)
{
fd = open(path.data, O_RDONLY);
if (fd > 0)
{
// great, just read it into memory
if (read(fd, &simps, sizeof(simps)) < (ssize_t) sizeof(simps))
{
printf("%s: read() to cache failed: %m\n", flags->argv0);
exit(1);
}
}
else if (errno == ENOENT)
{
rebuild = true;
}
else
{
TODO;
exit(1);
}
}
if (rebuild)
{
if (!flags->quiet)
{
puts(""
"I'll have to build up my cache of simplifications" "\n"
"I'll only have to do this once." "\n"
"\n"
"This may take a while." "\n"
"");
}
if (!flags->quiet && !flags->verbose)
{
puts("Re-run with '-v' to watch progress");
}
if (!flags->quiet && !flags->assume_yes)
{
puts("");
puts("Any input to start:"), getchar();
puts("Started.");
}
simps = calculate_simplifications(flags);
fd = open(path.data, O_WRONLY | O_TRUNC | O_CREAT, 0664);
if (fd < 0)
{
TODO;
exit(1);
}
if (write(fd, &simps, sizeof(simps)) < (ssize_t) sizeof(simps))
{
printf("%s: write() to cache failed: %m\n", flags->argv0);
exit(1);
}
}
if (fd > 0)
{
close(fd);
}
return simps;
}

View file

@ -0,0 +1,8 @@
#include "simplifications.h"
struct cmdln_flags;
struct simplifications get_cached_simplifications(
const struct cmdln_flags* flags);

2315
main.c

File diff suppressed because it is too large Load diff

View file

@ -1,28 +1,62 @@
# vim: noexpandtab tabstop=4 :
buildtype ?= release
optionset = buildtypes/${buildtype}.txt
prefix = bin/${buildtype}
default: ${prefix}/bsimp
.PRECIOUS: %/
%/:
@mkdir -p $@
srcs += calculate_simplifications.c cmdln.c evaluate.c
srcs += get_cached_simplifications.c main.c print.c
${prefix}/%.o ${prefix}/%.d: %.c ${optionset} | ${prefix}/%/
@echo "compiling (${buildtype}) ${*}.c ..."
@gcc -c @${optionset} $< -MD -MF ${prefix}/${*}.d -o ${prefix}/${*}.o
objs = $(patsubst %.c,${prefix}/%.o,${srcs})
${prefix}/bsimp: ${optionset} ${objs} | bin/
@echo "linking (${buildtype})"
@gcc @${optionset} ${objs} -o $@
args =
cc = gcc
cppflags += -D _GNU_SOURCE
#cppflags += -D ZDEBUG
cflags = -Werror -Wall -Wextra -Wstrict-prototypes -Wfatal-errors
cflags += -O4
# cflags += -Wno-unused
ldflags += -lreadline
/tmp/4-variable-simplifier: main.c
$(cc) $(cppflags) $(cflags) $< -o $@ $(ldflags)
run: /tmp/4-variable-simplifier
run: ${prefix}/bsimp
$< $(args)
valrun: ${prefix}/bsimp
valgrind --exit-on-first-error=yes --error-exitcode=1 -- $< ${args}
PREFIX ?= ${HOME}
install: ${PREFIX}/bin/bsimp
${PREFIX}/bin/bsimp: bin/release/bsimp | ${PREFIX}/bin/
cp -vau $< $@
include $(patsubst %.c,${prefix}/%.d,${srcs})
# nix --extra-experimental-features nix-command \
# --extra-experimental-features flakes develop --command 'make'

6
nix-make.sh Executable file
View file

@ -0,0 +1,6 @@
#!/bin/sh
set -ev
nix --extra-experimental-features nix-command \
--extra-experimental-features flakes \
develop \
--command "make" "$@"

199
print.c Normal file
View file

@ -0,0 +1,199 @@
#include <stdio.h>
#include <assert.h>
#include <debug.h>
#include <cmdln.h>
#include <expr.h>
#include "simplifications.h"
#include "print.h"
static void helper(
const struct simplifications *simps,
uint16_t truthtable,
bool print_with_color,
int depth)
{
#define LITERAL_ESCAPE "\e[38;2;200;200;100m"
#define VARIABLE_ESCAPE "\e[38;2;100;100;200m"
#define RESET_ESCAPE "\e[0m"
static const char* const operator_colors[10] = {
"\e[38;2;204;0;0m",
"\e[38;2;204;122;0m",
"\e[38;2;163;204;0m",
"\e[38;2;40;204;0m",
"\e[38;2;0;204;81m",
"\e[38;2;0;204;204m",
"\e[38;2;0;81;204m",
"\e[38;2;40;0;204m",
"\e[38;2;163;0;204m",
"\e[38;2;204;0;122m",
};
const struct expr* e = &simps->data[truthtable];
switch (e->kind)
{
case ek_unreachable:
{
assert(!"NOPE");
break;
}
case ek_0:
{
printf(print_with_color ? LITERAL_ESCAPE "0" RESET_ESCAPE : "0");
break;
}
case ek_1:
{
printf(print_with_color ? LITERAL_ESCAPE "1" RESET_ESCAPE : "1");
break;
}
case ek_W:
{
printf(print_with_color ? VARIABLE_ESCAPE "w" RESET_ESCAPE : "w");
break;
}
case ek_X:
{
printf(print_with_color ? VARIABLE_ESCAPE "x" RESET_ESCAPE : "x");
break;
}
case ek_Y:
{
printf(print_with_color ? VARIABLE_ESCAPE "y" RESET_ESCAPE : "y");
break;
}
case ek_Z:
{
printf(print_with_color ? VARIABLE_ESCAPE "z" RESET_ESCAPE : "z");
break;
}
case ek_not:
{
const char* start = print_with_color ? operator_colors[depth % 10] : "";
const char* end = print_with_color ? RESET_ESCAPE : "";
printf("%s(!%s", start, end);
helper(simps, e->left, print_with_color, depth + 1);
printf("%s)%s", start, end);
break;
}
#define BINARY_OPERATOR(kind, operatorstring) \
case kind: \
{ \
const char *start = "", *end = ""; \
\
if (print_with_color) \
{ \
start = operator_colors[depth % 10]; \
\
end = RESET_ESCAPE; \
} \
\
printf("%s(%s", start, end); \
\
helper(simps, e->left, print_with_color, depth + 1); \
\
printf("%s%s%s", start, operatorstring, end); \
\
helper(simps, e->right, print_with_color, depth + 1); \
\
printf("%s)%s", start, end); \
break; \
}
BINARY_OPERATOR(ek_or, " || ");
BINARY_OPERATOR(ek_orn, " |! ");
BINARY_OPERATOR(ek_nor, " !| ");
BINARY_OPERATOR(ek_and, " && ");
BINARY_OPERATOR(ek_andn, " &! ");
BINARY_OPERATOR(ek_nand, " !& ");
BINARY_OPERATOR(ek_xor, " != ");
BINARY_OPERATOR(ek_nxor, " == ");
BINARY_OPERATOR(ek_lt, " < ");
BINARY_OPERATOR(ek_lte, " <= ");
BINARY_OPERATOR(ek_gt, " > ");
BINARY_OPERATOR(ek_gte, " >= ");
#undef BINARY_OPERATOR
case ek_ternary:
{
const char *start = "", *end = "";
if (print_with_color)
{
start = operator_colors[depth % 10];
end = RESET_ESCAPE;
}
printf("%s(%s", start, end);
helper(simps, e->cond, print_with_color, depth + 1);
printf(" %s?%s ", start, end);
helper(simps, e->left, print_with_color, depth + 1);
printf(" %s:%s ", start, end);
helper(simps, e->right, print_with_color, depth + 1);
printf("%s)%s", start, end);
break;
}
}
}
void print(
const struct cmdln_flags* flags,
const struct simplifications *simps,
uint16_t truthtable)
{
helper(simps, truthtable, flags->print_with_color, 0);
}

12
print.h Normal file
View file

@ -0,0 +1,12 @@
#include <stdint.h>
struct cmdln_flags;
struct simplifications;
void print(
const struct cmdln_flags* flags,
const struct simplifications *simps,
uint16_t truthtable);

14
simplifications.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef STRUCT_SIMPLIFICATIONS
#define STRUCT_SIMPLIFICATIONS
#include "defines.h"
#include "expr.h"
struct simplifications
{
struct expr data[N];
};
#endif

View file

199
spikes/assignment.py Normal file
View file

@ -0,0 +1,199 @@
#!/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");