% vim: set ft=prolog:

% Usa o arquivo plunit.pl se é uma versão é < 6.0
:- current_prolog_flag(version, V), V < 60000, use_module(plunit) ; true.
% Usa o arquivo plunit.pl que vem com o Prolog se a versão é > 6.0.0
:- current_prolog_flag(version, V), V > 60000, use_module(library(plunit)) ; true.


%%%%%%%%%%%
% Exemplo 1 - Gerador de números inteiros

%% inteiro(-N) is nondet
%  
%  Verdadeiro se N é um número inteiro não negativo. Este predicado é um
%  gerador. A cada fez que ele é satisfeito ele gera um número.
%
%  Exemplos
%  ?- int(N).
%  N = 0 ;
%  N = 1 ;
%  N = 2 ;
%  ....

:- begin_tests(inteiro).

% Pergunta: como testar um gerador infinito?
% Resposta: usando corte com fail e ou ... Tente entender este teste
test(t0, all(N == [0, 1, 2, 3])) :-
    inteiro(N),
    ( N < 4
    ; N >= 4, !, fail
    ).

:- end_tests(inteiro).

inteiro(0).
inteiro(N) :-
	inteiro(N0),
	N is N0 + 1.


%% inteirox(?N) is nondet
%
%  Versão do predicado inteiro/1 que funciona com argumento instanciado ou não.

:- begin_tests(inteirox).

test(t0) :-
    inteirox(8).

test(t1, all(N == [0, 1, 2, 3])) :-
    inteirox(N),
    ( N < 4
    ; N >= 4, !, fail
    ).

:- end_tests(inteirox).

inteirox(N) :-
    nonvar(N), !,
    integer(N). % predicado pré-definido que é verdadeiro se N é inteiro.

inteirox(0).
inteirox(N) :-
    inteiro(N0),
    N is N0 + 1.

%% int(-N) is nondet
%
%  Versão eficiente do predicado inteiro/1.

:- begin_tests(int).

test(t0, all(N == [0, 1, 2, 3])) :-
    int(N),
    ( N < 4
    ; N >= 4, !, fail
    ).

:- end_tests(int).

int(N) :-
    int(0, N).

int(N, N).
int(N, X) :-
	N1 is N + 1,
	int(N1, X).



%%%%%%%%%%%
% Exemplo 2 - Combinação do corte com fail

%% aprova(?A) is nondet
%
%  Verdadeiro se o aluno A foi aprovado.

:- begin_tests(aprovado).

test(t0, all(A == [jose, andre])) :-
    aluno(A),
    aprovado(A).

:- end_tests(aprovado).

aprovado(A) :-
    faltas(A, F),
    F > 25,
    !,
    fail.

aprovado(A) :-
    media(A, M),
    M >= 6, !.

aprovado(A) :-
    media(A, M),
    exame(A, E),
    (M + E)/2 >= 5.

aluno(jose).
aluno(paulo).
aluno(andre).

faltas(jose, 10).
faltas(paulo, 30).
faltas(andre, 0).

media(jose, 7).
media(paulo, 8).
media(andre, 4).

exame(andre, 6.5).


%%%%%%%%%%%
% Exemplo 3 - Estratégia gerar e testa

%% primeiro_primo(+N, ?P) is semidet
%
%  Verdadeiro se P é o primeiro primo maior ou igual a N.

:- begin_tests(primeiro_primo).

test(t0, P == 17) :- primeiro_primo(14, P).
test(t0, P == 7) :- primeiro_primo(7, P).

:- end_tests(primeiro_primo).

primeiro_primo(N, P) :-
	int(N, P), % gerar um candidado
	primo(P),  % testa o candidado
    !.         % interrompe o processo após achar uma solução


%% goldbach(+X, -A, -B?) is semidet
%
%  Verdadeiro se a conjectura de goldbach é verdadeira para o número par X.
%  Ou seja, se existem A e B primos tal que X = A + B.

:- begin_tests(goldbach).

test(t0, (A, B) == (3, 5)) :- goldbach(8, A, B).
test(t1, (A, B) == (3, 19)) :- goldbach(22, A, B).

:- end_tests(goldbach).

goldbach(X, A, B) :-
	par(X),
	soma(X, A, B),  % gerar um candidado
	primo(A),       % testa
	primo(B),       % testa
	!.              % interrompe o processo

par(X) :- divisivel(X, 2).


%% soma(+A, -B, -C) is nondet
%
%  Verdadeiro se A = B + C. Veja o predicado pré-definido plus/3.
%  Observe que este predicado é um gerador.

soma(A, B, C) :-
    soma(1, A, B, C).

soma(N, A, B, C) :-
    N < A,
    B is N,
    C is A - B.

soma(N, A, B, C) :-
    N < A,
    N1 is N + 1,
    soma(N1, A, B, C).


% Os predicados a seguir foram copiados do arquivo exercicios_fundamentos.pl

%% primo(+X) is semidet
%  
%  Verdadeiro se X é um número primo.

primo(X) :-
    menor_divisor(X, 2, Y),
    X =:= Y.

%% menor_divisor(+X, +D, ?Y) is semidet
%
%  Verdadeiro se Y é o menor divisor de X maior ou igual a D.

% X é o menor divisor de X començando com X
menor_divisor(X, X, X) :- !.

% D é o menor divisor de X començando com D se X é divisível por D.
menor_divisor(X, D, D) :-
    divisivel(X, D), !.

% D não é divisor de X
menor_divisor(X, D, Y) :-
    \+ divisivel(X, D),
    D1 is D + 1,
    D1 =< X,
    menor_divisor(X, D1, Y).


%% Divisivel(+X, +Y) is semidet
%
% Verdadeiro se X é divisível por Y.

divisivel(X, Y) :-
    X rem Y =:= 0.
