CCCPaste

FACTORIAL RELATION - PROLOG

% Reversible factorial relation
% fact(N, F) is true when F = N!
% Works when N is given, when F is given, or when both are variables.

% public entry
fact(N, F) :-
    (  nonvar(N) ->                % N is known: compute F directly
         integer(N), N >= 0,
         fact_from_n(N, F)
    ;  nonvar(F) ->                % F is known: search N by growing factorial
         integer(F), F >= 0,
         fact_from_f(F, N)
    ;  % both variables: enumerate N = 0,1,2,...
         nat(N),
         fact_from_n(N, F)
    ).

% compute factorial when N is known
fact_from_n(0, 1) :- !.
fact_from_n(N, F) :-
    N > 0,
    N1 is N - 1,
    fact_from_n(N1, F1),
    F is F1 * N.

% find N given F by iterative search
% handles F = 0 only if you want to treat 0 specially (0! = 1), so we require F >= 0
fact_from_f(F, N) :-
    % start search from 0 with accumulator 1
    fact_search(0, 1, F, N).

% fact_search(CurrentN, Acc, TargetF, ResultN)
fact_search(N, Acc, F, N) :-
    Acc =:= F, !.
fact_search(N, Acc, F, Res) :-
    Acc < F,
    N1 is N + 1,
    Acc1 is Acc * N1,
    fact_search(N1, Acc1, F, Res).
% If Acc > F the search fails (no integer N with N! = F).
% nat/1 enumerates natural numbers 0,1,2,...
nat(0).
nat(N) :- nat(M), N is M + 1.