How to relate Roman and Arabic numerals up to 899999 in a pure manner? And this time without using arithmetic! Thus without (is)/2
nor clpfd
/clpz
/clpq
. Lacking any means to relate to integers, Arabic numerals have to be represented as a list of characters. Thus :- set_prolog_flag(double_quotes, chars).
. The characters used for Roman numbers are below.
As usual, Arabic numerals do not have leading zeroes, nor spaces in between, the Roman numerals use the "common" subtractive rules. See below.
What I have tried so far was to use integers. But this here should be without them. Efficiency isn't the goal, but termination is.
The pure part includes dif/2
and dif_si/2
although 90 facts could express the inequality between decimal digits as well.
?- arabic_roman(A,R) , false.
false. % it terminates, we have only 899999 numbers, very finite
?- arabic_roman("0",R).
false. % no zero
?- arabic_roman(['0'|_],R).
false. % and in general no leading zeros
?- R=[_], arabic_roman(A,R).
R = "I", A = "1"
; R = "V", A = "5"
; R = "X", A = "10"
; R = "L", A = "50"
; R = "C", A = "100"
; R = "D", A = "500" % no apostrophus "IↃ"
; R = "M", A = "1000" % no "CIↃ"
; R = "ↁ", A = "5000" % idem
; R = "ↂ", A = "10000"
; R = "ↇ", A = "50000"
; R = "ↈ", A = "100000"
; R = "@", A = "500000" % poor support in Unicode
; false.
?- arabic_roman("899999",R).
R = "@ↈↈↈↂↈMↂCMXCIX" % the largest number
; false.
?- arabic_roman( ['9',_,_, _,_,_],R).
false. % no six digit number starting with 9
?- arabic_roman( [_, _,_,_, _,_,_|_],R).
false. % no seven or more digit numbers
?- setof(R,A^arabic_roman(A,R),Rs),length(Rs,N).
Rs = ["@","@C","@CC","@CCC"|_], N = 899999. % terms omitted at _
?- setof(A,R^arabic_roman(A,R),As),length(As,N).
As = ["1","10","100","1000","10000","100000","100001"|_], N = 899999.
?- setof(A-R,arabic_roman(A,R),ARs),length(ARs,N).
ARs = ["1"-"I","10"-"X","100"-"C","1000"-"M","10000"-"ↂ"|_], N = 899999.
?- arabic_roman(A,"IL").
false. % no generalized subtractive rule
?- arabic_roman(A,"IM").
false. % idem
?- arabic_roman(A,R), phrase((...,[X,X,X,X],...),R).
false.
With 15 clauses (depending how we are counting). It terminates.
:- use_module(library(dcgs)).
:- use_module(library(lists)).
:- use_module(library(dif)).
foldl(G__3, [], S, S) --> [].
foldl(G__3, [E|Es], S0, S) --> call(G__3, E, S0, S1), foldl(G__3, Es, S1, S).
d5u('0', s([R10|Rs],[R5,R1|Ds]), s([R1,R5,R10|Rs],Ds)) --> [].
d5u('1', s(Rs,[R5,R1|Ds]), s([R1,R5|Rs],Ds)) --> [R1].
d5u('2', s(Rs,[R5,R1|Ds]), s([R1,R5|Rs],Ds)) --> [R1,R1].
d5u('3', s(Rs,[R5,R1|Ds]), s([R1,R5|Rs],Ds)) --> [R1,R1,R1].
d5u('4', s(Rs,[R5,R1|Ds]), s([R1,R5|Rs],Ds)) --> [R1,R5].
d5u('5', s(Rs,[R5,R1|Ds]), s([R1,R5|Rs],Ds)) --> [R5].
d5u('6', s(Rs,[R5,R1|Ds]), s([R1,R5|Rs],Ds)) --> [R5,R1].
d5u('7', s(Rs,[R5,R1|Ds]), s([R1,R5|Rs],Ds)) --> [R5,R1,R1].
d5u('8', s(Rs,[R5,R1|Ds]), s([R1,R5|Rs],Ds)) --> [R5,R1,R1,R1].
d5u('9', s([R10|Rs],[R5,R1|Ds]), s([R1,R5,R10|Rs],Ds)) --> [R1,R10].
au5(_, s([R1,R5|Rs0],Rs), s(Rs0,[R5,R1|Rs])).
arabic_roman([C|Cs]) -->
{ foldl(au5, [C|Cs], s("IVXLCDMↁↂↇↈ@",[]), S0) },
foldl(d5u,[C|Cs], S0, s([_|_],[])),
{ dif(C, '0') }.
arabic_roman(A, R) :-
phrase(arabic_roman(A), R).