/****************************************************************************
				chop.c  -  small tool to chop floating values close to zero
							to exactly zero
							 -------------------
	begin				: Tue Jul 14 2009
	copyright			: (C) 2009 by IAG, University of Stuttgart
	email				: kessler@iag.uni-stuttgart.de
 ****************************************************************************/

/****************************************************************************
 *																			*
 *	IAGLib is copyrighted software of the									*
 *	Institute for Aerodynamics and Gasdynamics (IAG)						*
 *	at the University of Stuttgart											*
 *																			*
 ****************************************************************************/

#include <getopt.h>

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

double absolute=1e-12;
double relative=1e-8;
int digits=0;

static double chop(double x) {
	double dest=(int)(x+0.5);
	double tolerance=0;
	double delta=fabs(dest-x);
	if (dest!=0) tolerance=relative*abs(dest);
	else tolerance=absolute;
	if (delta<tolerance && delta<absolute) return dest;
	else return x;
}

static int mightStartFloat(int c) {
	return isdigit(c) /*|| c=='+' || c=='-' */|| c=='.';
}

static int stopsFloat(int c) {
	return !(isdigit(c) || c=='+' || c=='-' || c=='.' || c=='e' || c=='E');
}

static int difference(char const * str, int l1, int l2) {
	if (l1!=l2) printf("%s on lines %d/%d\n", str, l1, l2);
	else printf("%s on line %d\n", str, l1);
	return 1;
}

FILE * file=0;
char * buffer=0, * cp=0;
int bufSize=0, bufLen=0;
char * rest=0;

int hasChar() {
	return (cp-buffer<bufLen) || !feof(file);
}

int nextChar() {
	if (cp-buffer<bufLen) return *cp++;
	else return getc(file);
}

void fillBuffer(int c) {
	cp=buffer;
	while (c!=EOF) {
		if (cp-buffer==bufSize) {
			int insPos=cp-buffer;
			if (bufSize) bufSize*=2;
			else bufSize=8;
			buffer=realloc(buffer, bufSize+1);
			rest=realloc(rest, bufSize+1);
			if (!buffer) {
				fprintf(stderr, "Memory allocation error, aborting\n");
				exit(1);
			}
			cp=buffer+insPos;
		}
		*cp++=c;
		++bufLen;
		if (stopsFloat(c)) break;
		c=getc(file);
	}
	*cp=0; // final zero delimier
	cp=buffer;
}

int main(int argc, char * argv[]) {
	int opt=0;
	char const * name=0;
	int c;
	double d;
	/* read options */
	while ((opt=getopt(argc, argv, "a:d:r:"))!=-1) {
		switch (opt) {
		case 'a': absolute=strtod(optarg, 0); break;
		case 'd': digits=strtol(optarg, 0, 10); break;
		case 'r': relative=strtod(optarg, 0); break;
		case '?': fprintf(stderr, "Unknown option %c, ignoring\n", opt); break;
		default: abort();
		}
	}
	if (optind<argc-2) {
		fprintf(stderr, "too many filename(s) given\nUsage:\n"
		"%s [-a VAL] [-d DIGITS] [-r VAL] [file]\n"
		"-a and -r specify absolute and relative tolerances for chopping\n"
		"-d specifies the number of digits to reduce to\n"
		, argv[0]);
		return 2; /* indicate trouble, as cmp does */
	}
	if (optind>=argc || !strcmp(name=argv[optind], "-")) file=stdin;
	else if ((file=fopen(name, "r"))==0) {
		perror("Error opening file: ");
		return 2;
	}
	while (hasChar()) {
		int c=nextChar();
		if (mightStartFloat(c)) {
			char * number=0;
			fillBuffer(c);
			number=buffer;
			while (isdigit(*number)) ++number;
			if (*number=='.' || *number=='e' || *number=='E') {
				int read=-1;
				if (sscanf(buffer, "%lf%n", &d, &read)==1 && chop(d)!=d) {
					if (digits)
						printf("%.*g", digits, chop(d));
					else
						printf("%g", chop(d));
					memmove(buffer, buffer+read, bufLen-read);
					bufLen-=read;
					cp=buffer;
				}
			}
			while (bufLen)
				--bufLen, printf("%c", *cp++);
		}
		else if (c!=EOF) printf("%c", c);
	}
	fclose(file);
	return 0;
}
