#include <stdio.h>
#include <stdint.h>
#include <string.h>

static const char* const ones[] = {"","one ","two ","three ","four ","five ","six ","seven ","eight ","nine "};
static const char* const tens[] = {"","ten","twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety"};
static const char* const teens[] = {"ten ","eleven ","twelve ","thirteen ","fourteen ","fifteen ","sixteen ","seventeen ","eighteen ","nineteen "};

static const char* const orders[] =
{
	"",
	"", "", "",
	"thousand ", "thousand ", "thousand ",
	"million ", "million ", "million ",
	"billion ", "billion ", "billion ",
	"trillion ", "trillion ", "trillion ",
	"quadrillion ", "quadrillion ", "quadrillion ",
	"quintillion ", "quintillion ", "quintillion ",
	"sextillion ", "sextillion ", "sextillion ",
	"septillion ", "septillion ", "septillion ",
	"octillion ", "octillion ", "octillion ",
	"nonillion ", "nonillion ", "nonillion ",
	"decillion ", "decillion ", "decillion ",
	"undecillion ", "undecillion ", "undecillion ",
	"duodecillion ", "duodecillion ", "duodecillion ",
	"tredecillion ", "tredecillion ", "tredecillion ",
	"quattuordecillion ", "quattuordecillion ", "quattuordecillion ",
	"quindecillion ", "quindecillion ", "quindecillion ",
	"sexdecillion ", "sexdecillion ", "sexdecillion ",
	"septendecillion ", "septendecillion ", "septendecillion ",
	"octodecillion ", "octodecillion ", "octodecillion ",
	"novemdecillion ", "novemdecillion ", "novemdecillion ",
	"vigintillion ", "vigintillion ", "vigintillion "
};

static const int const ones2[]  = { 0, 4, 4, 6, 5, 5, 4, 6,  6, 5 };
static const int const teens2[] = { 4, 7, 7, 9, 9, 8, 8, 10, 9, 9 };
static const int const tens2[]  = { 0, 3, 6, 6, 5, 5, 5, 7,  6, 6 };
static const int const orders2[] =
{
	0,							// not used
	0,	0,	0,			// not used
	9,	9,	9,			// thousand
	8,	8,	8,			// million
	8,	8,	8,			// billion
	9,  9,	9,			// trillion
	12, 12, 12,			// quadrillion
	12, 12, 12,			// quintillion
	11, 11, 11,			// sextillion
	11, 11, 11,			// septillion
	10, 10, 10,			// octillion
	10,	10,	10,			// nonillion
	10,	10,	10,			// decillion
	12,	12,	12,			// undecillion
	13,	13,	13,			// duodecillion
	13,	13,	13,			// tredecillion
	18,	18,	18,			// quattuordecillion
	14,	14,	14,			// quindecillion
	13,	13,	13,			// sexdecillion
	16,	16,	16,			// septendecillion
	14,	14,	14,			// octodecillion
	15,	15,	15,			// novemdecillion
	13,	13,	13			// vigintillion
};

void str_to_english (const char*, int, char*);
char* dec_to_english (const char*, int, int, char*);
int base10_convert (uint64_t, char*, int);
void copy_string2 (const char*, const char*, int);

#define copy_string(a,b,c) {copy_string2(a,b,c); a += c;}

/**
 * Copy from src to dst eight bytes at a time. The output buffer
 * obviously must be large enough to accommodate the multiple of 
 * eight immediately greater than the greatest input size.
 */
inline void
copy_string2 (const char *dst, const char *src, register int len)
{
	register uint64_t *dst2=(uint64_t*)dst, *src2=(uint64_t*)src;
	
	while (len > 0) {
		*dst2++ = *src2++;
		len -= 8;
	}
}

/**
 * Convert an integer into its base-10 equivalent, which is not the
 * printable ascii version. We store one base-10 digit per byte. To
 * print this string, add 0x30 to each byte.
 */
inline int
base10_convert (uint64_t inputNum, char *outputNum, int outputLen)
{
	char *outputPtr = outputNum + (outputLen - 2);
	register int digits=0, quotient;
	
	while (inputNum != 0) {
		quotient = inputNum / 10;
		*(outputPtr--) = (inputNum - (quotient * 10));
		inputNum = quotient;
	}
	
	digits = (outputLen-2) - (outputPtr - outputNum);
	
	copy_string2(outputNum, outputPtr+1, digits);
	*(outputNum+digits) = 0;
	
	return digits;
}

/**
 * Convert a base-10 number into its English equivalent, one chunk
 * (ie, three decimal digits) at a time.
 */
inline void
str_to_english (const char *inputNum, int inputLen, char *outputNum)
{
	int chunk, pos=inputLen;
	const char *dptr=inputNum;
	
	chunk = pos%3 == 0 ? 3 : pos%3;
	
	do {
		outputNum = dec_to_english(dptr, chunk, pos, outputNum);
		
		dptr += chunk;
		pos -= chunk;
		chunk = 3;
	}
	while (pos > 0);
}

/**
 * Convert a three-digit decimal number into its English equivalent.
 */
inline char *
dec_to_english (register const char *inputDec, int inputLen, int order, register char *outputNum)
{
	register char d_one=0, d_ten=0, d_hun=0;
	
	// parse the one's, ten's and hundred's digits
	if (inputLen >= 3) d_hun = *(inputDec++);
	if (inputLen >= 2) d_ten = *(inputDec++);
	if (inputLen >= 1) d_one = *(inputDec++);
	
	// handle a non-zero hundreds digit
	if (d_hun != 0) {
		copy_string(outputNum, ones[d_hun], ones2[d_hun]);
		copy_string(outputNum, "hundred ", 8);
	}
	
	// handle a non-zero tens digit
	if (d_ten > 0) {
		if (d_ten == 1) {
			copy_string(outputNum, teens[d_one], teens2[d_one])
		}
		else {
			copy_string(outputNum, tens[d_ten], tens2[d_ten]);
			copy_string(outputNum, (!d_one ? " " : "-"), 1);
		}
	}
	
	// handle a ones digit (if this isn't a "teen" number).
	if (d_ten != 1 && d_one != 0)
		copy_string(outputNum, ones[d_one], ones2[d_one]);
	
	// append the order-of-magnitude name, if there is one.
	if (orders2[order] > 3 && (d_one || d_ten || d_hun))
		copy_string(outputNum, orders[order], orders2[order]);
	
	*outputNum = '\0';
	
	return outputNum;
}

/**
 *
 */
int
main (int argc, const char * argv[])
{
	uint64_t i=1, declen;
	char number[1000], decstr[100];
	
	if (argc == 2) {
		declen = strlen(argv[1]);
		copy_string2(decstr, argv[1], declen);
		
		for (i=0; i < declen; ++i)
			decstr[i] -= 0x30;
		
		str_to_english(decstr, declen, number);
		
		printf("%s\n", number);
	}
	else {
		for (; i <= 100; ++i) {
			declen = base10_convert(i, decstr, 100);
			str_to_english(decstr, declen, number);
			printf("%s\n", number);
		}
	}
	
	return 0;
}
