From 8131295c044eae68de2eb7465384edd56838dc4a Mon Sep 17 00:00:00 2001
From: tomsmeding <tom.smeding@gmail.com>
Date: Sun, 5 Jun 2016 23:36:35 +0200
Subject: First

---
 Makefile |  15 +++++
 number   | Bin 0 -> 13512 bytes
 number.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 204 insertions(+)
 create mode 100644 Makefile
 create mode 100755 number
 create mode 100644 number.c

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2b814d4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+CC = gcc
+CFLAGS = -Wall -Wextra -O2 -std=c11 -fwrapv
+BIN = number
+
+.PHONY: all clean remake
+
+all: $(BIN)
+
+clean:
+	rm -f $(BIN)
+
+remake: clean all
+
+$(BIN): $(wildcard *.c *.h)
+	$(CC) $(CFLAGS) -o $@ $(filter %.c,$^)
diff --git a/number b/number
new file mode 100755
index 0000000..ee06277
Binary files /dev/null and b/number differ
diff --git a/number.c b/number.c
new file mode 100644
index 0000000..27df086
--- /dev/null
+++ b/number.c
@@ -0,0 +1,189 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+__attribute__((noreturn)) void usage(int argc,char **argv){
+	(void)argc;
+	fprintf(stderr,
+		"Usage: %s [-co] <number>\n"
+		"Converts a number to (British) English word form.\n"
+		"Currently supports only integers.\n"
+		"    -c  Capitalise the first output letter\n"
+		"    -o  Write as ordinal (first) instead of cardinal number (one)\n",
+		argv[0]);
+	exit(1);
+}
+
+char *thousands[6]={"thousand","million","billion","trillion","quadrillion","quintillion"};
+char *twenty[20]={
+	"zero","one","two","three","four","five","six","seven","eight","nine","ten",
+	"eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen"
+};
+char *tens[10]={
+	"zero","ten","twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety"
+};
+
+char *ordtwenty[20]={
+	"zeroth","first","second","third","fourth","fifth","sixth","seventh","eighth","ninth","tenth",
+	"eleventh","twelfth","thirteenth","fourteenth","fifteenth","sixteenth","seventeenth","eighteenth","nineteenth"
+};
+char *ordtens[10]={
+	"zeroth","tenth","twentieth","thirtieth","fortieth","fiftieth","sixtieth","seventieth","eightieth","ninetieth"
+};
+
+void addtobuf(char *text,char **buf){
+	int len=strlen(text);
+	strcpy(*buf,text);
+	*buf=*buf+len;
+}
+
+char* printnumber(intmax_t num,char *buf){
+	if(num<0){
+		addtobuf("minus ",&buf);
+		num=-num;
+	}
+
+	bool hadth=false;
+	if(num>=1000){
+		hadth=true;
+		intmax_t th[6],nth=0,rest;
+		rest=num%1000;
+		num/=1000;
+		while(num){
+			th[nth++]=num%1000;
+			num/=1000;
+		}
+		bool first=true;
+		while(nth-->0){
+			if(th[nth]==0)continue;
+			if(!first)addtobuf(" ",&buf);
+			buf=printnumber(th[nth],buf);
+			addtobuf(" ",&buf);
+			addtobuf(thousands[nth],&buf);
+			first=false;
+		}
+		num=rest;
+		if(num==0)return buf;
+		addtobuf(" ",&buf);
+	}
+
+	bool and=(num>=100||hadth)&&num%100!=0;
+	if(num>=100){
+		buf=printnumber(num/100,buf);
+		addtobuf(" ",&buf);
+		addtobuf("hundred",&buf);
+		num%=100;
+		if(num)addtobuf(" ",&buf);
+		else return buf;
+	}
+
+	if(and)addtobuf("and ",&buf);
+
+	if(num%10==0)addtobuf(tens[num/10],&buf);
+	else if(num<20)addtobuf(twenty[num],&buf);
+	else {
+		addtobuf(tens[num/10],&buf);
+		addtobuf("-",&buf);
+		addtobuf(twenty[num%10],&buf);
+	}
+	return buf;
+}
+
+char* printordinal(intmax_t num,char *buf){
+	if(num<0){
+		addtobuf("minus ",&buf);
+		num=-num;
+	}
+
+	bool hadth=false;
+	if(num>=1000){
+		hadth=true;
+		buf=printnumber(num/1000*1000,buf);
+		num%=1000;
+		if(num==0){
+			addtobuf("th",&buf);
+			return buf;
+		}
+		addtobuf(" ",&buf);
+	}
+
+	if(hadth&&num/100==0&&num%100!=0)addtobuf("and ",&buf);
+
+	if(num<20){
+		addtobuf(ordtwenty[num],&buf);
+		return buf;
+	}
+
+	if(num<100){
+		if(num%10==0)addtobuf(ordtens[num/10],&buf);
+		else {
+			addtobuf(tens[num/10],&buf);
+			addtobuf("-",&buf);
+			addtobuf(ordtwenty[num%10],&buf);
+		}
+		return buf;
+	}
+
+	buf=printnumber(num/100*100,buf);
+	if(num%100==0){
+		addtobuf("th",&buf);
+		return buf;
+	}
+	addtobuf(" and ",&buf);
+	return printordinal(num%100,buf);
+}
+
+int main(int argc,char **argv){
+	char *numstr=NULL;
+	bool ordinal=false,capfirst=false;
+	for(int i=1;i<argc;i++){
+		if(argv[i][0]=='\0'){
+			fprintf(stderr,"Unexpected empty argument\n");
+			usage(argc,argv);
+		}
+		if(argv[i][0]=='-'){
+			for(int j=1;argv[i][j];j++){
+				switch(argv[i][j]){
+					case 'c': capfirst=true; break;
+					case 'o': ordinal=true; break;
+					default:
+						fprintf(stderr,"Unrecognised flag -%c\n",argv[i][j]);
+						usage(argc,argv);
+				}
+			}
+		} else {
+			if(numstr==NULL)numstr=argv[i];
+			else {
+				fprintf(stderr,"Unexpected second bare argument '%s'\n",argv[i]);
+				usage(argc,argv);
+			}
+		}
+	}
+	if(numstr==NULL){
+		fprintf(stderr,"No number given\n");
+		usage(argc,argv);
+	}
+
+	char *endp;
+	intmax_t num=strtoimax(numstr,&endp,10);
+	if(*endp!='\0'){
+		fprintf(stderr,"Invalid number: '%s'\n",numstr);
+		return 1;
+	}
+	char *repr;
+	asprintf(&repr,"%jd",num);
+	if(strcmp(numstr,repr)!=0){
+		fprintf(stderr,"Outside range of %lu-bit number: '%s'\n",8*sizeof(intmax_t),numstr);
+		return 1;
+	}
+
+	char buf[300]={'\0'};
+	
+	if(ordinal)printordinal(num,buf);
+	else printnumber(num,buf);
+	if(capfirst)buf[0]-='a'-'A';
+	printf("%s\n",buf);
+	return 0;
+}
-- 
cgit v1.2.3-70-g09d2