% vim: set ft=prolog:

% Atenção: os predicados deste arquivo não foram escritos para serem
% eficientes, mas para serem claros e exemplificar o conteúdo estudado.

%% Ex 5 %%
%% permutacao(+L, -P) is nondet
%
%  Gera listas P tal que P é uma permutação de L. P deve ser uma variável.
%  Veja os predicados select/3 e permutation/2. Um bom exercícios complementar
%  é a implementação do predicado select/3.
%  Faça a simulação deste predicado para entender como ele funciona.
%  Exemplo
%  ?- permutacao([a, b, c], P).
%  P = [a, b, c] ;
%  P = [b, a, c] ;
%  P = [b, c, a] ;
%  P = [a, c, b] ;
%  P = [c, a, b] ;
%  P = [c, b, a] ;
%  false.

permutacao([], []).

permutacao(L, P) :-
    select(X, L, R), !, 
    permutacao(R, T),
    select(X, P, T).


%% Ex 6 %%
%% ordenacao(+L, -S) is semidet
%
%  Verdadeiro se S é a lista L com os elementos ordenados.
%  Exemplo
%  ?- ordenacao([7, 2, 4, 3], S).
%  S = [2, 3, 4, 7].

ordenacao(L, S) :-
    permutacao(L, S),
    ordenado(S), !.


%% ordenado(+L) is semidet
%
%  Verdadeiro se L é uma lista de números ordenados.

ordenado([]).
ordenado([_]).
ordenado([A,B|R]) :-
    A =< B,
    ordenado([B|R]).


%% Ex 7 %%
%% primo(?N) is nondet
%
%  Verdadeiro se N é um número primo. Quando N é uma variável, este predicado
%  gerar números primos.

% N é primo se N é inteiro e não existe divisor de N entre 2 e N-1.
primo(N) :-
    integer(N), !,
    N0 is N - 1,
    \+((divisor(2, N0, _, N))).

% Gera todos os número primos começando com 2.
primo(N) :-
    entre(2, infinito, N),
    primo(N).


%% divisor(+A, +B, -D, +N) is nondet
%
%  Gera divisores D de N tal que A <= D <= B. D deve ser uma variável.

divisor(A, B, D, N) :-
    var(D),
    entre(A, B, D),
    divisivel(N, D).

%% entre(+A, +B, -N) is nondet
%
%  Gerar inteiros N tal que A <= N <= B. Se B = infinito, então gera inteiros N
%  tal que A <= N. N deve ser uma variável.
%  Veja o predicado pré-definido between/3.

entre(_, _, N) :-
    nonvar(N), !, fail.

entre(A, infinito, A).
entre(A, infinito, N) :-
    A1 is A + 1,
    entre(A1, infinito, N).

entre(A, B, A) :-
    A =< B.
entre(A, B, N) :-
    A < B,
    A1 is A + 1,
    entre(A1, B, N).


%% divisivel(+N, +D) is semidet
%
%  Verdadeiro se N é divisível por D.

divisivel(N, D) :-
    N mod D =:= 0.
