#include <u.h>
#include <libc.h>
#include <stdio.h>

/*
 * try all combination of flags and float conversions
 * with some different widths & precisions
 */

#define Njust 2
#define Nplus 3
#define Nalt 2
#define Nzero 2
#define Nspec 5
#define Nwidth 5
#define Nprec 5

static double fmtvals[] = {
	3.1415925535897932e15,
	3.1415925535897932e14,
	3.1415925535897932e13,
	3.1415925535897932e12,
	3.1415925535897932e11,
	3.1415925535897932e10,
	3.1415925535897932e9,
	3.1415925535897932e8,
	3.1415925535897932e7,
	3.1415925535897932e6,
	3.1415925535897932e5,
	3.1415925535897932e4,
	3.1415925535897932e3,
	3.1415925535897932e2,
	3.1415925535897932e1,
	3.1415925535897932e0,
	3.1415925535897932e-1,
	3.1415925535897932e-2,
	3.1415925535897932e-3,
	3.1415925535897932e-4,
	3.1415925535897932e-5,
	3.1415925535897932e-6,
	3.1415925535897932e-7,
	3.1415925535897932e-8,
	3.1415925535897932e-9,
	3.1415925535897932e-10,
	3.1415925535897932e-11,
	3.1415925535897932e-12,
	3.1415925535897932e-13,
	3.1415925535897932e-14,
	3.1415925535897932e-15,
};

/*
 * are the numbers close?
 * used to compare long numbers where the last few digits are garbage
 * due to precision problems
 */
static int
numclose(char *num1, char *num2)
{
	int ndig;
	double d1, d2;
	enum { MAXDIG = 15 };

	d1 = fmtstrtod(num1, 0);
	d2 = fmtstrtod(num2, 0);
	if(d1 != d2)
		return 0;

	ndig = 0;
	while (*num1) {
		if (*num1 >= '0' && *num1 <= '9') {
			ndig++;
			if (ndig > MAXDIG) {
				if (!(*num2 >= '0' && *num2 <= '9')) {
					return 0;
				}
			} else if (*num1 != *num2) {
				return 0;
			}
		} else if (*num1 != *num2) {
			return 0;
		} else if (*num1 == 'e' || *num1 == 'E') {
			ndig = 0;
		}
		num1++;
		num2++;
	}
	if (*num1 || !num2)
		return 0;
	return 1;
}

static void
doit(int just, int plus, int alt, int zero, int width, int prec, int spec)
{
	char format[256];
	char *p;
	const char *s;
	int i;

	p = format;
	*p++ = '%';
	if (just > 0)
		*p++ = "-"[just - 1];
	if (plus > 0)
		*p++ = "+ "[plus - 1];
	if (alt > 0)
		*p++ = "#"[alt - 1];
	if (zero > 0)
		*p++ = "0"[zero - 1];

	s = "";
	switch (width) {
	case 1: s = "1"; break;
	case 2: s = "5"; break;
	case 3: s = "10"; break;
	case 4: s = "15"; break;
	}
	strcpy(p, s);

	s = "";
	switch (prec) {
	case 1: s = ".0"; break;
	case 2: s = ".2"; break;
	case 3: s = ".5"; break;
	case 4: s = ".15"; break;
	}
	strcat(p, s);

	p = strchr(p, '\0');
	*p++ = "efgEG"[spec];
	*p = '\0';

	for (i = 0; i < sizeof(fmtvals) / sizeof(fmtvals[0]); i++) {
		char ref[1024], buf[1024];
		Rune rbuf[1024];
		double d1, d2;

		sprintf(ref, format, fmtvals[i]);
		snprint(buf, sizeof(buf), format, fmtvals[i]);
		if (strcmp(ref, buf) != 0
		&& !numclose(ref, buf)) {
			d1 = fmtstrtod(ref, 0);
			d2 = fmtstrtod(buf, 0);
			fprintf(stderr, "%s: ref='%s'%s fmt='%s'%s\n",
				format,
				ref, d1==fmtvals[i] ? "" : " (ref is inexact!)",
				buf, d2==fmtvals[i] ? "" : " (fmt is inexact!)");
		//	exits("oops");
		}

		/* Check again with output to rune string */
		runesnprint(rbuf, 1024, format, fmtvals[i]);
		snprint(buf, sizeof(buf), "%S", rbuf);
		if (strcmp(ref, buf) != 0
		&& !numclose(ref, buf)) {
			d1 = fmtstrtod(ref, 0);
			d2 = fmtstrtod(buf, 0);
			fprintf(stderr, "%s: ref='%s'%s fmt='%s'%s\n",
				format,
				ref, d1==fmtvals[i] ? "" : " (ref is inexact!)",
				buf, d2==fmtvals[i] ? "" : " (fmt is inexact!)");
		//	exits("oops");
		}
	}
}

void
main(int argc, char **argv)
{
	int just, plus, alt, zero, width, prec, spec;

	for (just = 0; just < Njust; just++)
	for (plus = 0; plus < Nplus; plus++)
	for (alt = 0; alt < Nalt; alt++)
	for (zero = 0; zero < Nzero; zero++)
	for (width = 0; width < Nwidth; width++)
	for (prec = 0; prec < Nprec; prec++)
	for (spec = 0; spec < Nspec; spec++)
		doit(just, plus, alt, zero, width, prec, spec);

	exits(0);
}
