% Generatore di problemi random.
% Dati N, D, P e Q genera un problema.
% NOTA: Distrugge anche il problema eventualmente presente.

:- dynamic(constraint/2).
:- dynamic(consistent/4).
:- dynamic(function/1).
:- dynamic(repeatable/0).
:- dynamic(weight/1).

rep(X) :- var(X),!, (repeatable -> X=true ; X=false).
rep(true) :- assert(repeatable).
rep(false) :- retract_all(repeatable).

% Questo per ora genera 2 funzioni, andrebbe modificato (modificando in qualche modo
% l'interfaccia) se si vuole un caso + generale
generate(N,D,P,Q) :-
	
	(repeatable -> seed(3), write(rep)
		; true),
	writeln(gen),
	destroy_problem,
	unary_constraints(N,D),
	build_constraint_matrix(N,P),
%	save_constraint_matrix,
	build_data_matrix(N,D,Q), 
	generate_functions(N,2),!,
% questo cut e' solo per motivi di efficienza, non cambia la semantica
%	save_data_matrix.
	(repeatable -> save_problem
		; true).

build_index_list(N,N,[N]) :- !.
build_index_list(N1,N2,[N1|T]) :-
	Ntemp is N1+1,
	build_index_list(Ntemp,N2,T).

build_constraint_matrix(N,P) :-
	N1 is N-1, build_index_list(1,N1,Li),
%	create_const_matrix(N),
	put_random(N,P,Li).

%create_const_matrix(N) :-
	% make_local_array(const(N,N),byte).

put_random(_,_,[]):-!.
put_random(N,P,[I|T]) :-
	I1 is I+1,
	build_index_list(I1,N,L),
	put_random_line(I,P,L),
	put_random(N,P,T).

put_random_line(_,_,[]) :-!.
put_random_line(I,P,[J|T]) :-
	frandom(X),
	(X<P/100 ->	% setval(const(I,J),1), setval(const(J,I),1),
	assert(constraint(I,J))
	;	% setval(const(I,J),0), setval(const(J,I),0)
	true),
	put_random_line(I,P,T).

build_data_matrix(N,D,Q) :-
	findall([I,J], constraint(I,J), L),
	insert_constraints(L,N,D,Q).

insert_constraints([],_,_,_) :-!.
insert_constraints([[I,J]|T],N,D,Q) :-
	D1 is D-2,
	build_index_list(0,D1,Li),
	for_x(D,Q,Li,I,J),
	insert_constraints(T,N,D,Q).

for_x(_,_,[],_,_) :- !.
for_x(D,Q,[X|T],I,J) :-
	I1 is X+1,
%	assert(consistent(I,J,X,[X])),
	D1 is D-1,
	build_index_list(I1,D1,Ly),
	for_y(D,Q,Ly,I,J,X),
	for_x(D,Q,T,I,J).

for_y(_,_,[],_,_,_) :- !.
for_y(D,Q,[Y|T],I,J,X) :-
	frandom(Rand),
	(Rand<Q/100 ->
		add_element(I,J,X,Y),
	  	add_element(J,I,Y,X)
		; true),
	for_y(D,Q,T,I,J,X).

add_element(I,J,X,Y) :-
	( consistent(I,J,X,L)
	   ->	(memberchk(Y,L) -> true
	   	   ;	L1 = [Y|L],
	   		retract(consistent(I,J,X,L)),
	   		assert(consistent(I,J,X,L1)))
	   ;	assert(consistent(I,J,X,[Y]))),!.

% Mette i vincoli unari (ogni elemento e' consistente con se stesso)
unary_constraints(N,D) :-
	build_index_list(1,N,Ln),
	for_n(Ln,D).

for_n([],_) :- !.
for_n([X|T],D) :-
	D1 is D-1,
	build_index_list(0,D1,Ld),
	for_d(Ld,X), for_n(T,D).

for_d([],_) :- !.
for_d([D|T],X) :-
	assert(consistent(X,X,D,[D])), for_d(T,X).

destroy_problem :- 
	retract_all(constraint(_,_)), 
	retract_all(consistent(_,_,_,_)),
	retract_all(function(_)).

%%%%%%%%%%%%%%%%% Generazione delle funzioni obiettivo %%%%%%%%%%%%%%%%%%%%%%%%
% Genero una lista di pesi casuale e considero la funzione lineare associata.
%generate_weights(N,NF) :-
%	(for(I,1,NF), foreach( do
%		gen_weights(N,I,

% INIZIO GENERAZIONE FUNZIONI OBIETTIVO OBSOLETA %%%%%%%%%%%%%%%%%%%
% Genera una funzione di N variabili che e` una somma di coeffic1enti +1, 0, -1
% delle N variabili
% Vabbe`, per ora (semplice) non genera lo zero: solo +1 o -1
generate_function(N) :-
	gen_fun(N,F,_),
	assert(function(F)).

% Genera la funzione F di N variabili; il 3 parametro restituisce anche N
% nel formato s(s(s(0)))
gen_fun(N,F,C) :-
	N1 is N-1, gen_fun1(N1,F,C).
gen_fun1(0,0,0):- !.
gen_fun1(N,F,s(C)) :-
	frandom(R),
	N1 is N-1,
	gen_fun1(N1,F1,C),
	( R<0.5
	  -> F = F1 + s(C)
	  ;  F = F1 - s(C) ).

% Genera NF funzioni di N variabili e le asserisce.
generate_functions(_,0).
generate_functions(N,NF) :-
	generate_function(N),
	NF1 is NF-1,
	generate_functions(N,NF1).

get_functions(FList) :-
	findall(F,function(F),FList).

save_problem :-
	open("problema.pl",write,file),
	findall(constraint(A,B),constraint(A,B),L),
	save_list(L,file),
	findall(consistent(A,B,C,D),consistent(A,B,C,D),L1),
	save_list(L1,file),
	findall(function(X),function(X),L2),
	save_list(L2,file),
	close(file).

save_list([],_).
save_list([H|T],F) :-
	writeln(F,H),
	save_list(T,F).

