%% Cryptarithmetic puzzle  (? Ref. CLP paper, Proc. 4th ICLP)

%% version for andorra1.7 (i.e. after added parser and new built-ins)
:- module(crypt,[go/0,test/0],_).

:- use_module(library(write), [write/1]).

%:- determinate( test, true ).

test:-
    solve(A,B,C,D,E,F,G,H),
%       wakeup(_1,_3), to include in the result!!!
    print_out(A,B,C,D,E,F,G,H).

%:- determinate( go, true ).

go:- solve(S,E,N,D,M,O,R,Y).

%:- determinate( go(X), true ).

go(fn(S,E,N,D,M,O,R,Y)):- solve(S,E,N,D,M,O,R,Y),
                    print_out(S,E,N,D,M,O,R,Y).

%:- determinate( solve(S,E,N,D,M,O,R,Y), true ).

solve(S,E,N,D,M,O,R,Y):- 
    M = 1,                  %% M must be 1
    carry(C1,C2,C3),
    ganerate(S,E,N,D,M,O,R,Y),
    S > 0,                  %% S can't be 0
    const(0,D,E,Y,C1),      %% D+E = Y+10*C1
    const(C1,N,R,E,C2),     %% C1+N+R = E+10*C2
    const(C2,E,O,N,C3),     %% C2+E+O = N+10*C3
    const(C3,S,M,O,M).      %% C3+S+M = O+10*M


%:- determinate(  const(A,B,C,D,E), (
%       ground(E), ground(D), ground(B), ground(C) ;
%       ground(D), ground(E), ground(A), ground(C) ;
%       ground(A), ground(E), ground(D), ground(B) ;
%       ground(B), ground(E), ground(A), ground(C) ;
%       ground(C), ground(D), ground(A), ground(B) ) ).

const(A,B,C,D,E):-              %% if only A is unbound
    A is 10 * E + D - B - C,!.
const(A,B,C,D,E):-              %% if only B is unbound
    B is 10 * E + D - A - C,!.
const(A,B,C,D,E):-              %% if only C is unbound
    C is 10 * E + D - A - B,!.
const(A,B,C,D,E):-              %% if only D is unbound
    D is C - 10 * E + A + B,!.
const(A,B,C,D,E):-              %% if only E is unbound
    E is (C - D + A + B) / 10.
    
%:- determinate( ganerate(S,E,N,D,M,O,R,Y), true ).

ganerate(S,E,N,D,M,O,R,Y):- 
    digi(S,1), 
    digi(E,2), 
    digi(N,3), 
    digi(D,4), 
    digi(M,5),
    digi(O,6), 
    digi(R,7), 
    digi(Y,8), 
    difflist([S,E,N,D,M,O,R,Y]).

%:- determinate( difflist(A), nonvar(A) ).

difflist([]).
difflist([X|T]):- not_member(X,T), difflist(T).

%:- determinate( not_member(A,B), nonvar(B) ).

not_member( X, [] ).
not_member( X, [Y|L] ):- not_same(X,Y), not_member( X, L ).

%:- determinate( not_same(A,B), true ).

not_same(X,Y):- X\==Y.

%:- determinate( digi(A,B), nonvar(A) ).

%% should be digi/1, the second argument is only for debugging
digi(9,X).
digi(8,X).
digi(7,X).
digi(6,X).
digi(5,X).
digi(4,X).
digi(3,X).
digi(2,X).
digi(1,X).
digi(0,X).

%:- determinate( carry(A,B,C), ( nonvar(A),nonvar(B),nonvar(C) ) ).

carry(1,1,1).
carry(1,1,0).
carry(1,0,1).
carry(1,0,0).
carry(0,1,1).
carry(0,1,0).
carry(0,0,1).
carry(0,0,0).

%:- determinate( print_out(S,E,N,D,M,O,R,Y), true ).

print_out(S,E,N,D,M,O,R,Y):- 
    write('    SEND'), nl,
    write(' +  MORE'), nl,
    write('-----------'), nl,
    write('   MONEY'), nl, nl,
    write('The solution is:'), nl, nl,
    write('   '), write(S),write(E),write(N),write(D), nl,
    write(' + '), write(M),write(O),write(R),write(E), nl,
    write('-----------'), nl,
    write('  '),
    write(M),write(O),write(N),write(E),write(Y), nl.
