1642 lines
31 KiB
C
1642 lines
31 KiB
C
|
|
#if 0
|
|
#define _GNU_SOURCE
|
|
|
|
#include <stdarg.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
|
|
#include <avl.h>
|
|
|
|
#define argv0 \
|
|
program_invocation_name
|
|
|
|
#define TODO \
|
|
assert(!"TODO");
|
|
|
|
#define CHECK \
|
|
assert(!"CHECK");
|
|
|
|
#ifdef DEBUG
|
|
unsigned debug_depth = 0;
|
|
|
|
#define ENTER \
|
|
printf("%*s" "<%s>\n", debug_depth++, "", __PRETTY_FUNCTION__);
|
|
|
|
#define EXIT \
|
|
printf("%*s" "</%s>\n", --debug_depth, "", __PRETTY_FUNCTION__);
|
|
|
|
#define dpvs(x) \
|
|
printf("%*s" "%s = \"%s\"\n", debug_depth, "", #x, x);
|
|
|
|
#define dpvu(x) \
|
|
printf("%*s" "%s = %u\n", debug_depth, "", #x, x);
|
|
|
|
#define dpvlu(x) \
|
|
printf("%*s" "%s = %lu\n", debug_depth, "", #x, x);
|
|
|
|
#define dpvi(x) \
|
|
printf("%*s" "%s = %i\n", debug_depth, "", #x, x);
|
|
|
|
#define dpvb(x) \
|
|
printf("%*s" "%s = %s\n", debug_depth, "", #x, (x) ? "true" : "false");
|
|
|
|
#define ddprintf(fmt, ...) \
|
|
printf("%*s" fmt, debug_depth, "", ## __VA_ARGS__);
|
|
#else
|
|
#define ENTER ;
|
|
|
|
#define EXIT ;
|
|
|
|
#define dpvs(x) ;
|
|
|
|
#define dpvu(x) ;
|
|
|
|
#define dpvlu(x) ;
|
|
|
|
#define dpvi(x) ;
|
|
|
|
#define dpvb(x) ;
|
|
|
|
#define ddprintf(fmt, ...) ;
|
|
#endif
|
|
|
|
void* smalloc(size_t size)
|
|
{
|
|
void* ptr = malloc(size);
|
|
|
|
if (!ptr)
|
|
{
|
|
TODO;
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
void* srealloc(void* oldptr, size_t newsize)
|
|
{
|
|
void* newptr = realloc(oldptr, newsize);
|
|
|
|
if (!newptr)
|
|
{
|
|
TODO;
|
|
}
|
|
|
|
return newptr;
|
|
}
|
|
|
|
struct cmdln_flags
|
|
{
|
|
const char* input_file;
|
|
|
|
bool dump_automata, verbose;
|
|
};
|
|
|
|
struct cmdln_flags* parse_args(
|
|
int argc, char* const* argv)
|
|
{
|
|
ENTER;
|
|
|
|
const char* input_file = NULL;
|
|
|
|
bool dump_automata = false;
|
|
|
|
bool verbose = false;
|
|
|
|
int i = 1;
|
|
|
|
while (i < argc)
|
|
{
|
|
const char* arg = argv[i];
|
|
|
|
dpvs(arg);
|
|
|
|
if (arg[0] == '-')
|
|
{
|
|
if (!strcmp(arg, "-d"))
|
|
{
|
|
dump_automata = true, i++;
|
|
}
|
|
else if (!strcmp(arg, "-v"))
|
|
{
|
|
verbose = true, i++;
|
|
}
|
|
else
|
|
{
|
|
TODO;
|
|
}
|
|
}
|
|
else if (input_file)
|
|
{
|
|
TODO;
|
|
}
|
|
else
|
|
{
|
|
input_file = arg, i++;
|
|
}
|
|
}
|
|
|
|
if (!input_file)
|
|
{
|
|
printf("%s: missing input file!\n", argv0);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
struct cmdln_flags* flags = smalloc(sizeof(*flags));
|
|
|
|
flags->input_file = input_file;
|
|
|
|
flags->dump_automata = dump_automata;
|
|
|
|
flags->verbose = verbose;
|
|
|
|
EXIT;
|
|
return flags;
|
|
}
|
|
|
|
void free_cmdln_flags(
|
|
struct cmdln_flags* this)
|
|
{
|
|
TODO;
|
|
}
|
|
|
|
struct quack
|
|
{
|
|
void** data;
|
|
size_t i, n, cap;
|
|
};
|
|
|
|
struct quack* new_quack(void)
|
|
{
|
|
ENTER;
|
|
|
|
struct quack* this = smalloc(sizeof(*this));
|
|
|
|
this->data = NULL;
|
|
|
|
this->i = 0;
|
|
this->n = 0;
|
|
this->cap = 0;
|
|
|
|
EXIT;
|
|
return this;
|
|
}
|
|
|
|
void quack_append(
|
|
struct quack* this,
|
|
void* element)
|
|
{
|
|
ENTER;
|
|
|
|
dpvlu(this->i);
|
|
dpvlu(this->n);
|
|
dpvlu(this->cap);
|
|
|
|
if (this->n == this->cap)
|
|
{
|
|
size_t oldcap = this->cap;
|
|
size_t newcap = oldcap << 1 ?: 1;
|
|
|
|
this->data = srealloc(this->data, sizeof(*this->data) * newcap);
|
|
|
|
if (this->i)
|
|
{
|
|
TODO;
|
|
}
|
|
|
|
this->cap = newcap;
|
|
|
|
dpvlu(this->cap);
|
|
}
|
|
|
|
size_t index = ((this->i + this->n++) % this->cap);
|
|
|
|
// ddprintf("wrote to index %lu\n", index);
|
|
|
|
this->data[index] = element;
|
|
|
|
dpvlu(this->i);
|
|
dpvlu(this->n);
|
|
dpvlu(this->cap);
|
|
|
|
EXIT;
|
|
}
|
|
|
|
void* quack_pop(
|
|
struct quack* this)
|
|
{
|
|
ENTER;
|
|
|
|
assert(this->n);
|
|
|
|
dpvlu(this->i);
|
|
dpvlu(this->n);
|
|
dpvlu(this->cap);
|
|
|
|
/* ddprintf("reading from index %lu\n", this->i);*/
|
|
|
|
void* retval = this->data[this->i];
|
|
|
|
this->i = (this->i + 1) % this->cap, this->n--;
|
|
|
|
dpvlu(this->i);
|
|
dpvlu(this->n);
|
|
dpvlu(this->cap);
|
|
|
|
EXIT;
|
|
return retval;
|
|
}
|
|
|
|
void free_quack(
|
|
struct quack* this)
|
|
{
|
|
ENTER;
|
|
|
|
free(this);
|
|
|
|
EXIT;
|
|
}
|
|
|
|
struct automata;
|
|
|
|
struct automataset
|
|
{
|
|
struct avl_tree_t* tree;
|
|
|
|
size_t refcount;
|
|
};
|
|
|
|
int ptrcmp(const void* a, const void* b)
|
|
{
|
|
if (a > b)
|
|
return +1;
|
|
|
|
if (a < b)
|
|
return -1;
|
|
|
|
return +0;
|
|
}
|
|
|
|
struct automataset* new_automataset(void)
|
|
{
|
|
ENTER;
|
|
|
|
struct automataset* this = smalloc(sizeof(*this));
|
|
|
|
this->tree = avl_alloc_tree(ptrcmp, NULL);
|
|
|
|
this->refcount = 1;
|
|
|
|
EXIT;
|
|
return this;
|
|
}
|
|
|
|
bool automataset_is_nonempty(
|
|
struct automataset* this)
|
|
{
|
|
return !!this->tree->head;
|
|
}
|
|
|
|
bool automataset_add(
|
|
struct automataset* this,
|
|
struct automata* automata)
|
|
{
|
|
bool retval;
|
|
ENTER;
|
|
|
|
errno = 0;
|
|
|
|
struct avl_node_t* node = avl_insert(this->tree, automata);
|
|
|
|
if (node)
|
|
{
|
|
retval = true;
|
|
}
|
|
else if (errno == EEXIST)
|
|
{
|
|
retval = false;
|
|
}
|
|
else
|
|
{
|
|
TODO;
|
|
}
|
|
|
|
EXIT;
|
|
return retval;
|
|
}
|
|
|
|
void automataset_foreach(
|
|
struct automataset* this,
|
|
void (*callback)(
|
|
struct automata*))
|
|
{
|
|
ENTER;
|
|
|
|
for (struct avl_node_t* node = this->tree->head; node; node = node->next)
|
|
{
|
|
callback(node->item);
|
|
}
|
|
|
|
EXIT;
|
|
}
|
|
|
|
struct automata* automataset_pop(
|
|
struct automataset* this)
|
|
{
|
|
ENTER;
|
|
|
|
assert(this->tree->head);
|
|
|
|
struct automata* retval = this->tree->head->item;
|
|
|
|
avl_delete_node(this->tree, this->tree->head);
|
|
|
|
EXIT;
|
|
return retval;
|
|
}
|
|
|
|
void free_automataset(
|
|
struct automataset* this)
|
|
{
|
|
ENTER;
|
|
|
|
if (!--this->refcount)
|
|
{
|
|
avl_free_tree(this->tree);
|
|
|
|
free(this);
|
|
}
|
|
|
|
EXIT;
|
|
}
|
|
|
|
struct automata
|
|
{
|
|
bool is_accepting;
|
|
|
|
struct automata *on[2];
|
|
};
|
|
|
|
struct automata* new_automata(void)
|
|
{
|
|
ENTER;
|
|
|
|
struct automata* this = malloc(sizeof(*this));
|
|
|
|
this->is_accepting = false;
|
|
|
|
this->on[0] = NULL;
|
|
this->on[1] = NULL;
|
|
|
|
EXIT;
|
|
return this;
|
|
}
|
|
|
|
void dump_automata(
|
|
struct automata* start,
|
|
const char* fmt, ...)
|
|
{
|
|
ENTER;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
static size_t dump_id = 0;
|
|
|
|
snprintf(path, PATH_MAX, "/tmp/%05lu.dot", dump_id++);
|
|
|
|
FILE* stream = fopen(path, "w");
|
|
|
|
fprintf(stream, ""
|
|
"digraph {" "\n"
|
|
"\t" "rankdir = LR;" "\n"
|
|
"\t"
|
|
"\t" "node [" "\n"
|
|
"\t" "\t" "shape = circle" "\n"
|
|
"\t" "\t" "label = \"\"" "\n"
|
|
"\t" "];" "\n"
|
|
"\t" "\n"
|
|
"");
|
|
|
|
va_list va;
|
|
va_start(va, fmt);
|
|
|
|
vfprintf(stream, fmt, va);
|
|
|
|
va_end(va);
|
|
|
|
struct quack* todo = new_quack();
|
|
|
|
struct automataset* queued = new_automataset();
|
|
|
|
if (automataset_add(queued, start))
|
|
{
|
|
quack_append(todo, start);
|
|
}
|
|
|
|
while (todo->n)
|
|
{
|
|
struct automata* state = quack_pop(todo);
|
|
|
|
if (state->is_accepting)
|
|
{
|
|
fprintf(stream, "\"%p\" [ shape = doublecircle ];\n", state);
|
|
}
|
|
|
|
for (int on = 0; on < 2; on++)
|
|
{
|
|
struct automata* to = state->on[on];
|
|
|
|
if (to)
|
|
{
|
|
fprintf(stream, "\"%p\" -> \"%p\" [label = \"%i\"];\n", state, to, on);
|
|
|
|
if (automataset_add(queued, to))
|
|
{
|
|
quack_append(todo, to);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fprintf(stream, ""
|
|
"}" "\n"
|
|
"");
|
|
|
|
free_quack(todo);
|
|
|
|
free_automataset(queued);
|
|
|
|
fclose(stream);
|
|
|
|
EXIT;
|
|
}
|
|
|
|
struct automata* create_variable_automata(
|
|
int depth,
|
|
const struct cmdln_flags* flags,
|
|
unsigned variable_index,
|
|
bool value,
|
|
unsigned number_of_variables)
|
|
{
|
|
ENTER;
|
|
|
|
assert(variable_index < number_of_variables);
|
|
|
|
if (flags->verbose)
|
|
{
|
|
printf("%*s" "variable(index = %u, value = %s):\n",
|
|
depth++, "", variable_index, value ? "true" : "false");
|
|
}
|
|
|
|
size_t total_number_of_states = 0;
|
|
|
|
struct automata* start = new_automata();
|
|
struct automata* moving = start;
|
|
|
|
total_number_of_states++;
|
|
|
|
for (unsigned i = 0; i < number_of_variables; i++)
|
|
{
|
|
struct automata* new = new_automata();
|
|
total_number_of_states++;
|
|
|
|
if (i == variable_index)
|
|
{
|
|
moving->on[value] = new;
|
|
}
|
|
else
|
|
{
|
|
moving->on[0] = new;
|
|
moving->on[1] = new;
|
|
}
|
|
|
|
moving = new;
|
|
}
|
|
|
|
moving->is_accepting = true;
|
|
|
|
if (flags->verbose)
|
|
{
|
|
printf("%*s" "returned automata with %lu states.\n",
|
|
depth, "", total_number_of_states);
|
|
}
|
|
|
|
EXIT;
|
|
return start;
|
|
}
|
|
|
|
struct automata* automata_union(
|
|
int depth,
|
|
const struct cmdln_flags* flags,
|
|
const struct automata* left,
|
|
const struct automata* right)
|
|
{
|
|
ENTER;
|
|
|
|
if (flags->verbose)
|
|
{
|
|
printf("%*s" "union():\n", depth++, "");
|
|
}
|
|
|
|
struct bundle
|
|
{
|
|
const struct automata *left, *right; // key
|
|
|
|
struct automata* new;
|
|
};
|
|
|
|
struct bundle* new_bundle(
|
|
const struct automata *left,
|
|
const struct automata* right,
|
|
struct automata* new)
|
|
{
|
|
ENTER;
|
|
|
|
struct bundle* this = smalloc(sizeof(*this));
|
|
|
|
this->left = left;
|
|
this->right = right;
|
|
|
|
this->new = new;
|
|
|
|
EXIT;
|
|
return this;
|
|
}
|
|
|
|
int compare(const void* a, const void* b)
|
|
{
|
|
const struct bundle *A = a, *B = b;
|
|
|
|
return ptrcmp(A->left, B->left) ?: ptrcmp(A->right, B->right);
|
|
}
|
|
|
|
void append(struct bundle* bundle)
|
|
{
|
|
ENTER;
|
|
|
|
EXIT;
|
|
}
|
|
|
|
size_t total_number_of_states = 0;
|
|
|
|
struct avl_tree_t* mapping = avl_alloc_tree(compare, free);
|
|
|
|
struct automata* start = new_automata();
|
|
|
|
total_number_of_states++;
|
|
|
|
struct quack* todo = new_quack();
|
|
|
|
// initial state:
|
|
{
|
|
struct bundle* new = new_bundle(left, right, start);
|
|
|
|
quack_append(todo, new);
|
|
|
|
avl_insert(mapping, new);
|
|
}
|
|
|
|
while (todo->n)
|
|
{
|
|
struct bundle* bundle = quack_pop(todo);
|
|
|
|
const struct automata *const left = bundle->left;
|
|
const struct automata *const right = bundle->right;
|
|
|
|
struct automata* const new = bundle->new;
|
|
|
|
new->is_accepting = false
|
|
|| (left && left->is_accepting)
|
|
|| (right && right->is_accepting);
|
|
|
|
for (int value = 0; value < 2; value++)
|
|
{
|
|
struct automata* left_to = left ? left->on[value] : NULL;
|
|
struct automata* right_to = right ? right->on[value] : NULL;
|
|
|
|
if (left_to || right_to)
|
|
{
|
|
struct avl_node_t* node = avl_search(mapping,
|
|
&(struct bundle) {left_to, right_to, NULL});
|
|
|
|
if (node)
|
|
{
|
|
struct bundle* bundle_to = node->item;
|
|
|
|
new->on[value] = bundle_to->new;
|
|
}
|
|
else
|
|
{
|
|
struct automata* new_to = new_automata();
|
|
|
|
total_number_of_states++;
|
|
|
|
new->on[value] = new_to;
|
|
|
|
struct bundle* bundle_to = new_bundle(
|
|
left_to, right_to, new_to);
|
|
|
|
avl_insert(mapping, bundle_to);
|
|
|
|
quack_append(todo, bundle_to);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flags->dump_automata)
|
|
{
|
|
dump_automata(start, "label = \"automata_union\"");
|
|
}
|
|
}
|
|
|
|
if (flags->verbose)
|
|
{
|
|
printf("%*s" "returned automata with %lu states" "\n",
|
|
depth, "", total_number_of_states);
|
|
}
|
|
|
|
avl_free_tree(mapping);
|
|
|
|
free_quack(todo);
|
|
|
|
EXIT;
|
|
return start;
|
|
}
|
|
|
|
struct automata* automata_intersection(
|
|
const struct cmdln_flags* flags,
|
|
const struct automata* left,
|
|
const struct automata* right)
|
|
{
|
|
TODO;
|
|
}
|
|
|
|
struct automata* automata_simplify(
|
|
int depth,
|
|
const struct cmdln_flags* flags,
|
|
struct automata* original)
|
|
{
|
|
ENTER;
|
|
|
|
if (flags->verbose)
|
|
{
|
|
printf("%*s" "simplify():\n", depth++, "");
|
|
}
|
|
|
|
struct pair
|
|
{
|
|
struct automata *a, *b;
|
|
};
|
|
|
|
struct pair* new_pair(struct automata* a, struct automata* b)
|
|
{
|
|
ENTER;
|
|
|
|
struct pair* this = smalloc(sizeof(*this));
|
|
|
|
assert(a < b);
|
|
|
|
this->a = a;
|
|
this->b = b;
|
|
|
|
EXIT;
|
|
return this;
|
|
}
|
|
|
|
struct dependent_of_node
|
|
{
|
|
struct pair pair;
|
|
|
|
struct avl_tree_t* dependent_of; // tree of pairs
|
|
};
|
|
|
|
struct dependent_of_node* new_dependent_of_node(
|
|
struct automata* a, struct automata* b)
|
|
{
|
|
ENTER;
|
|
|
|
TODO;
|
|
#if 0
|
|
struct dependent_of_node* this = smalloc(sizeof(*this));
|
|
|
|
this->pair.a = a;
|
|
this->pair.b = b;
|
|
|
|
this->dependent_of = avl_alloc_tree(compare_pairs, free);
|
|
|
|
EXIT;
|
|
return this;
|
|
#endif
|
|
}
|
|
|
|
int compare_dependent_of_nodes(const void* a, const void* b)
|
|
{
|
|
TODO;
|
|
#if 0
|
|
int cmp = 0;
|
|
const struct dependent_of_node *A = a, *B = b;
|
|
ENTER;
|
|
|
|
cmp = compare_pairs(&A->pair, &B->pair);
|
|
|
|
EXIT;
|
|
return cmp;
|
|
#endif
|
|
}
|
|
|
|
void free_dependent_of_node(void* ptr)
|
|
{
|
|
TODO;
|
|
#if 0
|
|
struct dependent_of_node* this = ptr;
|
|
ENTER;
|
|
|
|
avl_free_tree(this->dependent_of);
|
|
|
|
free(this);
|
|
|
|
EXIT;
|
|
#endif
|
|
}
|
|
|
|
struct task
|
|
{
|
|
struct pair pair;
|
|
|
|
unsigned hopcount;
|
|
};
|
|
|
|
struct task* new_task(
|
|
struct automata* a, struct automata* b,
|
|
unsigned hopcount)
|
|
{
|
|
ENTER;
|
|
|
|
struct task* this = smalloc(sizeof(*this));
|
|
|
|
this->pair.a = a;
|
|
this->pair.b = b;
|
|
|
|
this->hopcount = hopcount;
|
|
|
|
EXIT;
|
|
return this;
|
|
}
|
|
|
|
int compare_tasks(const void* a, const void* b)
|
|
{
|
|
const struct task* A = a, *B = b;
|
|
int cmp;
|
|
ENTER;
|
|
|
|
if (A->hopcount > B->hopcount)
|
|
cmp = +1;
|
|
else if (A->hopcount < B->hopcount)
|
|
cmp = -1;
|
|
else
|
|
cmp = +0;
|
|
|
|
EXIT;
|
|
return cmp;
|
|
}
|
|
|
|
struct heap
|
|
{
|
|
void** data;
|
|
|
|
unsigned n, cap;
|
|
|
|
int (*cmp)(const void*, const void*);
|
|
};
|
|
|
|
struct heap* new_heap(
|
|
int (*cmp)(const void*, const void*))
|
|
{
|
|
ENTER;
|
|
|
|
struct heap* this = malloc(sizeof(*this));
|
|
|
|
this->cmp = cmp;
|
|
|
|
this->data = NULL;
|
|
this->n = 0;
|
|
this->cap = 0;
|
|
|
|
EXIT;
|
|
return this;
|
|
}
|
|
|
|
void* heap_pop(struct heap* this)
|
|
{
|
|
ENTER;
|
|
|
|
assert(this->n);
|
|
|
|
void* retval = this->data[0], *swap;
|
|
|
|
if (--this->n)
|
|
{
|
|
unsigned l, r, smallest, i = 0;
|
|
void** const data = this->data;
|
|
|
|
data[0] = data[this->n];
|
|
|
|
again: l = 2 * i + 1, r = 2 * i + 2, smallest = i;
|
|
|
|
if (l < this->n && this->cmp(data[l], data[i]) < 0)
|
|
smallest = l;
|
|
|
|
if (r < this->n && this->cmp(data[r], data[smallest]) < 0)
|
|
smallest = r;
|
|
|
|
if (smallest != i)
|
|
{
|
|
swap = data[i];
|
|
data[i] = data[smallest];
|
|
data[smallest] = swap;
|
|
|
|
i = smallest;
|
|
|
|
goto again; // forgive my father for I have sinned.
|
|
}
|
|
}
|
|
|
|
EXIT;
|
|
return retval;
|
|
}
|
|
|
|
void heap_push(struct heap* this, void* new)
|
|
{
|
|
ENTER;
|
|
|
|
if (this->n + 1 > this->cap)
|
|
{
|
|
this->cap = this->cap << 1 ?: 1;
|
|
|
|
this->data = realloc(this->data, sizeof(*this->data) * this->cap);
|
|
}
|
|
|
|
size_t i = this->n++, j;
|
|
void** const data = this->data, *swap;
|
|
|
|
data[i] = new;
|
|
|
|
for (; i > 0 && this->cmp(data[j = (i - 1) / 2], data[i]) > 0; i = j)
|
|
{
|
|
swap = data[i];
|
|
data[i] = data[j];
|
|
data[j] = swap;
|
|
}
|
|
|
|
EXIT;
|
|
}
|
|
|
|
void add_dep(
|
|
struct avl_tree_t* dependent_of,
|
|
struct automata* a_on, struct automata* b_on,
|
|
struct automata* a_of, struct automata* b_of)
|
|
{
|
|
ENTER;
|
|
|
|
assert(a_on < b_on);
|
|
|
|
if (a_of > b_of)
|
|
{
|
|
struct automata* swap = b_of;
|
|
|
|
b_of = a_of, a_of = swap;
|
|
}
|
|
|
|
struct avl_node_t* node = avl_search(dependent_of,
|
|
&(struct pair){a_of, b_of});
|
|
|
|
if (node)
|
|
{
|
|
struct dependent_of_node* old = node->item;
|
|
|
|
if (!avl_search(old->dependent_of, &(struct pair){a_on, b_on}))
|
|
{
|
|
struct pair* dep = new_pair(a_on, b_on);
|
|
|
|
avl_insert(old->dependent_of, dep);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct dependent_of_node* new = new_dependent_of_node(a_of, b_of);
|
|
|
|
struct pair* dep = new_pair(a_on, b_on);
|
|
|
|
avl_insert(new->dependent_of, dep);
|
|
|
|
avl_insert(dependent_of, new);
|
|
}
|
|
|
|
EXIT;
|
|
}
|
|
|
|
|
|
struct automataset* build_universe(struct automata* start)
|
|
{
|
|
ENTER;
|
|
|
|
struct automataset* universe = new_automataset();
|
|
|
|
struct quack* todo = new_quack();
|
|
|
|
quack_append(todo, start);
|
|
|
|
automataset_add(universe, start);
|
|
|
|
while (todo->n)
|
|
{
|
|
struct automata* node = quack_pop(todo);
|
|
|
|
for (int value = 0; value < 2; value++)
|
|
{
|
|
struct automata* const to = node->on[value];
|
|
|
|
if (to && automataset_add(universe, to))
|
|
{
|
|
quack_append(todo, to);
|
|
}
|
|
}
|
|
}
|
|
|
|
free_quack(todo);
|
|
|
|
EXIT;
|
|
return universe;
|
|
}
|
|
|
|
struct avl_tree_t* dependent_of = avl_alloc_tree(
|
|
compare_dependent_of_nodes, free_dependent_of_node);
|
|
|
|
struct automataset* universe = build_universe(original);
|
|
|
|
struct heap* todo = new_heap(compare_tasks);
|
|
|
|
automataset_foreach(universe, ({
|
|
void runme(struct automata* a) {
|
|
automataset_foreach(universe, ({
|
|
void runme(struct automata* b) {
|
|
if (a < b)
|
|
{
|
|
bool unequal = false;
|
|
|
|
if (a->is_accepting != b->is_accepting)
|
|
{
|
|
unequal = true;
|
|
}
|
|
else for (int value = 0; !unequal && value < 2; value++)
|
|
{
|
|
struct automata* a_to = a->on[value];
|
|
struct automata* b_to = b->on[value];
|
|
|
|
if (!a_to != !b_to)
|
|
{
|
|
unequal = true;
|
|
}
|
|
else if (a_to && b_to)
|
|
{
|
|
add_dep(dependent_of, a, b, a_to, b_to);
|
|
}
|
|
}
|
|
|
|
if (unequal)
|
|
{
|
|
heap_push(todo, new_task(a, b, 0));
|
|
}
|
|
}
|
|
}
|
|
runme;
|
|
}));
|
|
}
|
|
runme;
|
|
}));
|
|
|
|
TODO;
|
|
#if 0
|
|
|
|
#ifdef VERBOSE
|
|
void handler12(int _)
|
|
{
|
|
char ptr[200] = {};
|
|
|
|
size_t len = snprintf(ptr, 200,
|
|
"\e[K" "zebu: automata simplify (allocating sets): %ju of %ju (%.2f%%)\r",
|
|
count, n, (((double) count * 100) / n));
|
|
|
|
if (write(1, ptr, len) != len)
|
|
{
|
|
abort();
|
|
}
|
|
}
|
|
|
|
{
|
|
count = 0, n = automataset_len(universe);
|
|
|
|
#ifdef LINUX_PLATFORM
|
|
signal(SIGALRM, handler12);
|
|
#else
|
|
#ifdef WINDOWS_PLATFORM
|
|
timer_handler = handler12;
|
|
#else
|
|
#error bad platform
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
struct avl_tree_t* connections = avl_alloc_tree(compare_same_as_nodes, free_same_as_node);
|
|
|
|
automataset_foreach(universe, ({
|
|
void runme(void* ptr) {
|
|
struct automata* a = ptr;
|
|
|
|
struct automataset* uni = automataset_clone(universe);
|
|
|
|
struct same_as_node* sa = new_same_as_node(a, uni);
|
|
|
|
avl_insert(connections, sa);
|
|
|
|
#ifdef VERBOSE
|
|
count++;
|
|
#endif
|
|
}
|
|
runme;
|
|
}));
|
|
|
|
struct avl_tree_t* connections = avl_alloc_tree(compare_same_as_nodes, free_same_as_node);
|
|
|
|
automataset_foreach(universe, ({
|
|
void runme(void* ptr) {
|
|
struct automata* a = ptr;
|
|
|
|
struct automataset* uni = automataset_clone(universe);
|
|
|
|
struct same_as_node* sa = new_same_as_node(a, uni);
|
|
|
|
avl_insert(connections, sa);
|
|
|
|
#ifdef VERBOSE
|
|
count++;
|
|
#endif
|
|
}
|
|
runme;
|
|
}));
|
|
|
|
#ifdef VERBOSE
|
|
unsigned completed = 0;
|
|
|
|
void handler2(int _)
|
|
{
|
|
char buffer[1000] = {};
|
|
|
|
unsigned total = completed + heap_len(todo);
|
|
|
|
size_t len = snprintf(buffer, sizeof(buffer),
|
|
"\e[K" "zebu: automata simplify (percolate): %u of %u (%.2f%%)\r",
|
|
completed, total,
|
|
(double) completed * 100 / total);
|
|
|
|
if (write(1, buffer, len) != len)
|
|
{
|
|
abort();
|
|
}
|
|
}
|
|
|
|
#ifdef LINUX_PLATFORM
|
|
signal(SIGALRM, handler2);
|
|
#else
|
|
#ifdef WINDOWS_PLATFORM
|
|
timer_handler = handler2;
|
|
#else
|
|
#error bad platform
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
while (heap_is_nonempty(todo))
|
|
{
|
|
#ifdef VERBOSE
|
|
completed++;
|
|
#endif
|
|
|
|
struct task* task = heap_pop(todo);
|
|
|
|
if (mark_as_unequal(connections, &task->pair))
|
|
{
|
|
struct avl_node_t* node = avl_search(dependent_of, &task->pair);
|
|
|
|
if (node)
|
|
{
|
|
struct dependent_of_node* dep = node->item;
|
|
|
|
unsigned hopcount = task->hopcount + 1;
|
|
|
|
avl_tree_foreach(dep->dependent_of, ({
|
|
void runme(void* ptr) {
|
|
const struct pair* pair = ptr;
|
|
heap_push(todo, new_task(pair->a, pair->b, hopcount));
|
|
}
|
|
runme;
|
|
}));
|
|
}
|
|
}
|
|
|
|
free(task);
|
|
}
|
|
|
|
// consider which states can reach any accepting state.
|
|
|
|
struct automata* new_start = clone(connections, original);
|
|
|
|
avl_free_tree(dependent_of);
|
|
|
|
free_automataset(universe);
|
|
|
|
avl_free_tree(connections);
|
|
|
|
free_heap(todo);
|
|
|
|
#ifdef VERBOSE
|
|
#ifdef LINUX_PLATFORM
|
|
signal(SIGALRM, default_sighandler);
|
|
#else
|
|
#ifdef WINDOWS_PLATFORM
|
|
timer_handler = default_sighandler;
|
|
#else
|
|
#error bad platform
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
EXIT;
|
|
return new_start;
|
|
|
|
#endif
|
|
|
|
size_t total_number_of_states = 0;
|
|
|
|
TODO;
|
|
|
|
if (flags->verbose)
|
|
{
|
|
printf("%*s" "returned automata with %lu states" "\n",
|
|
depth, "", total_number_of_states);
|
|
}
|
|
|
|
EXIT;
|
|
}
|
|
|
|
void free_automata(
|
|
struct automata* this)
|
|
{
|
|
TODO;
|
|
}
|
|
|
|
struct clause
|
|
{
|
|
struct clause_bundle {
|
|
unsigned variable;
|
|
bool value;
|
|
}* data;
|
|
|
|
size_t n, cap;
|
|
};
|
|
|
|
struct clause* new_clause(void)
|
|
{
|
|
struct clause* this = smalloc(sizeof(*this));
|
|
|
|
this->data = NULL;
|
|
|
|
this->n = 0;
|
|
this->cap = 0;
|
|
|
|
return this;
|
|
}
|
|
|
|
void clause_append(
|
|
struct clause* this,
|
|
unsigned variable,
|
|
bool value)
|
|
{
|
|
ENTER;
|
|
|
|
if (this->n == this->cap)
|
|
{
|
|
this->cap = this->cap << 1 ?: 1;
|
|
|
|
this->data = srealloc(this->data, sizeof(*this->data) * this->cap);
|
|
}
|
|
|
|
this->data[this->n++] = (struct clause_bundle) {variable, value};
|
|
|
|
EXIT;
|
|
}
|
|
|
|
void free_clause(
|
|
struct clause* this)
|
|
{
|
|
TODO;
|
|
}
|
|
|
|
struct problem
|
|
{
|
|
unsigned number_of_variables;
|
|
|
|
struct {
|
|
struct clause** data;
|
|
|
|
size_t n, cap;
|
|
} clauses;
|
|
};
|
|
|
|
struct problem* new_problem(
|
|
unsigned number_of_variables)
|
|
{
|
|
ENTER;
|
|
|
|
struct problem* this = smalloc(sizeof(*this));
|
|
|
|
this->number_of_variables = number_of_variables;
|
|
|
|
this->clauses.data = NULL;
|
|
this->clauses.n = 0;
|
|
this->clauses.cap = 0;
|
|
|
|
EXIT;
|
|
return this;
|
|
}
|
|
|
|
void problem_append(
|
|
struct problem* this,
|
|
struct clause* clause)
|
|
{
|
|
ENTER;
|
|
|
|
if (this->clauses.n == this->clauses.cap)
|
|
{
|
|
this->clauses.cap = this->clauses.cap << 1 ?: 1;
|
|
|
|
this->clauses.data = srealloc(
|
|
this->clauses.data,
|
|
sizeof(*this->clauses.data) * this->clauses.cap);
|
|
}
|
|
|
|
this->clauses.data[this->clauses.n++] = clause;
|
|
|
|
EXIT;
|
|
}
|
|
|
|
void free_problem(
|
|
struct problem* this)
|
|
{
|
|
TODO;
|
|
}
|
|
|
|
struct problems
|
|
{
|
|
struct problem** data;
|
|
size_t n, cap;
|
|
};
|
|
|
|
struct problems* new_problems(void)
|
|
{
|
|
ENTER;
|
|
|
|
struct problems* this = smalloc(sizeof(*this));
|
|
|
|
this->data = NULL;
|
|
this->n = 0;
|
|
this->cap = 0;
|
|
|
|
EXIT;
|
|
return this;
|
|
}
|
|
|
|
void problems_append(
|
|
struct problems* this,
|
|
struct problem* problem)
|
|
{
|
|
ENTER;
|
|
|
|
if (this->n == this->cap)
|
|
{
|
|
this->cap = this->cap << 1 ?: 1;
|
|
|
|
this->data = srealloc(this->data, sizeof(*this->data) * this->cap);
|
|
}
|
|
|
|
this->data[this->n++] = problem;
|
|
|
|
EXIT;
|
|
}
|
|
|
|
void free_problems(
|
|
struct problems* this)
|
|
{
|
|
TODO;
|
|
}
|
|
|
|
|
|
struct problems* parse(const char* file)
|
|
{
|
|
ENTER;
|
|
|
|
dpvs(file);
|
|
|
|
struct problems* problems = new_problems();
|
|
|
|
FILE* stream = fopen(file, "r");
|
|
|
|
char *line = NULL;
|
|
size_t len = 0;
|
|
ssize_t nread;
|
|
|
|
while ((nread = getline(&line, &len, stream)) > 0)
|
|
{
|
|
line[--nread] = 0;
|
|
|
|
dpvs(line);
|
|
|
|
if (line[0] == '\0') continue;
|
|
if (line[0] == 'c') continue;
|
|
|
|
if (line[0] != 'p')
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
|
|
unsigned number_of_variables;
|
|
unsigned number_of_clauses;
|
|
|
|
int r = sscanf(line, " p %u %u ", &number_of_variables, &number_of_clauses);
|
|
|
|
if (r < 2)
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
|
|
dpvu(number_of_variables);
|
|
|
|
dpvu(number_of_clauses);
|
|
|
|
struct problem* problem = new_problem(number_of_variables);
|
|
|
|
for (unsigned i = 0; i < number_of_clauses; i++)
|
|
{
|
|
nread = getline(&line, &len, stream);
|
|
|
|
if (nread < 0)
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
|
|
line[--nread] = 0;
|
|
|
|
dpvs(line);
|
|
|
|
struct clause* clause = new_clause();
|
|
|
|
char *moving = line;
|
|
|
|
for (char* word; (word = strtok_r(NULL, " ", &moving)); )
|
|
{
|
|
dpvs(word);
|
|
|
|
errno = 0;
|
|
|
|
char* m;
|
|
|
|
signed variable = strtol(word, &m, 10);
|
|
|
|
if (errno || *m)
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
|
|
bool value = true;
|
|
|
|
if (variable < 0)
|
|
{
|
|
variable = -variable;
|
|
value = !value;
|
|
}
|
|
|
|
dpvi(variable);
|
|
|
|
dpvb(value);
|
|
|
|
assert(variable > 0);
|
|
|
|
clause_append(clause, variable, value);
|
|
}
|
|
|
|
problem_append(problem, clause);
|
|
}
|
|
|
|
problems_append(problems, problem);
|
|
}
|
|
|
|
free(line);
|
|
fclose(stream);
|
|
|
|
EXIT;
|
|
return problems;
|
|
}
|
|
|
|
struct automata* build_automata(
|
|
struct cmdln_flags* flags,
|
|
struct problem* problem)
|
|
{
|
|
ENTER;
|
|
|
|
unsigned number_of_variables = problem->number_of_variables;
|
|
|
|
struct {
|
|
unsigned* data;
|
|
size_t n, cap;
|
|
} variable_lookup = {};
|
|
|
|
unsigned variable_to_index(signed variable)
|
|
{
|
|
unsigned retval;
|
|
ENTER;
|
|
|
|
assert(variable > 0);
|
|
|
|
dpvi(variable);
|
|
dpvlu(variable_lookup.cap);
|
|
|
|
if ((unsigned) variable < variable_lookup.cap)
|
|
{
|
|
dpvu(variable_lookup.data[variable]);
|
|
|
|
if (variable_lookup.data[variable] == (unsigned) -1)
|
|
{
|
|
variable_lookup.data[variable] = variable_lookup.n++;
|
|
}
|
|
|
|
dpvu(variable_lookup.data[variable]);
|
|
|
|
retval = variable_lookup.data[variable];
|
|
}
|
|
else
|
|
{
|
|
while ((unsigned) variable >= variable_lookup.cap)
|
|
{
|
|
size_t oldcap = variable_lookup.cap;
|
|
size_t newcap = oldcap << 1 ?: 1;
|
|
|
|
variable_lookup.data = srealloc(
|
|
variable_lookup.data,
|
|
sizeof(*variable_lookup.data) * newcap);
|
|
|
|
for (size_t i = oldcap; i < newcap; i++)
|
|
{
|
|
variable_lookup.data[i] = -1;
|
|
}
|
|
|
|
variable_lookup.cap = newcap;
|
|
|
|
dpvlu(variable_lookup.cap);
|
|
}
|
|
|
|
retval = variable_lookup.data[variable] = variable_lookup.n++;
|
|
}
|
|
|
|
dpvu(retval);
|
|
|
|
EXIT;
|
|
return retval;
|
|
}
|
|
|
|
int depth = 0;
|
|
|
|
for (unsigned i = 0, n = problem->clauses.n; i < n; i++)
|
|
{
|
|
struct clause* clause = problem->clauses.data[i];
|
|
|
|
struct automata* clause_automata = NULL;
|
|
|
|
depth++;
|
|
|
|
for (unsigned j = 0, m = clause->n; j < m; j++)
|
|
{
|
|
depth++;
|
|
|
|
struct clause_bundle bundle = clause->data[j];
|
|
|
|
unsigned index = variable_to_index(bundle.variable);
|
|
|
|
struct automata* variable_automata = create_variable_automata(
|
|
depth, flags,
|
|
index, bundle.value, number_of_variables);
|
|
|
|
if (flags->dump_automata)
|
|
{
|
|
dump_automata(variable_automata,
|
|
"label = \"%i (%u) = %s\"", bundle.variable, index,
|
|
bundle.value ? "true" : "false");
|
|
}
|
|
|
|
if (clause_automata)
|
|
{
|
|
struct automata* old = clause_automata;
|
|
|
|
struct automata* new = automata_union(
|
|
depth, flags, old, variable_automata);
|
|
|
|
struct automata* simp = automata_simplify(
|
|
depth, flags, new);
|
|
|
|
clause_automata = simp;
|
|
|
|
free_automata(new);
|
|
|
|
free_automata(old);
|
|
}
|
|
else
|
|
{
|
|
clause_automata = variable_automata;
|
|
}
|
|
|
|
depth--;
|
|
}
|
|
|
|
TODO;
|
|
|
|
/* if args.dump_automata:*/
|
|
/* title = " or ".join(f"{a} = {b}" for a, b in clause);*/
|
|
/* */
|
|
/* dump_automata(clause_automata, title);*/
|
|
/* */
|
|
/* clause_automatas.append(clause_automata); */
|
|
|
|
depth--;
|
|
}
|
|
|
|
TODO;
|
|
|
|
free(variable_lookup.data);
|
|
|
|
EXIT;
|
|
}
|
|
|
|
int main(
|
|
int argc,
|
|
char* const* argv)
|
|
{
|
|
ENTER;
|
|
|
|
struct cmdln_flags* flags = parse_args(argc, argv);
|
|
|
|
struct problems* problems = parse(flags->input_file);
|
|
|
|
for (unsigned i = 0, n = problems->n; i < n; i++)
|
|
{
|
|
struct problem* problem = problems->data[i];
|
|
|
|
struct automata* automata = build_automata(flags, problem);
|
|
|
|
TODO;
|
|
|
|
free_automata(automata);
|
|
}
|
|
|
|
free_problems(problems);
|
|
|
|
free_cmdln_flags(flags);
|
|
|
|
EXIT;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|