/* This program works for numbers between 1 and 2,147,483,647  with only 31 numbers in the KB */
/* 	To translate a number to words, run 
		trans(number).
	To translate words into number, use
		sd(Words, Number).
	where Words is the number written as list of words (WITHOUT the word 'and')
	e.g. sd([fifty, two, thousand, eight, hundred, twenty, six], Number).			*/

/* NUMBER TO STRING */

/* FOR SWI PROLOG USE THIS ONE */
myconcat(First, Second, Result) :-
	atom_concat(First, ' ', R),
	atom_concat(R, Second, Result).


/* FOR LPA PROLOG USE THIS ONE */
/* myconcat(First, Second, Result) :-
	cat([First, ' ', Second], Result, _). */


/* this is to choose wheter it is a hundred and something or just a hundred */
check_hundreds('hundred', 0, 'hundred'):-!.
check_hundreds('hundred', _, 'hundred and'):-!.
check_hundreds(Word,_,Word).


is_bigger_than(Number, 1000000000) :- Number > 999999999,!.
is_bigger_than(Number, 1000000) :- Number > 999999,!.
is_bigger_than(Number, 1000) :- Number > 999,!.
is_bigger_than(Number, 100) :- Number > 99,!.
is_bigger_than(Number, 10) :- Number > 9,!.
is_bigger_than(_, 1).

get_trans(Number, 'range check error') :- 		/* check whether number is less then 2^31 (to fit into integer) */
	Number > 2147483647.

get_trans(Number,String) :- pair(Number, String).	/* trivial case #1: number found in KB, no problem */

get_trans(Number, String) :-				/* trivial case #2: this is for the 21-99, because they behave differently */
	is_bigger_than(Number, Bigger), 		/* this may look silly, but it is different from is_bigger_than(Number, 10) */
	Bigger = 10,				/* this asks for the LARGEST number that succeeds and checks whether it is 10 */
	Top is (Number // 10)*10, Bottom is (Number - Top),		
	pair(Top, TopStr), pair(Bottom, BottomStr),		
	myconcat(TopStr, BottomStr, String). 		

get_trans(Number, String) :-				/* and this does the rest, i.e. 100 - 2,147,483,647 */
	is_bigger_than(Number, Bigger),		/* find the ORDER of the number */

	Top is (Number // Bigger),			/* get the top */
	Bottom is (Number mod  Bigger),		/* get to bottom */

	get_trans(Top, TopStr),			/* recursively translate the top and the bottom */
	get_trans(Bottom, BottomStr),		

	pair(Bigger, MiddleWord),			/* get the middle word, e.g. 'thousand' */

	check_hundreds(MiddleWord, Bottom, TempStr),	/* see whether the word 'and' is needed */

	myconcat(TopStr, TempStr, TopStr2),		/* Top + Middle */
	myconcat(TopStr2, BottomStr, String),		/* (Top + Middle) + Buttom */
	!. 					/* DONE */

trans(Number) :- trans(Number, String), write(String).	/* arity one predicate */
trans(Number, String) :- get_trans(Number, String).	/* arity two predicate */

trans_list([Head1],[Head2]) :- get_trans(Head1, Head2).	/* and this one is for lists */
trans_list([Head1|Tail1], [Head2|Tail2]) :-
	trans_list(Tail1, Tail2), 
	get_trans(Head1, Head2).

/* STRING TO NUMBER */

/* standard list concatenation */
list_concat([], L2, L2).
list_concat([H|T],L2, [H|L3]) :- list_concat(T, L2, L3).

/* this converts list of words into list of numbers */
conv_to_digits([H1], [H2]) :- pair(H2,H1).
conv_to_digits([H1|T1], [H2|T2]) :- conv_to_digits(T1, T2), pair(H2,H1).

order(4, 1000000000).		/* billion */
order(3, 1000000).			/* million */
order(2, 1000).			/* thousand */
order(1, 100).			/* hundred */
order(0, 1).			/* rest */


rev_trans(_, [],0):-!.			/* empty list is zero */
rev_trans(0, [N1], N1):-!.			/* order 0, only one number present */
rev_trans(0, [N1,N2], Number) :- Number is N1 + N2,!.	/*order 0, two numbers present */

rev_trans(O, List, Number) :-
	order(O, Order),			/* get the corresponding number */
	member(Order, List),		/* check whether it is in the list, if not, see next clause */
	list_concat(Pre, [Order|Tail], List),	/* chop off the bits before and after the ORDER */
	NewOrder is O - 1,			/* lower order by one */
	rev_trans(NewOrder, Pre, PreN),	/* translate the bits using this new order */
	rev_trans(NewOrder, Tail, PostN),
	Number is Order*PreN + PostN.	/* multiply the starting bit by the order and add the trailing bit */

rev_trans(Order, List, Number) :-		/* if order not in the list, try lower order */
	NewOrder is Order -1,
	rev_trans(NewOrder, List, Number).

/* 
sn([ninety, two, million, eight, hundred, fifty, one, thousand, five, hundred, thirty, one], L).
*/	

sn(List, Number) :-
	conv_to_digits(List, Digits),		/* convert words to numbers */
	rev_trans(4, Digits, Number),!.	/* translate list of numbers into a number */

/* the knowlegde base */
pair(0, '').
pair(1, 'one').
pair(2, 'two').
pair(3, 'three').
pair(4, 'four').
pair(5, 'five').
pair(6, 'six').
pair(7, 'seven').
pair(8, 'eight').
pair(9, 'nine').
pair(10, 'ten').
pair(11, 'eleven').
pair(12, 'twelve').
pair(13, 'thirteen').
pair(14, 'fourteen').
pair(15, 'fifteen').
pair(16, 'sixteen').
pair(17, 'seventeen').
pair(18, 'eighteen').
pair(19, 'nineteen').
pair(20, 'twenty').
pair(30, 'thirty').
pair(40, 'forty').
pair(50, 'fifty').
pair(60, 'sixty').
pair(70, 'seventy').
pair(80, 'eighty').
pair(90, 'ninety').
pair(100, 'hundred').
pair(1000, 'thousand').
pair(1000000, 'million').
pair(1000000000, 'billion').


/* 2001 Jan Strojil */

