added printing stats

This commit is contained in:
Alex Thannhauser 2025-07-17 11:20:48 -05:00
parent 3f161b9630
commit 14be1007c5
2 changed files with 111 additions and 71 deletions

180
main.c
View file

@ -1,4 +1,5 @@
#include <math.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <errno.h> #include <errno.h>
@ -46,6 +47,8 @@ bool verbose = false;
bool print_with_color = false; bool print_with_color = false;
bool print_stats = false;
bool force_rebuild = false; bool force_rebuild = false;
bool quiet = false; bool quiet = false;
@ -56,7 +59,7 @@ static void parse_args(int argc, char* const* argv)
print_with_color = isatty(1); print_with_color = isatty(1);
for (int opt; (opt = getopt(argc, argv, "pyqvc:eEo:C:B")) != -1; ) switch (opt) for (int opt; (opt = getopt(argc, argv, "pyqmvc:eEo:C:B")) != -1; ) switch (opt)
{ {
case 'p': case 'p':
print_all_and_quit = true; print_all_and_quit = true;
@ -70,6 +73,10 @@ static void parse_args(int argc, char* const* argv)
quiet = true; quiet = true;
break; break;
case 'm':
print_stats = true;
break;
case 'v': case 'v':
verbose = true; verbose = true;
break; break;
@ -158,14 +165,14 @@ static void parse_args(int argc, char* const* argv)
break; break;
} }
case 'B': case 'B':
{ {
force_rebuild = true; force_rebuild = true;
break; break;
} }
default: default:
assert(!"TODO"); assert(!"TODO");
break; break;
@ -189,7 +196,7 @@ static void parse_args(int argc, char* const* argv)
enum kind { enum kind {
ek_unreachable, ek_unreachable,
ek_0, ek_0,
ek_1, ek_1,
ek_W, ek_W,
@ -222,7 +229,7 @@ struct expr {
enum kind kind; enum kind kind;
uint16_t cond, left, right; uint16_t cond, left, right;
int cost; int cost;
} lookup[N]; } lookup[N];
@ -254,7 +261,7 @@ static void print(uint16_t truthtable, int depth)
assert(!"NOPE"); assert(!"NOPE");
break; break;
} }
case ek_0: case ek_0:
{ {
printf(print_with_color ? LITERAL_ESCAPE "0" RESET_ESCAPE : "0"); printf(print_with_color ? LITERAL_ESCAPE "0" RESET_ESCAPE : "0");
@ -377,30 +384,30 @@ void calculate_simplifications(void)
for (int i = 0; i < N; i++) for (int i = 0; i < N; i++)
{ {
lookup[i].kind = ek_unreachable; lookup[i].kind = ek_unreachable;
lookup[i].cost = INT_MAX; lookup[i].cost = INT_MAX;
} }
} }
// heap of truthtables; key = cost // heap of truthtables; key = cost
struct { struct {
uint16_t data[N]; uint16_t data[N];
int n; int n;
// truthtable -> index in 'todo' // truthtable -> index in 'todo'
int reverse[N]; int reverse[N];
} todo; } todo;
// init 'todo': // init 'todo':
{ {
todo.n = 0; todo.n = 0;
for (int i = 0; i < N; i++) for (int i = 0; i < N; i++)
{ {
todo.reverse[i] = -1; todo.reverse[i] = -1;
} }
} }
uint16_t pop(void) uint16_t pop(void)
{ {
assert(todo.n > 0); assert(todo.n > 0);
@ -465,11 +472,11 @@ void calculate_simplifications(void)
index = new_index; index = new_index;
} }
todo.data[index] = truthtable; todo.data[index] = truthtable;
todo.reverse[truthtable] = index; todo.reverse[truthtable] = index;
lookup[truthtable].cost = cost; lookup[truthtable].cost = cost;
} }
@ -495,60 +502,60 @@ void calculate_simplifications(void)
lookup[truthtable].cost = cost; lookup[truthtable].cost = cost;
} }
// create a list of the "done" truthtables // create a list of the "done" truthtables
struct { struct {
int headtails[N], next[N], head; int headtails[N], next[N], head;
// for debugging: // for debugging:
#if ZDEBUG #if ZDEBUG
bool in[N]; bool in[N];
#endif #endif
} done = {}; } done = {};
// init 'done': // init 'done':
{ {
done.head = -1; done.head = -1;
for (int i = 0; i < N; i++) for (int i = 0; i < N; i++)
{ {
done.headtails[i] = -1; done.headtails[i] = -1;
#if ZDEBUG #if ZDEBUG
done.in[i] = false; done.in[i] = false;
#endif #endif
} }
} }
void insert(int index) void insert(int index)
{ {
#if ZDEBUG #if ZDEBUG
assert(!done.in[index]); assert(!done.in[index]);
#endif #endif
int head = index & 1 ? index & ~(index & -index) : index, prevhead = -1; int head = index & 1 ? index & ~(index & -index) : index, prevhead = -1;
while (done.headtails[head] == -1 || index < done.headtails[head]) while (done.headtails[head] == -1 || index < done.headtails[head])
{ {
done.headtails[head] = index; done.headtails[head] = index;
prevhead = head, head = head & ~(head & -head); prevhead = head, head = head & ~(head & -head);
} }
if (done.headtails[head] < index) if (done.headtails[head] < index)
{ {
if (prevhead == -1) if (prevhead == -1)
{ {
assert(done.headtails[head] == head); assert(done.headtails[head] == head);
done.next[head] = index; done.next[head] = index;
} }
else else
{ {
int tophalftail = done.headtails[prevhead - 1]; int tophalftail = done.headtails[prevhead - 1];
assert(tophalftail != -1); assert(tophalftail != -1);
done.next[tophalftail] = index; done.next[tophalftail] = index;
} }
} }
@ -556,35 +563,35 @@ void calculate_simplifications(void)
{ {
done.head = index; done.head = index;
} }
int n = ~index & M; int n = ~index & M;
int tail = index & 1 ? index : index | (n & -n), prevtail = -1; int tail = index & 1 ? index : index | (n & -n), prevtail = -1;
while (done.headtails[tail] == -1 || done.headtails[tail] < index) while (done.headtails[tail] == -1 || done.headtails[tail] < index)
{ {
done.headtails[tail] = index; done.headtails[tail] = index;
prevtail = tail, tail = tail | (n = ~tail & M, n & -n); prevtail = tail, tail = tail | (n = ~tail & M, n & -n);
} }
if (index < done.headtails[tail]) if (index < done.headtails[tail])
{ {
if (prevtail == -1) if (prevtail == -1)
{ {
assert(done.headtails[tail] == tail); assert(done.headtails[tail] == tail);
#if ZDEBUG #if ZDEBUG
assert(done.in[tail]); assert(done.in[tail]);
#endif #endif
done.next[index] = tail; done.next[index] = tail;
} }
else else
{ {
int bottomhalfhead = done.headtails[prevtail + 1]; int bottomhalfhead = done.headtails[prevtail + 1];
assert(bottomhalfhead != -1); assert(bottomhalfhead != -1);
done.next[index] = bottomhalfhead; done.next[index] = bottomhalfhead;
} }
} }
@ -592,26 +599,26 @@ void calculate_simplifications(void)
{ {
done.next[index] = -1; done.next[index] = -1;
} }
#if ZDEBUG #if ZDEBUG
done.in[index] = true; done.in[index] = true;
#endif #endif
} }
append(W, 0), lookup[W].kind = ek_W; append(W, 0), lookup[W].kind = ek_W;
append(X, 0), lookup[X].kind = ek_X; append(X, 0), lookup[X].kind = ek_X;
append(Y, 0), lookup[Y].kind = ek_Y; append(Y, 0), lookup[Y].kind = ek_Y;
append(Z, 0), lookup[Z].kind = ek_Z; append(Z, 0), lookup[Z].kind = ek_Z;
append(0, 1), lookup[0].kind = ek_0; append(0, 1), lookup[0].kind = ek_0;
append(M, 1), lookup[M].kind = ek_1; append(M, 1), lookup[M].kind = ek_1;
for (int iterations = 1; todo.n && iterations <= N; iterations++) for (int iterations = 1; todo.n && iterations <= N; iterations++)
{ {
uint16_t truthtable = pop(); uint16_t truthtable = pop();
insert(truthtable); insert(truthtable);
int cost = lookup[truthtable].cost; int cost = lookup[truthtable].cost;
if (verbose) if (verbose)
@ -893,9 +900,9 @@ void get_simplifications(void)
"This may take a while." "\n" "This may take a while." "\n"
""); "");
} }
rebuild: {}; rebuild: {};
if (!quiet && !verbose) if (!quiet && !verbose)
{ {
puts("re-run with '-v' to watch progress"); puts("re-run with '-v' to watch progress");
@ -1439,6 +1446,61 @@ int main(int argc, char* const* argv)
} }
} }
} }
else if (print_stats)
{
printf("statistics:" "\n");
uintmax_t total_cost = 0;
int n = 0;
int max_cost = 0;
for (int i = 0; i < N; i++)
{
int cost = lookup[i].cost;
if (cost != INT_MAX)
{
if (max_cost < cost)
{
max_cost = cost;
}
total_cost += cost;
n += 1;
}
}
if (n)
{
printf("minimum operators needed: 1" "\n");
printf("maximum operators needed: %i" "\n", max_cost);
double average = (double) total_cost / n;
printf("average operators needed: %g" "\n", average);
double working = 0.0;
for (int i = 0; i < N; i++)
{
int cost = lookup[i].cost;
if (cost != INT_MAX)
{
working += pow(cost - average, 2);
}
}
double stddev = sqrt(working / (n - 1));
printf("standard deviation: %g" "\n", stddev);
}
else
{
puts("everything unreachable: no statistics to give");
}
}
else if (command) else if (command)
{ {
uint16_t truthtable = evaluate(command); uint16_t truthtable = evaluate(command);
@ -1456,28 +1518,6 @@ int main(int argc, char* const* argv)
} }
else else
{ {
// Let's humble-brag just a little.
#if 0
{
int max_cost = 0;
for (int i = 0; i < N; i++)
{
int cost = lookup[i].cost;
if (cost != INT_MAX && max_cost < cost)
{
max_cost = cost;
}
}
puts("");
printf("I can simplify any tree down to %i operators or less.\n",
max_cost);
puts("");
}
#endif
if (!quiet) if (!quiet)
{ {
puts("Use C-style syntax for boolean operators and expressions."); puts("Use C-style syntax for boolean operators and expressions.");
@ -1486,13 +1526,13 @@ int main(int argc, char* const* argv)
puts("Comparison operators are also suppported."); puts("Comparison operators are also suppported.");
} }
for (char* line; (line = readline(">>> ")); free(line)) for (char* line; (line = readline(">>> ")); free(line))
{ {
if (!*line) continue; if (!*line) continue;
add_history(line); add_history(line);
uint16_t truthtable = evaluate(line); uint16_t truthtable = evaluate(line);
// printf("truthtable = 0b%016b\n", truthtable); // printf("truthtable = 0b%016b\n", truthtable);

View file

@ -16,7 +16,7 @@ cflags += -O3
cflags += -Wno-unused cflags += -Wno-unused
ldflags += -lreadline ldflags += -lreadline -lm
/tmp/4-variable-simplifier: main.c /tmp/4-variable-simplifier: main.c
$(cc) $(cppflags) $(cflags) $< -o $@ $(ldflags) $(cc) $(cppflags) $(cflags) $< -o $@ $(ldflags)