:-use_module(library(clpfd)).
:-dynamic(pessoa/1).

%%%
%%% Neste exemplo há dois irmãos e só um deles tem um intervalo para o nascimento.
%%% O check vai buscar um par de irmãos, as datas de nascimento e cria novos limites.
%%%

:- retractall(pessoa(_)).
:- retractall(irmao(_, _)).

insere_irmao(A,C):-
	\+ irmao(A, C),
	\+ irmao(C, A),
	A \= C,
    assert(irmao(A,C)),
    check_irmao(A,C).

transitividade_irmao:-
	irmao(A, B), irmao(B, C),
	insere_irmao(A, C),
	fail.
transitividade_irmao:-
	irmao(A, B), irmao(C, B),
	insere_irmao(A, C),
	fail.
transitividade_irmao:-
	irmao(B, A), irmao(B, C),
	insere_irmao(A, C),
	fail.
transitividade_irmao.

pessoa_com_nome(N, P):-
	P = p(N, _Ns, _BR, _DR),
	pessoa(P).

get_birth(P, B):-
	P = p(_N, _Ns, BR, _DR),
	B in BR.
set_birth(P, B, NP):-
	P = p(N, Ns, _, DR),
	fd_dom(B, BR),
	NP = p(N, Ns, BR, DR).

get_death(P, D):-
	P = p(_N, _Ns, _BR, DR),
	D in DR.

set_death(P, D, NP):-
	P = p(N, Ns, BR, _),
	fd_dom(D, DR),
	NP = p(N, Ns, BR, DR).

check_pessoa(N):-
	pessoa_com_nome(N, P),
	get_birth(P, B),
	get_death(P, D),
	D - B #< 110,
	B #=< D,
	set_birth(P, B, P1),
	set_death(P1, D, NP),
	retract(pessoa(P)),
	assert(pessoa(NP)).

check_irmao(N1, N2):-
	irmao(N1, N2), 
	pessoa_com_nome(N1, P1),
	pessoa_com_nome(N2, P2),
	get_birth(P1, B1),
	get_birth(P2, B2),
	check_brother_constraint(N1, B1, N2, B2),
	set_birth(P1, B1, NP1),
	set_birth(P2, B2, NP2),
	retract(pessoa(P1)),
	retract(pessoa(P2)),
	assert(pessoa(NP1)),
	assert(pessoa(NP2)).

check_brother_constraint(N1, B1, N2, B2):-
	abs(B2 - B1) #< 30, !;
	write('Constraint violated: irmao('), write(N1), write(', '), write(N2), writeln(')'), fail.
