
/* stablemain.c
 * Neil Moore <neil@cs.uky.edu>
 * 26 Dec 1999
 *
 * Part of lpsms.  See COPYRIGHT for copyright information.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <lp.h>
#include <lp_smrd.h>
#include <lp_stable.h>
#include <lp_util.h>
#include <lp_strrd.h>

#ifndef VERSION
#define VERSION "unknown"
#endif

static void usage(void);
static void version(void);
static int to_int(const char *str);

static clock_t last_clock;
static int verbosity=1;
static int timing=1;
static int nmodels=0; /* Number of models to compute, 0 = all */

int printmodels(struct lp *p, int *model);

int main(int argc, char *argv[]) 
{
	const char *infilename = NULL;
	const char *strfilename = NULL;
	FILE *infile=0, *strfile=0;
	int *str=0;
	int nstr=0;
	struct lp *prog=0;
	clock_t start, end;
	int choices, stable, conflict, forced, tried, more;
	int sloppy = 0, source = 0;

	while (*++argv != NULL) {
		if(!strcmp(*argv, "-s") || !strcmp(*argv, "--stratification")) {
			++argv;
			if(*argv == NULL) {
				usage();
				return 1;
			}
			strfilename = *argv;
		} else if(!strncmp(*argv, "-s", 2)) {
			strfilename = *argv + 2;
		} else if(!strncmp(*argv, "--stratification=", 17)) {
			strfilename = *argv + 17;
		} else if(!strcmp(*argv, "-v") || !strcmp(*argv, "--version")) {
			version();
			return 0;
		} else if(!strcmp(*argv, "-h") || !strcmp(*argv, "--help")) {
			usage();
			return 0;
		} else if(!strcmp(*argv, "-S") || !strcmp(*argv, "--sloppy-lookahead")) {
			sloppy = 1;
		} else if(!strcmp(*argv, "-F") || !strcmp(*argv, "--full-lookahead")) {
			sloppy = 0;
		} else if(!strcmp(*argv, "-p") || !strcmp(*argv, "--source-pointer")) {
			source = 1;
		} else if(!strcmp(*argv, "-P") || !strcmp(*argv, "--no-source-pointer")) {
			source = 0;
		} else if(!strcmp(*argv, "-1") || !strcmp(*argv, "--single")) {
			nmodels = 1;
		} else if(!strcmp(*argv, "-a") || !strcmp(*argv, "--all")) {
			nmodels = 0;
		} else if(!strcmp(*argv, "-c") || !strcmp(*argv, "--count")) {
			++argv;
			if(*argv == NULL || (nmodels = to_int(*argv)) < 0) {
				usage();
				exit(1);
			}
		} else if(!strncmp(*argv, "-c", 2)) {
			if((nmodels = to_int(*argv+2)) < 0) {
				usage();
				exit(1);
			}
		} else if(!strncmp(*argv, "--count=", 8)) {
			if((nmodels = to_int(*argv+8)) < 0) {
				usage();
				exit(1);
			}
		} else if(!strcmp(*argv, "-t") || !strcmp(*argv, "--time")) {
			timing = 1;
		} else if(!strcmp(*argv, "-n") || !strcmp(*argv, "--no-time")) {
			timing = 0;
		} else if(!strcmp(*argv, "-V") || !strcmp(*argv, "--verbose")) {
			verbosity = 2;
		} else if(!strcmp(*argv, "-m") || !strcmp(*argv, "--messages")) {
			verbosity = 1;
		} else if(!strcmp(*argv, "-q") || !strcmp(*argv, "--quiet")) {
			verbosity = 0;
		} else if(!strcmp(*argv, "-qq") || !strcmp(*argv, "--very-quiet")) {
			verbosity = -1;
		} else if(!strcmp(*argv, "-")) {
			infilename = NULL;
		} else if ((*argv)[0] == '-') {
			usage();
			return 1;
		} else {
			infilename = *argv;
		}		
	}
	
	if(infilename == NULL)
		infile = stdin;
	else if((infile = fopen(infilename, "r")) == NULL) {
		if(verbosity > 0)
			fprintf(stderr, "cannot open %s: %s\n", infilename, strerror(errno));
		goto failure;
	}
	
	start = clock();
	
	if(strfilename) {
		if((strfile = fopen(strfilename, "r")) == NULL) {
			if(verbosity > 0)
				fprintf(stderr, "cannot open %s: %s\n", strfilename, strerror(errno));
			goto nostr;
		}
		if(verbosity > 1)
			fprintf(stderr, "Reading stratification...");

		nstr = lp_read_strata(strfile, &str);
		
		if(nstr > 0) {
			if(verbosity > 1)
				fprintf(stderr, "done.\n");
		} else {
			if(verbosity > 0) {
				const char *msg = lp_smrd_error();
				if(msg)
					fprintf(stderr, "%s\n", msg);
				fprintf(stderr, "error in stratification\n");
			}
	    nostr:
			if(verbosity > 0)
				fprintf(stderr, "Assuming trivial stratification.\n");
			
			nstr = 0;
			if(str)
				free(str);
			str = NULL;
		}
		if(strfile)
			fclose(strfile);
	}

	prog = lp_malloc(sizeof(struct lp));

	if(verbosity > 1) {
		fprintf(stderr, "Reading...");
	}
	if(lp_sm_read_file(infile, prog) < 0) {
		if(verbosity > 0) {
			const char *msg = lp_smrd_error();
			if(msg)
				fprintf(stderr, "%s\n", msg);
			fprintf(stderr, "error in input; output not generated\n");
		}
		goto failure;
	}
	if(verbosity > 1)
		fprintf(stderr, "done.\n");
	
	last_clock = clock();
	lp_stable_options(sloppy,source,0);
	more = lp_stable(prog, printmodels, str, nstr);
	if(verbosity > -1)
		printf("\n");
	lp_stable_stats(&choices, &stable, &conflict, &forced, &tried);
	
	end = clock();

	if(verbosity > 0) {
		printf("%d choice point%s\n", choices, choices==1 ? "" : "s");
		printf("%d conflict%s\n", conflict, conflict==1 ? "" : "s");
		printf("%d forced atom%s\n", forced, forced==1 ? "" : "s");
		printf("%d tried atom%s\n", tried, tried==1 ? "" : "s");
	}
	/* We want to know how many models even if we don't print them */
	if(verbosity != 0)
		printf("%d stable model%s\n", stable, stable==1 ? "" : "s");
	if(timing)
		printf("%f seconds\n", ((double)end - start)/ CLOCKS_PER_SEC);

	if(verbosity > 1)
		fprintf(stderr, "\nCleaning up...\n");
	if(infile != stdout)
		fclose(infile);
	if(str)
		free(str);
	lp_free_lp(prog);
	
	return 0;
	
 failure:
	if(verbosity > 1)
		fprintf(stderr, "Cleaning up...\n");
	if(infile && infile != stdout)
		fclose(infile);
	if(str)
		free(str);
	if(prog)
		lp_free_lp(prog);
	
	return 1;
}


int printmodels(struct lp *p, int *model) 
{
	int i, choices, stable, conflict, forced, tried;
	clock_t this_clock = clock();

	lp_stable_stats(&choices, &stable, &conflict, &forced, &tried);
	
	if(verbosity > -1) {
		printf("\nModel %d: ", stable);
		for(i=0; i<p->natoms; i++) {
			if(model[i] == LP_L_TRUE)
				printf("%s ", p->atoms[i].name);
			else if(model[i] == LP_L_UNKNOWN && verbosity > 0)
				fprintf(stderr, "ERROR: %s UNKNOWN IN MODEL\n", p->atoms[i].name);
		}
		printf("\n");
	}
	
	if(timing)
		printf("Model %d: %f seconds\n", stable, ((double)this_clock - last_clock)/ CLOCKS_PER_SEC);
	
	last_clock = this_clock;

	/* If nmodels = 0 (all models), the following will never be true */
	if(stable == nmodels)
		return 1;
	else
		return 0;
}


/* Like atoi(), but returns a negative number if the string is non-numeric */
int to_int(const char *str)
{
	long result;
	char *end=NULL;
	if(str == NULL)
		return 0;
	
	result = strtol(str, &end, 0);

	if(*str != '\0' && *end == '\0')
		return (int)result;
	else
		return -1;
}


void usage(void) 
{
	printf("usage: stable [options] [filename]\n");
	printf("Compute the stable models of a logic program.\n\n");
	
	printf(" -s, --stratification=file  read stratification from file\n");
	printf(" -F, --full-lookahead       perform full lookahead (default)\n");
	printf(" -S, --sloppy-lookahead     do not perform full lookahead\n");
	printf(" -p, --source-pointer       use source pointer optimisation\n");
	printf(" -P, --no-source-pointer    do not use source pointer optimisation (default)\n");
	printf(" -1, --single               find only the first model\n");
	printf(" -a, --all                  find all stable models (default)\n");
	printf(" -c, --count=N              find the first N stable models\n");
	printf(" -t, --time                 print timing information to stderr (default)\n");
	printf(" -n, --no-time              do not print timing information\n");
	printf(" -V, --verbose              print additional information to stderr\n");
	printf(" -m, --messages             print status messages to stderr (default)\n");
	printf(" -q, --quiet                do not print status information (other than\n");
	printf("                             timing and models)\n");
	printf(" -qq, --very-quiet          compute models but do not print them\n");
	printf(" -v, --version              print version information to stdout\n");
	printf(" -h, --help                 display this message\n\n");

	printf("Input is read from filename (defaults to stdin).\n");
}

void version(void) 
{
	printf("smdtconv %s, from lpsms %s, using liblp %s\n", VERSION, lp_sms_version(), lp_version());
}
