toaruos/userspace/solver.c
2012-02-08 12:01:25 -06:00

251 lines
6.6 KiB
C

/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* Brute-Force SAT Solver
*
* Copyright (c) 2012 Kevin Lange. All rights reserved.
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal with the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimers.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimers in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of Kevin Lange, nor the names of its contributors
* may be used to endorse or promote products derived from this Software
* without specific prior written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* WITH THE SOFTWARE.
*
*/
#define _XOPEN_SOURCE 500
#include <stdint.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "lib/list.h"
/* Width of a line to read for a clause. Clauses should not be too terrible long. */
#define LINE_WIDTH 4096
/* This brute-force solution uses bitsets, one bit per variable */
#define BITS_IN_SET 8
uint8_t * bit_sets = NULL;
uint64_t bit_sets_n = 0;
uint64_t variables = 0;
uint64_t clause_n = 0;
uint64_t collected = 0;
#define DEBUG 0
/* Clauses are stored as dynamic linked-lists of variable states */
list_t ** clauses;
/* Check a bit (variable) for the current state */
inline uint8_t checkbit(uint64_t bit) {
uint64_t set_id = bit / BITS_IN_SET;
uint8_t offset = bit - set_id * BITS_IN_SET;
uint8_t tmp = ((bit_sets[set_id] & (1 << offset)) > 0);
#if DEBUG
fprintf(stderr,"bit %ld [%ld:%d] = %d [%x, %d]\n", bit, set_id, offset, tmp, bit_sets[set_id], 1 << offset);
#endif
return tmp;
}
/* Initialize the bitsets */
inline void setup_bitsets() {
bit_sets_n = variables / BITS_IN_SET + 1;
bit_sets = (uint8_t *)malloc(sizeof(uint8_t) * bit_sets_n);
memset(bit_sets, 0x00, sizeof(uint8_t) * bit_sets_n);
}
/*
* Increment the state.
* The bit sets are like a very large integer, and we are
* incrementing by one.
*/
inline void next_bitset(uint64_t i) {
if (__builtin_expect(bit_sets[i] == 0xFF, 0)) {
bit_sets[i] = 0;
if (__builtin_expect(i + 1 == bit_sets_n, 0)) {
/* If we have run out of state, there is no solution. */
printf("UNSATISFIABLE\n");
exit(0);
}
next_bitset(i + 1);
} else {
bit_sets[i]++;
if (i + 1 == bit_sets_n) {
if (__builtin_expect(bit_sets[i] == (1 << variables), 0)) {
/* If we have run out of state, there is no solution. */
printf("UNSATISFIABLE\n");
exit(0);
}
}
}
}
/*
* We can determine if a clause is true by
* running through each of the values in it and checking
* if any of them are true (as a clause is a set of ORs)
*/
inline uint8_t is_clause_true(uint64_t i) {
list_t * clause = clauses[i];
foreach(node, clause) {
intptr_t var = (intptr_t)node->value;
if (var < 0) {
if (!checkbit(-var - 1)) return 1;
} else {
if (checkbit(var - 1)) return 1;
}
}
return 0;
}
inline uint8_t solved_with_bitset() {
for (uint64_t i = 0; i < clause_n; ++i) {
if (!is_clause_true(i)) {
/* If any clause is not true, the entire statement
* is false for this state */
return 0;
}
}
/* If all of the clauses are true, we are done. */
return 1;
}
/* Read a line of input and process it */
int read_line() {
char c = fgetc(stdin);
switch (c) {
case 'c':
/* Comment */
while (fgetc(stdin) != '\n');
break;
case 'p':
/* Problem definition */
{
fgetc(stdin);
while (fgetc(stdin) != ' ');
char num[30];
int offset = 0;
char input;
while ((input = fgetc(stdin)) != ' ') {
num[offset] = input;
offset++;
}
num[offset] = '\0';
variables = atoi(num);
offset = 0;
while ((input = fgetc(stdin)) != '\n') {
num[offset] = input;
offset++;
}
num[offset] = '\0';
clause_n = atoi(num);
clauses = malloc(sizeof(list_t *) * clause_n);
setup_bitsets();
#if DEBUG
fprintf(stderr, "%ld variables, %ld clauses\n", variables, clause_n);
#endif
}
break;
default:
/* Clause */
{
assert(variables > 0);
assert(clauses > 0);
ungetc(c, stdin);
clauses[collected] = list_create();
char * line = malloc(LINE_WIDTH);
fgets(line, LINE_WIDTH - 1, stdin);
if (line[strlen(line) - 1] == '\n') {
line[strlen(line) - 1] = '\0';
}
#if DEBUG
fprintf(stderr, "Preparing to add clause %ld: %s\n", collected, line);
#endif
char *p, *last;
for ((p = strtok_r(line, " ", &last)); ;
(p = strtok_r(NULL, " ", &last))) {
int var = atoi(p);
if (var == 0) break;
uintptr_t x = var;
list_insert(clauses[collected], (void *)x);
}
free(line);
#if DEBUG
fprintf(stderr, "Added clause.\n");
#endif
collected++;
if (collected == clause_n) return 0;
}
break;
}
return 1;
}
int main(int argc, char * argv[]) {
/* Read the CNF file from standard in and process it into clauses */
while (read_line());
#if DEBUG
/* Debug: Print out the clauses */
for (uint32_t i = 0; i < clause_n; ++i) {
list_t * clause = clauses[i];
fprintf(stderr, "[clause #%d] ", (i + 1));
foreach(node, clause) {
fprintf(stderr, "%ld ", (uintptr_t)node->value);
}
fprintf(stderr, "\n");
}
/* Print out variable information */
fprintf(stderr, "%ld variables to check, which means %d combinations to bruteforce.\n", variables, 1 << variables);
#endif
/* Brute force! */
while (!solved_with_bitset()) {
next_bitset(0);
}
/* If we get to this point, we have a solution. */
for (uint64_t i = 0; i < variables; ++i) {
/* Print the state of each variable. */
if (checkbit(i)) {
printf("%ld", i + 1);
} else {
printf("-%ld", i + 1);
}
if (i != variables - 1) {
printf(" ");
} else {
printf("\n");
}
}
}