summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile15
-rwxr-xr-xnumberbin0 -> 13512 bytes
-rw-r--r--number.c189
3 files changed, 204 insertions, 0 deletions
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
--- /dev/null
+++ b/number
Binary files 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;
+}