#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

#define NDEBUG
/*#define BENCHMARK*/

#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#define SQR(x)	((x)*(x))
#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
#define INF	(10.0e10)
#define EPS	(1.0/INF)

#define MIN_CONF_DETECT	4
#define MAX_MSE_DETECT	0.1
#define	MIN_CONF_RECOG	2
#define	MAX_MSE_RECOG	0.2

#define CODE_STARTA 103
#define CODE_STARTB 104
#define CODE_STARTC 105
#define CODE_STOP   106
unsigned char codetab[][6] = {
	{ 2, 1, 2, 2, 2, 2 },	/* 00 */
	{ 2, 2, 2, 1, 2, 2 },
	{ 2, 2, 2, 2, 2, 1 },
	{ 1, 2, 1, 2, 2, 3 },
	{ 1, 2, 1, 3, 2, 2 },
	{ 1, 3, 1, 2, 2, 2 },
	{ 1, 2, 2, 2, 1, 3 },
	{ 1, 2, 2, 3, 1, 2 },
	{ 1, 3, 2, 2, 1, 2 },
	{ 2, 2, 1, 2, 1, 3 },
	{ 2, 2, 1, 3, 1, 2 },
	{ 2, 3, 1, 2, 1, 2 },
	{ 1, 1, 2, 2, 3, 2 },
	{ 1, 2, 2, 1, 3, 2 },
	{ 1, 2, 2, 2, 3, 1 },
	{ 1, 1, 3, 2, 2, 2 },
	{ 1, 2, 3, 1, 2, 2 },
	{ 1, 2, 3, 2, 2, 1 },
	{ 2, 2, 3, 2, 1, 1 },
	{ 2, 2, 1, 1, 3, 2 },
	{ 2, 2, 1, 2, 3, 1 },
	{ 2, 1, 3, 2, 1, 2 },
	{ 2, 2, 3, 1, 1, 2 },
	{ 3, 1, 2, 1, 3, 1 },
	{ 3, 1, 1, 2, 2, 2 },
	{ 3, 2, 1, 1, 2, 2 },
	{ 3, 2, 1, 2, 2, 1 },
	{ 3, 1, 2, 2, 1, 2 },
	{ 3, 2, 2, 1, 1, 2 },
	{ 3, 2, 2, 2, 1, 1 },
	{ 2, 1, 2, 1, 2, 3 },
	{ 2, 1, 2, 3, 2, 1 },
	{ 2, 3, 2, 1, 2, 1 },
	{ 1, 1, 1, 3, 2, 3 },
	{ 1, 3, 1, 1, 2, 3 },
	{ 1, 3, 1, 3, 2, 1 },
	{ 1, 1, 2, 3, 1, 3 },
	{ 1, 3, 2, 1, 1, 3 },
	{ 1, 3, 2, 3, 1, 1 },
	{ 2, 1, 1, 3, 1, 3 },
	{ 2, 3, 1, 1, 1, 3 },
	{ 2, 3, 1, 3, 1, 1 },
	{ 1, 1, 2, 1, 3, 3 },
	{ 1, 1, 2, 3, 3, 1 },
	{ 1, 3, 2, 1, 3, 1 },
	{ 1, 1, 3, 1, 2, 3 },
	{ 1, 1, 3, 3, 2, 1 },
	{ 1, 3, 3, 1, 2, 1 },
	{ 3, 1, 3, 1, 2, 1 },
	{ 2, 1, 1, 3, 3, 1 },
	{ 2, 3, 1, 1, 3, 1 },
	{ 2, 1, 3, 1, 1, 3 },
	{ 2, 1, 3, 3, 1, 1 },
	{ 2, 1, 3, 1, 3, 1 },
	{ 3, 1, 1, 1, 2, 3 },
	{ 3, 1, 1, 3, 2, 1 },
	{ 3, 3, 1, 1, 2, 1 },
	{ 3, 1, 2, 1, 1, 3 },
	{ 3, 1, 2, 3, 1, 1 },
	{ 3, 3, 2, 1, 1, 1 },
	{ 3, 1, 4, 1, 1, 1 },
	{ 2, 2, 1, 4, 1, 1 },
	{ 4, 3, 1, 1, 1, 1 },
	{ 1, 1, 1, 2, 2, 4 },
	{ 1, 1, 1, 4, 2, 2 },
	{ 1, 2, 1, 1, 2, 4 },
	{ 1, 2, 1, 4, 2, 1 },
	{ 1, 4, 1, 1, 2, 2 },
	{ 1, 4, 1, 2, 2, 1 },
	{ 1, 1, 2, 2, 1, 4 },
	{ 1, 1, 2, 4, 1, 2 },
	{ 1, 2, 2, 1, 1, 4 },
	{ 1, 2, 2, 4, 1, 1 },
	{ 1, 4, 2, 1, 1, 2 },
	{ 1, 4, 2, 2, 1, 1 },
	{ 2, 4, 1, 2, 1, 1 },
	{ 2, 2, 1, 1, 1, 4 },
	{ 4, 1, 3, 1, 1, 1 },
	{ 2, 4, 1, 1, 1, 2 },
	{ 1, 3, 4, 1, 1, 1 },
	{ 1, 1, 1, 2, 4, 2 },
	{ 1, 2, 1, 1, 4, 2 },
	{ 1, 2, 1, 2, 4, 1 },
	{ 1, 1, 4, 2, 1, 2 },
	{ 1, 2, 4, 1, 1, 2 },
	{ 1, 2, 4, 2, 1, 1 },
	{ 4, 1, 1, 2, 1, 2 },
	{ 4, 2, 1, 1, 1, 2 },
	{ 4, 2, 1, 2, 1, 1 },
	{ 2, 1, 2, 1, 4, 1 },
	{ 2, 1, 4, 1, 2, 1 },
	{ 4, 1, 2, 1, 2, 1 },
	{ 1, 1, 1, 1, 4, 3 },
	{ 1, 1, 1, 3, 4, 1 },
	{ 1, 3, 1, 1, 4, 1 },
	{ 1, 1, 4, 1, 1, 3 },
	{ 1, 1, 4, 3, 1, 1 },
	{ 4, 1, 1, 1, 1, 3 },
	{ 4, 1, 1, 3, 1, 1 },
	{ 1, 1, 3, 1, 4, 1 },	/* 99 */
	{ 1, 1, 4, 1, 3, 1 },	/* 100: code B */
	{ 3, 1, 1, 1, 4, 1 },	/* 101: code A */
	{ 4, 1, 1, 1, 3, 1 },	/* 102: FNC 1 */
	{ 2, 1, 1, 4, 1, 2 },	/* 103: START code A */
	{ 2, 1, 1, 2, 1, 4 },	/* 104: START code B */
	{ 2, 1, 1, 2, 3, 2 },	/* 105: START code C */
	{ 2, 3, 3, 1, 1, 1 } 	/* 106: STOP + 2 */
};		
unsigned char codetab_stop_rev[] = { 2, 1, 1, 1, 3, 3, 2 };	/* 106: STOP reversed*/

char charsets[][2] = {
	{ ' ', ' ' },
	{ '!', '!' },
	{ '"', '"' },
	{ '#', '#' },
	{ '$', '$' },
	{ '%', '%' },
	{ '&', '&' },
	{ '\'', '\'' },
	{ '(', '(' },
	{ ')', ')' },
	{ '*', '*' },
	{ '+', '+' },
	{ ',', ',' },
	{ '-', '-' },
	{ '.', '.' },
	{ '/', '/' },
	{ '0', '0' },
	{ '1', '1' },
	{ '2', '2' },
	{ '3', '3' },
	{ '4', '4' },
	{ '5', '5' },
	{ '6', '6' },
	{ '7', '7' },
	{ '8', '8' },
	{ '9', '9' },
	{ ':', ':' },
	{ ';', ';' },
	{ '<', '<' },
	{ '=', '=' },
	{ '>', '>' },
	{ '?', '?' },
	{ '@', '@' },
	{ 'A', 'A' },
	{ 'B', 'B' },
	{ 'C', 'C' },
	{ 'D', 'D' },
	{ 'E', 'E' },
	{ 'F', 'F' },
	{ 'G', 'G' },
	{ 'H', 'H' },
	{ 'I', 'I' },
	{ 'J', 'J' },
	{ 'K', 'K' },
	{ 'L', 'L' },
	{ 'M', 'M' },
	{ 'N', 'N' },
	{ 'O', 'O' },
	{ 'P', 'P' },
	{ 'Q', 'Q' },
	{ 'R', 'R' },
	{ 'S', 'S' },
	{ 'T', 'T' },
	{ 'U', 'U' },
	{ 'V', 'V' },
	{ 'W', 'W' },
	{ 'X', 'X' },
	{ 'Y', 'Y' },
	{ 'Z', 'Z' },
	{ '[', '[' },
	{ '\\', '\\' },
	{ ']', ']' },
	{ '^', '^' },
	{ '_', '_' },
	{ '?', '\'' },	/* 		NUL */
	{ '?', 'a' },	/* 		SOH */
	{ '?', 'b' },	/* 		STX */
	{ '?', 'c' },	/* 		ETX */
	{ '?', 'd' },	/* 		EOT */
	{ '?', 'e' },	/* 		ENQ */
	{ '?', 'f' },	/* 		ACK */
	{ '?', 'g' },	/* 		BEL */
	{ '?', 'h' },	/* 		BS */
	{ '?', 'i' },	/* 		HT */
	{ '?', 'j' },	/* 		LF */
	{ '?', 'k' },	/* 		VT */
	{ '?', 'l' },	/* 		FF */
	{ '?', 'm' },	/* 		CR */
	{ '?', 'n' },	/* 		SO */
	{ '?', 'o' },	/* 		SI */
	{ '?', 'p' },	/*		DLE */
	{ '?', 'q' },	/*		DC1 */
	{ '?', 'r' },	/*		DC2 */
	{ '?', 's' },	/*		DC3 */
	{ '?', 't' },	/*		DC4 */
	{ '?', 'u' },	/*		NAK */
	{ '?', 'v' },	/*		SYN */
	{ '?', 'w' },	/*		ETB */
	{ '?', 'x' },	/*		CAN */
	{ '?', 'y' },	/* 		EM */
	{ '?', 'z' },	/*		SUB */
	{ '?', '{' },	/*		ESC */
	{ '?', '|' },	/* 		FS */
	{ '?', '}' },	/* 		GS */
	{ '?', '~' },	/* 		RS */
	{ '?', '?' },	/* DEL US */
	{ '?', '?' },	/* FNC3 FNC3  */
	{ '?', '?' },	/* FNC2 FNC2  */
	{ '?', '?' },	/* SHIFT SHIFT  */
	{ '?', '?' },	/* CODEC CODEC  */
	{ '?', '?' },	/* CODEB FNC4 CODEB  */
	{ '?', '?' },	/* FNC4 CODEA CODEA  */
	{ '?', '?' }	/* FNC1 FNC1 FNC 1  */
};

static char *progname = "";

void error(char *msg, ...)
{
	va_list ap;
	va_start(ap, msg);
	fprintf(stderr, "%s: ", progname);
	vfprintf(stderr, msg, ap);
	fprintf(stderr, ".\n");
	exit(1);
}

void alloc_mem(void **ptr, int *ptrsize, int nelem, size_t elem_size) {
	size_t req_size = nelem*elem_size;
	size_t new_size;
	void *p;
	if (*ptr==NULL) *ptrsize = 0;
	if (req_size <= *ptrsize) return;
	new_size = 16*elem_size + 2*req_size;
	p = realloc(*ptr, new_size);
	if (p==NULL) error("Out of memory");
	*ptr = p;
	*ptrsize = new_size;
}

void flipscan(unsigned char *scanline, int width) {
	unsigned char c;
	int i;
	if (scanline==NULL) return;
	for (i=0; i<width/2; i++) {
		c = scanline[i];
		scanline[i] = scanline[width-i-1];
		scanline[width-i-1] = c;
	}
}

/************** PGM routines *******************/

struct Pgm {
	FILE *file;
	int width,height;
	int maxgray;
	unsigned char *scanline;
};

static void pgm_nexttoken(struct Pgm *pgm) {
	int c;
	do {
		c = getc(pgm->file); 
		if (c==EOF) error("unexpected end of file");
		if (c=='#') {
			do {
				c = getc(pgm->file);
				if (c==EOF) error("unexpected end of file");
			} while (c!='\n');
			c = ' ';
		}
	} while (isspace(c));
	ungetc(c,pgm->file);
}

static unsigned int pgm_readnum(struct Pgm *pgm) {
	unsigned int c, val = 0;
	pgm_nexttoken(pgm);
	do {
		c = getc(pgm->file);
		if (!isdigit(c)) break;
		val = val*10 + c - '0';		/* Assume ASCII */
	} while (1);
	if (!isspace(c)) error("PGM file error");
	ungetc(c,pgm->file);
	return val;
}

void pgm_init(struct Pgm *pgm, FILE *file) {
	char c1,c2,c3;
	
	pgm->file = file;
	pgm->scanline = NULL;

	/* Check signature */
	c1 = getc(pgm->file);
	c2 = getc(pgm->file);
	c3 = getc(pgm->file);
	if (c1!='P' || c2!='5' || !isspace(c3)) error("Not raw bits PGM file");
	ungetc(c3,pgm->file);
	
	/* Read dimensions */
	pgm->width = pgm_readnum(pgm);
	pgm->height = pgm_readnum(pgm);		/* Height, not used */
	pgm->maxgray = pgm_readnum(pgm);	/* Max gray value, not used */
	c1 = getc(pgm->file);
	if (!isspace(c1)) error("PGM file error");
}

/* Return true if is still scanlines to process, false otherwise */
unsigned char *pgm_readscan(struct Pgm *pgm) {
	size_t r;
	if (pgm->scanline==NULL) {
		pgm->scanline = malloc(pgm->width);
		if (pgm->scanline==NULL) error("out of memory");
	}
	r = fread(pgm->scanline, pgm->width, 1, pgm->file);
	return (r!=1) ? NULL : pgm->scanline;
}

int pgm_getwidth(struct Pgm *pgm) {
	return pgm->width;
}

void pgm_close(struct Pgm *pgm) {
	free(pgm->scanline);
	pgm->scanline = NULL;
}

/************** Thresholding routines *******************/

static int adaptive = 0;
static int interpolate = 0;

#define WHITE 0
#define BLACK 1

struct Runs_info {
	struct Runs *runs;	/* Dynamically allocated array of Runs */
	int runs_num;		/* Actual number of used entries */
	int runs_size;		/* Maximum entries in runs, may be enlargened */
};

struct Runs {
	unsigned char color;	/* 0=white, 1=black */
	double len;		/* Interpolated run length, 0=last run */
	double start, end;	/* Interpolated start and end of a run */
};

void runs_init(struct Runs_info *runs_info) {
	runs_info->runs = NULL;
	runs_info->runs_num = 0;
	runs_info->runs_size = 0;
}

void runs_close(struct Runs_info *runs_info) {
	free(runs_info->runs);
	runs_info->runs = NULL;
}

void runs_compute(struct Runs_info *runs_info, unsigned char *slice, unsigned int width) {
	unsigned char color, cur_color;
	int sum,nsum,i;
	double oldmean,mean,intersect;
	
	nsum = MAX(width/32, 64);	/* Smaller nsum=more adaptive */
	if (nsum>width || !adaptive) nsum = width;
	sum = 0;
	for (i=0; i<nsum; i++) sum += slice[i];
	oldmean = mean = (double)sum/nsum;

	alloc_mem(&(runs_info->runs), &(runs_info->runs_size), 1, sizeof(struct Runs));
	cur_color = (slice[0]<mean) ? BLACK : WHITE;
	runs_info->runs[0].color = cur_color;
	runs_info->runs[0].start = 0.0;
	runs_info->runs_num = 1;
	for (i=1; i<width; i++) {
		if ((i+nsum/2)<width && (i-(nsum+1)/2)>=0) 
			sum += slice[i+nsum/2] - slice[i-(nsum+1)/2];
		mean = (double)sum/nsum;
		color = (slice[i]<mean) ? BLACK : WHITE;
		if (color != cur_color) {
			alloc_mem(&(runs_info->runs), &(runs_info->runs_size), runs_info->runs_num+1, sizeof(struct Runs));
			if (interpolate) {
				intersect = (slice[i-1]-oldmean)/(mean-oldmean-slice[i]+slice[i-1]);	/* linear interpolation */
			} else {
				intersect = 0.0;
			}
			intersect += i - 1;
			runs_info->runs[runs_info->runs_num].start = intersect;
			runs_info->runs[runs_info->runs_num-1].end = intersect;
			runs_info->runs[runs_info->runs_num-1].len = intersect - runs_info->runs[runs_info->runs_num-1].start;
			runs_info->runs[runs_info->runs_num].color = color;
			runs_info->runs_num++;
			cur_color = color;
		}
		oldmean = mean;
	}
	runs_info->runs[runs_info->runs_num-1].end = i;
	runs_info->runs[runs_info->runs_num-1].len = i - runs_info->runs[runs_info->runs_num-1].start;
}

/************** Decoding routines *******************/

#ifdef BENCHMARK
static int try_hard = 1;
#else
static int try_hard = 0;
#endif

struct Dec {
	int *codewords;
	int codewords_num;
	int codewords_size;
};

static void compute_mse(int vals, struct Runs runs[], char pattern[], double *mse, double *est) {
	double e = 0, m = 0, s;
	int k;

	/* First estimate the module (narrowest possible bar) width */
	for (k=0; k<vals; k++)
		e += runs[k].len / pattern[k];
	e /= vals;
	*est = e;

	/* Then compute the normalized mean squared error of pattern and runs */
	for (k=0; k<vals; k++) {
		s = runs[k].len / e - pattern[k];
		m += SQR(s);
	}
	m /= vals;
	*mse = m;
}

void dec_init(struct Dec *dec) {
	dec->codewords = NULL;
	dec->codewords_num = 0;
	dec->codewords_size = 0;
}

void dec_close(struct Dec *dec) {
	free(dec->codewords);
	dec->codewords = NULL;
}

int dec_decode(struct Dec *dec, struct Runs_info *runs_info) {
	struct Runs *runs = runs_info->runs;
	double best_mse, best2_mse, best_est;
	int best_start, best_code, k, m;
	double mse, est, conf, module_width;
#ifndef NDEBUG
	double min_conf=INF, max_mse=0.0;
#endif
	
	/* First find some code that means end or start of the barcode */
	best_mse = best2_mse = INF;
	best_code = -1;
	best_start = -1;
	best_est = -1;
	for (k=0; k<runs_info->runs_num; k++) {
		if (runs[k].color==BLACK && k+6<runs_info->runs_num) {
			for (m=CODE_STARTA; m<=CODE_STOP; m++) {
				compute_mse(m==CODE_STOP? 7:6, &runs[k], m==CODE_STOP? codetab_stop_rev : codetab[m],
						&mse, &est);
				if (best_mse>mse) {
					best2_mse = best_mse;
					best_mse = mse;
					best_est = est;
					best_start = k;
					best_code = m;
				} else if (best2_mse>mse) {
					best2_mse = mse;
				}
			}
			
		}
	}
	if (best_code==-1) return -1;
	conf = best2_mse/best_mse;	/* Should I check for zero-division here? */
	module_width = best_est;

	if (!try_hard && best_mse>MAX_MSE_DETECT) {
#ifndef NDEBUG
		fprintf(stderr, "Too bad match: ignoring code %g\n", best_mse);
#endif		
		return -1;
	}

	if (!try_hard && conf<MIN_CONF_DETECT) {
#ifndef NDEBUG
		fprintf(stderr, "Too low confidence: ignoring code %g\n", conf);
#endif		
		return -1;
	}

	if (best_code==CODE_STOP) {
#ifndef NDEBUG
		fprintf(stderr, "Barcode is upside down!\n");
#endif		
		return -1;
	}

#ifndef NDEBUG
//	fprintf(stderr, "Code %i found at %i, confidence %g, modulewidth %g\n", best_code, best_start, conf, module_width);
#endif

	/* Then decode the barcode */
	dec->codewords_num = 1;
	k = best_start;
	while (1) {
		k += 6;
		alloc_mem(&(dec->codewords), &(dec->codewords_size), dec->codewords_num, sizeof(int));
		dec->codewords[dec->codewords_num-1] = best_code;
		if (best_code==CODE_STOP) break;
		if (k+5>=runs_info->runs_num) {
#ifndef NDEBUG
//			fprintf(stderr, "End of barcode missing\n");
#endif			
			return -1;
		}
		dec->codewords_num++;
		best_mse = best2_mse = INF;
		best_code = -1;
		for (m=0; m<SIZE(codetab); m++) {
			compute_mse(6, &runs[k], codetab[m], &mse, &est);
			mse = (6*mse + 4*SQR(est/module_width-1)) / 10.0;
			if (best_mse>mse) {
				best2_mse = best_mse;
				best_mse = mse;
				best_est = est;
				best_code = m;
			} else if (best2_mse>mse) {
				best2_mse = mse;
			}
		}
		if (!try_hard && best_mse>MAX_MSE_RECOG) {
#ifndef NDEBUG
			fprintf(stderr,"Too bad barcode %g\n", best_mse);
#endif
			return -1;
		}
		conf = best2_mse/best_mse;
		if (!try_hard && conf<MIN_CONF_RECOG) {
#ifndef NDEBUG
			fprintf(stderr,"Too unreliable barcode %g\n", conf);
#endif
			return -1;
		}

#ifndef NDEBUG
		if (best_mse>max_mse) max_mse=best_mse;
		if (conf<min_conf) min_conf=conf;
#endif
			
#ifndef NDEBUG
//		fprintf(stderr, "Code %i at %i, confidence %g, mse %g\n", best_code, k, conf, best_mse);
#endif		
	}

#ifndef NDEBUG
	fprintf(stderr, "max_mse=%g     min_conf=%g\n", max_mse, min_conf);
#endif
	return 0;
}

/************** Translation routines *******************/

void msg_translate(char **msg, struct Dec *dec) {
	int msg_size, msg_num, charset;
	int checksum, code, k;
	
	if (*msg != NULL) {
		free(*msg);
		*msg = NULL;
	}
	msg_size = 0;
	msg_num = 0;
	
	/* Verify checksum */
	checksum = dec->codewords[0];
	for (k=1; k<dec->codewords_num-2; k++) {
		checksum += k * dec->codewords[k];
	}
	checksum %= 103;
	if (checksum != dec->codewords[dec->codewords_num-2]) {
#ifndef NDEBUG
		fprintf(stderr, "checksum error\n");
#endif		
		return;
	}
	
	/* Decode character string */
	charset = dec->codewords[0];
	for (k=1; k<dec->codewords_num-2; k++) {
		code = dec->codewords[k];
		switch (charset) {
			case CODE_STARTA:
			case CODE_STARTB:
				if (code==99) {
					charset = CODE_STARTC;
					break;
				}
				if (charset==CODE_STARTA && code==100) {
					charset = CODE_STARTB;
					break;
				}
				if (charset==CODE_STARTB && code==101) {
					charset = CODE_STARTA;
					break;
				}
				alloc_mem(msg, &msg_size, msg_num+1, sizeof(char));
				(*msg)[msg_num++] = charsets[code][charset==CODE_STARTA ? 0 : 1];
				break;
			case CODE_STARTC:
				if (code<100) {
					alloc_mem(msg, &msg_size, msg_num+2, sizeof(char));
					(*msg)[msg_num++] = code / 10 + '0';
					(*msg)[msg_num++] = code % 10 + '0';
				} else {
					switch (code) {
					case 100:
						charset = CODE_STARTB;
						break;
					case 101:
						charset = CODE_STARTA;
						break;
					default:
						fprintf(stderr, "warning: unsupported character %i at %i\n", code, k);
					}
				}
				break;
			default:
				error("error");
		}
	}
	alloc_mem(msg, &msg_size, msg_num+1, sizeof(char));
	(*msg)[msg_num] = 0;
}


/************** Main *******************/

int main(int argc, char *argv[]) {
	struct Pgm pgm;
	struct Runs_info runs;
	struct Dec dec;
	char *msg;
	unsigned char *scanline;
	FILE *input;
	int r;
#ifdef BENCHMARK
	int count_rec=0;
	FILE *resultfile;
	char buf[16];
#endif	
	if (argc>=1) progname = argv[0];
	if (argc>=2) {
		input = fopen(argv[1], "rb");
		if (input==NULL) error("error opening %s", argv[1]);
	} else {
		input = stdin;
	}
	
	pgm_init(&pgm, input);
	runs_init(&runs);
	dec_init(&dec);
	msg = NULL;

#ifdef BENCHMARK
	resultfile = fopen("results.pgm","wb");
	if (resultfile==NULL) error("opening results file");
	fprintf(resultfile, "P5\n%i %i\n255\n", pgm.width+1*16, pgm.height);
#endif	
	do {
		scanline = pgm_readscan(&pgm);
		if (scanline==NULL) break;
//		flipscan(scanline, pgm_getwidth(&pgm));
		runs_compute(&runs, scanline, pgm_getwidth(&pgm));
		r = dec_decode(&dec, &runs);
		if (r==0) {
			msg_translate(&msg, &dec);
			if (msg!=NULL) {
				printf("%s\n", msg);
#ifndef BENCHMARK			
				break;
#else
				count_rec++;
#endif
			}
		} else {
			free(msg);
			msg = NULL;
		}
#ifdef BENCHMARK
		fwrite(scanline, pgm.width, 1, resultfile);
		for (r=0; r<16; r++) buf[r] = (msg!=NULL)? 0 : 255;
		fwrite(buf, 16, 1, resultfile);
#endif		
	} while (1);
#ifdef BENCHMARK
	fprintf(stderr, "%i recognized\n", count_rec);
	fclose(resultfile);
#endif
	free(msg);
	dec_close(&dec);
	runs_close(&runs);
	pgm_close(&pgm);
	if (argc>=2) fclose(input);
	return 0;
}
