%
% ex85.pl - 「6. 簡単なパズル(?)を解かせる」の5.
%

% gennum1/1 - 一桁の数値生成
gennum1(0).
gennum1(1).
gennum1(2).
gennum1(3).
gennum1(4).
gennum1(5).
gennum1(6).
gennum1(7).
gennum1(8).
gennum1(9).

% 4項からなる式を優先順位に従って計算する。
% ex84.plとは発想を変えて、計算と同時に式を組み立てる。
% (演算順序の組み合わせとしては5通り)
opr(A, B, C, D, Z, [[[A, Op1, B], Op2, C], Op3, D]) :-
    ops(A, B, X, Op1),
    ops(X, C, Y, Op2),
    ops(Y, D, Z, Op3).
opr(A, B, C, D, Z, [[A, Op1, B], Op2, [C, Op3, D]]) :-
    ops(A, B, X, Op1),
    ops(C, D, Y, Op3),
    ops(X, Y, Z, Op2).
% Op3, Op1, の順に演算するのは上と同等なので略。
opr(A, B, C, D, Z, [[A, Op1, [B, Op2, C]], Op3, D]) :-
    ops(B, C, X, Op2),
    ops(A, X, Y, Op1),
    ops(Y, D, Z, Op3).
opr(A, B, C, D, Z, [A, Op1, [[B, Op2, C], Op3, D]]) :-
    ops(B, C, X, Op2),
    ops(X, D, Y, Op3),
    ops(A, Y, Z, Op1).
opr(A, B, C, D, Z, [A, Op1, [B, Op2, [C, Op3, D]]]) :-
    ops(C, D, X, Op3),
    ops(B, X, Y, Op2),
    ops(A, Y, Z, Op1).

% 式「A Op1 B Op2 C Op3 D」の結果が10であるなら真
opr10(Exp) :-
    gennum1(A),
    gennum1(B), B \= A,
    gennum1(C), C \= A, C \= B,
    gennum1(D), D \= A, D \= B, D \= C,
    opr(A, B, C, D, 10, Exp).

% ops(X, Y, Z, Op) は、式「Z is X Op Y」を意味する。
% ここで、Opは演算子である。
ops(X, Y, Z, +) :- Z is X + Y.
ops(X, Y, Z, -) :- Z is X - Y.
ops(X, Y, Z, *) :- Z is X * Y.
ops(X, Y, Z, /) :- Y \= 0, Z is X rdiv Y.
% 注: rdivは / と同様に除算であるが、結果を小数ではなく分数とする。
%     これを用いると 1 / 3 * 3 等も正確に計算される。
%     除算の際は、0で割るとエラーになるので、そのチェックも入れておく。

% 一々手でバックトラックを指示するのは面倒なので、自動的にバックトラック
% しながら、画面に表示させる述語も定義しておく。
all_opr10 :-
    opr10(Exp),
    list2form(Exp, Form),
    writeln(Form),
    fail.
all_opr10.

% リストで表した式を通常の式の表現に変換する。
% string_to_atom/2, atom_to_term/3, atom_codes/2 についてはSWI-Prolog
% のマニュアルを参照のこと。
% なお、これは見易さだけの問題なので、これを使わずリストのまま表示
% するのでも本質的には変わらない。
list2form(Exp, Form) :-
    list2str(Exp, Str),
    string_to_atom(Str, Atom),
    atom_to_term(Atom, Form, []).
list2str([A, O, B], Str) :- !,
    list2str(A, Sa),
    atom_codes(O, So),
    list2str(B, Sb),
    append("(", Sa, X),
    append(X, So, Y),
    append(Y, Sb, Z),
    append(Z, ")", Str).
list2str([A, O1, B, O2, C], Str) :- !,
    list2str(A, Sa),
    atom_codes(O1, So1),
    list2str(B, Sb),
    atom_codes(O2, So2),
    list2str(C, Sc),
    append("(", Sa, X),
    append(X, So1, Y),
    append(Y, Sb, Z),
    append(Z, So2, W),
    append(W, Sc, V),
    append(V, ")", Str).
list2str([A, O1, B, O2, C, O3, D], Str) :- !,
    list2str(A, Sa),
    atom_codes(O1, So1),
    list2str(B, Sb),
    atom_codes(O2, So2),
    list2str(C, Sc),
    atom_codes(O3, So3),
    list2str(D, Sd),
    append("(", Sa, X),
    append(X, So1, Y),
    append(Y, Sb, Z),
    append(Z, So2, W),
    append(W, Sc, V),
    append(V, So3, U),
    append(U, Sd, T),
    append(T, ")", Str).
list2str(A, Str) :-
    atom_codes(A, Str).

