%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  This is a rough approximation to the algorithm presented in:
%
%	"An Algorithm for NAND Decomposition Under Network Constraints,"
%	IEEE Trans. Comp., vol C-18, no. 12, Dec. 1969, p. 1098
%	by E. S. Davidson.
%
%  Written by Bruce Holmer
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  I have used the paper's terminology for names used in the program.
%
%  The data structure for representing functions and variables is
%		function(FunctionNumber, TrueSet, FalseSet,
%			ConceivableInputs,
%			ImmediatePredecessors, ImmediateSuccessors,
%			Predecessors, Successors)
%
%
%  Common names used in the program:
%
%	NumVars		number of variables (signal inputs)
%	NumGs		current number of variables and functions
%	Gs		list of variable and function data
%	Gi,Gj,Gk,Gl	individual variable or function--letter corresponds to
%			the subscript in the paper (most of the time)
%	Vector,V	vector from a function's true set
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:- mode(init_state(+,-,-,-)).

top:-main(0).

main(N) :-
	init_state(N, NumVars, NumGs, Gs),
        add_necessary_functions(NumVars, NumGs, Gs, NumGs2, Gs2),
	test_bounds(NumVars, NumGs2, Gs2),
	search(NumVars, NumGs2, Gs2).
main(_).
%	write('Search completed'), nl.

%  Test input
%  init_state(circuit(NumInputs, NumOutputs, FunctionList))
init_state(0, 2, 3, [		% 2 input xor
		function(2, [1,2], [0,3], [], [], [], [], []),
		function(1, [2,3], [0,1], [], [], [], [], []),
		function(0, [1,3], [0,2], [], [], [], [], [])
		]) :-
	update_bounds(_, 100, _).
init_state(1, 3, 4, [		% carry circuit
		function(3, [3,5,6,7], [0,1,2,4], [], [], [], [], []),
		function(2, [4,5,6,7], [0,1,2,3], [], [], [], [], []),
		function(1, [2,3,6,7], [0,1,4,5], [], [], [], [], []),
		function(0, [1,3,5,7], [0,2,4,6], [], [], [], [], [])
		]) :-
	update_bounds(_, 100, _).
init_state(2, 3, 4, [		% example in paper
		function(3, [1,2,4,6,7], [0,3,5], [], [], [], [], []),
		function(2, [4,5,6,7], [0,1,2,3], [], [], [], [], []),
		function(1, [2,3,6,7], [0,1,4,5], [], [], [], [], []),
		function(0, [1,3,5,7], [0,2,4,6], [], [], [], [], [])
		]) :-
	update_bounds(_, 100, _).
init_state(3, 3, 4, [		% sum (3 input xor)
		function(3, [1,2,4,7], [0,3,5,6], [], [], [], [], []),
		function(2, [4,5,6,7], [0,1,2,3], [], [], [], [], []),
		function(1, [2,3,6,7], [0,1,4,5], [], [], [], [], []),
		function(0, [1,3,5,7], [0,2,4,6], [], [], [], [], [])
		]) :-
	update_bounds(_, 100, _).
init_state(4, 3, 5, [		% do sum and carry together
		function(4, [3,5,6,7], [0,1,2,4], [], [], [], [], []),
		function(3, [1,2,4,7], [0,3,5,6], [], [], [], [], []),
		function(2, [4,5,6,7], [0,1,2,3], [], [], [], [], []),
		function(1, [2,3,6,7], [0,1,4,5], [], [], [], [], []),
		function(0, [1,3,5,7], [0,2,4,6], [], [], [], [], [])
		]) :-
	update_bounds(_, 100, _).
init_state(5, 5, 8, [		% 2 bit full adder
		function(7,		% A2 (output)
			[1,3,4,6,9,11,12,14,16,18,21,23,24,26,29,31],
			[0,2,5,7,8,10,13,15,17,19,20,22,25,27,28,30],
			[], [], [], [], []),
		function(6,		% B2 (output)
			[2,3,5,6,8,9,12,15,17,18,20,21,24,27,30,31],
			[0,1,4,7,10,11,13,14,16,19,22,23,25,26,28,29],
			[], [], [], [], []),
		function(5,		% carry-out (output)
			[7,10,11,13,14,15,19,22,23,25,26,27,28,29,30,31],
			[0,1,2,3,4,5,6,8,9,12,16,17,18,20,21,24],
			[], [], [], [], []),
		function(4,		% carry-in
			[16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],
			[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],
			[], [], [], [], []),
		function(3,		% B1 input
			[8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31],
			[0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23],
			[], [], [], [], []),
		function(2,		% B0 input
			[4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31],
			[0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27],
			[], [], [], [], []),
		function(1,		% A1 input
			[2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31],
			[0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29],
			[], [], [], [], []),
		function(0,		% A0 input
			[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31],
			[0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30],
			[], [], [], [], [])
		]) :-
	update_bounds(_, 21, _).


%  Iterate over all the TRUE vectors that need to be covered.
%  If no vectors remain to be covered (select_vector fails), then
%  the circuit is complete (printout results, update bounds, and
%  continue search for a lower cost circuit).
search(NumVars, NumGsIn, GsIn) :-
	select_vector(NumVars, NumGsIn, GsIn, Gj, Vector), !,
	cover_vector(NumVars, NumGsIn, GsIn, Gj, Vector, NumGs, Gs),
	add_necessary_functions(NumVars, NumGs, Gs, NumGsOut, GsOut),
	test_bounds(NumVars, NumGsOut, GsOut),
	search(NumVars, NumGsOut, GsOut).
search(NumVars, NumGs, Gs) :-
%	output_results(NumVars, NumGs, Gs),
	update_bounds(NumVars, NumGs, Gs),
	fail.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  Given the current solution, pick the best uncovered TRUE vector
%  for covering next.
%  The selected vector is specified by its vector number and function.
%  Select_vector fails if all TRUE vectors are covered.
%  Select_vector is determinant (gives only one solution).
select_vector(NumVars, NumGs, Gs, Gj, Vector) :-
	select_vector(Gs, NumVars, NumGs, Gs,
		dummy, 0, nf, 999, Gj, Vector, Type, _), !,
	\+ Type = cov,
	\+ Type = nf.

% loop over functions
select_vector([Gk|_], NumVars, _, _, Gj, V, Type, N, Gj, V, Type, N) :-
	function_number(Gk, K),
	K < NumVars.
select_vector([Gk|Gks], NumVars, NumGs, Gs,
		GjIn, Vin, TypeIn, Nin, GjOut, Vout, TypeOut, Nout) :-
	function_number(Gk, K),
	K >= NumVars,
	true_set(Gk, Tk),
	select_vector(Tk, Gk, NumVars, NumGs, Gs,
		GjIn, Vin, TypeIn, Nin, Gj, V, Type, N),
	select_vector(Gks, NumVars, NumGs, Gs,
		Gj, V, Type, N, GjOut, Vout, TypeOut, Nout).

% loop over vectors
select_vector([], _, _, _, _, Gj, V, Type, N, Gj, V, Type, N).
select_vector([V|Vs], Gk, NumVars, NumGs, Gs,
		GjIn, Vin, TypeIn, Nin, GjOut, Vout, TypeOut, Nout) :-
	vector_cover_type(NumVars, Gs, Gk, V, Type, N),
	best_vector(GjIn, Vin, TypeIn, Nin,
			Gk, V, Type, N,
			Gj2, V2, Type2, N2),
	select_vector(Vs, Gk, NumVars, NumGs, Gs,
		Gj2, V2, Type2, N2, GjOut, Vout, TypeOut, Nout).

vector_cover_type(NumVars, Gs, Gj, Vector, Type, NumCovers) :-
	immediate_predecessors(Gj, IPs),
	conceivable_inputs(Gj, CIs),
	false_set(Gj, Fj),
	cover_type1(IPs, Gs, Vector, nf, 0, T, N),
	cover_type2(CIs, Gs, NumVars, Fj, Vector, T, N, Type, NumCovers).

cover_type1([], _, _, T, N, T, N).
cover_type1([I|IPs], Gs, V, TypeIn, Nin, TypeOut, Nout) :-
	function(I, Gs, Gi),
	true_set(Gi, Ti),
	\+ set_member(V, Ti), !,
	false_set(Gi, Fi),
	(set_member(V, Fi) ->
		max_type(TypeIn, cov, Type);
		max_type(TypeIn, exp, Type)),
	N is Nin + 1,
	cover_type1(IPs, Gs, V, Type, N, TypeOut, Nout).
cover_type1([_|IPs], Gs, V, TypeIn, Nin, TypeOut, Nout) :-
	cover_type1(IPs, Gs, V, TypeIn, Nin, TypeOut, Nout).

cover_type2([], _, _, _, _, T, N, T, N).
cover_type2([I|CIs], Gs, NumVars, Fj, V, TypeIn, Nin, TypeOut, Nout) :-
	I < NumVars,
	function(I, Gs, Gi),
	false_set(Gi, Fi),
	set_member(V, Fi), !,
	max_type(TypeIn, var, Type),
	N is Nin + 1,
	cover_type2(CIs, Gs, NumVars, Fj, V, Type, N, TypeOut, Nout).
cover_type2([I|CIs], Gs, NumVars, Fj, V, TypeIn, Nin, TypeOut, Nout) :-
	I >= NumVars,
	function(I, Gs, Gi),
	true_set(Gi, Ti),
	\+ set_member(V, Ti), !,
	false_set(Gi, Fi),
	(set_member(V, Fi) ->
		(set_subset(Fj, Ti) ->
			max_type(TypeIn, fcn, Type);
			max_type(TypeIn, mcf, Type));
		(set_subset(Fj, Ti) ->
			max_type(TypeIn, exf, Type);
			max_type(TypeIn, exmcf, Type))),
	N is Nin + 1,
	cover_type2(CIs, Gs, NumVars, Fj, V, Type, N, TypeOut, Nout).
cover_type2([_|CIs], Gs, NumVars, Fj, V, TypeIn, Nin, TypeOut, Nout) :-
	cover_type2(CIs, Gs, NumVars, Fj, V, TypeIn, Nin, TypeOut, Nout).

%  The best vector to cover is the one with worst type, or, if types
%  are equal, with the least number of possible covers.
best_vector(dummy, _, _, _, Gj2, V2, Type2, N2, Gj2, V2, Type2, N2) :- !.
best_vector(Gj1, V1, Type1, N1, dummy, _, _, _, Gj1, V1, Type1, N1) :- !.
best_vector(Gj1, V1, Type, N1, Gj2, _, Type, N2, Gj1, V1, Type, N1) :-
	function_number(Gj1, J), function_number(Gj2, J),
	N1 < N2, !.
best_vector(Gj1, _, Type, N1, Gj2, V2, Type, N2, Gj2, V2, Type, N2) :-
	function_number(Gj1, J), function_number(Gj2, J),
	N1 >= N2, !.
best_vector(Gj1, V1, Type, N1, Gj2, _, Type, _, Gj1, V1, Type, N1) :-
	(Type = exp ; Type = var),
	function_number(Gj1, J1), function_number(Gj2, J2),
	J1 > J2, !.
best_vector(Gj1, _, Type, _, Gj2, V2, Type, N2, Gj2, V2, Type, N2) :-
	(Type = exp ; Type = var),
	function_number(Gj1, J1), function_number(Gj2, J2),
	J1 < J2, !.
best_vector(Gj1, V1, Type, N1, Gj2, _, Type, _, Gj1, V1, Type, N1) :-
	\+ (Type = exp ; Type = var),
	function_number(Gj1, J1), function_number(Gj2, J2),
	J1 < J2, !.
best_vector(Gj1, _, Type, _, Gj2, V2, Type, N2, Gj2, V2, Type, N2) :-
	\+ (Type = exp ; Type = var),
	function_number(Gj1, J1), function_number(Gj2, J2),
	J1 > J2, !.
best_vector(Gj1, V1, Type1, N1, _, _, Type2, _, Gj1, V1, Type1, N1) :-
	type_order(Type2, Type1), !.
best_vector(_, _, Type1, _, Gj2, V2, Type2, N2, Gj2, V2, Type2, N2) :-
	type_order(Type1, Type2), !.

max_type(T1, T2, T1) :- type_order(T1, T2), !.
max_type(T1, T2, T2) :- \+ type_order(T1, T2), !.

%  Order of types

type_order(cov, exp).
type_order(cov, var).
type_order(cov, fcn).
type_order(cov, mcf).
type_order(cov, exf).
type_order(cov, exmcf).
type_order(cov, nf).
type_order(exp, var).
type_order(exp, fcn).
type_order(exp, mcf).
type_order(exp, exf).
type_order(exp, exmcf).
type_order(exp, nf).
type_order(var, fcn).
type_order(var, mcf).
type_order(var, exf).
type_order(var, exmcf).
type_order(var, nf).
type_order(fcn, mcf).
type_order(fcn, exf).
type_order(fcn, exmcf).
type_order(fcn, nf).
type_order(mcf, exf).
type_order(mcf, exmcf).
type_order(mcf, nf).
type_order(exf, exmcf).
type_order(exf, nf).
type_order(exmcf, nf).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%  Cover_vector will cover the specified vector and
%  generate new circuit information.
%  Using backtracking, all possible coverings are generated.
%  The ordering of the possible coverings is approximately that
%  given in Davidson's paper, but has been simplified.

cover_vector(NumVars, NumGsIn, GsIn, Gj, Vector, NumGsOut, GsOut) :-
	immediate_predecessors(Gj, IPs),
	conceivable_inputs(Gj, CIs),
	vector_types(Type),
	cover_vector(Type, IPs, CIs, Gj, Vector, NumVars, NumGsIn, GsIn,
		NumGsOut, GsOut).

vector_types(var).
vector_types(exp).
vector_types(fcn).
vector_types(mcf).
vector_types(exf).
vector_types(exmcf).
vector_types(nf).

cover_vector(exp, [I|_], _, Gj, V, _, NumGs, GsIn, NumGs, GsOut) :-
	function(I, GsIn, Gi),
	true_set(Gi, Ti),
	\+ set_member(V, Ti),
	update_circuit(GsIn, Gi, Gj, V, GsIn, GsOut).
cover_vector(exp, [_|IPs], _, Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	cover_vector(exp, IPs, _, Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut).
cover_vector(var, _, [I|_], Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	I < NumVars,
	function(I, GsIn, Gi),
	false_set(Gi, Fi),
	set_member(V, Fi),
	update_circuit(GsIn, Gi, Gj, V, GsIn, GsOut).
cover_vector(var, _, [_|CIs], Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	cover_vector(var, _, CIs, Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut).
cover_vector(fcn, _, [I|_], Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	I >= NumVars,
	function(I, GsIn, Gi),
	false_set(Gi, Fi),
	set_member(V, Fi),
	true_set(Gi, Ti),
	false_set(Gj, Fj),
	set_subset(Fj, Ti),
	update_circuit(GsIn, Gi, Gj, V, GsIn, GsOut).
cover_vector(fcn, _, [_|CIs], Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	cover_vector(fcn, _, CIs, Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut).
cover_vector(mcf, _, [I|_], Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	I >= NumVars,
	function(I, GsIn, Gi),
	false_set(Gi, Fi),
	set_member(V, Fi),
	true_set(Gi, Ti),
	false_set(Gj, Fj),
	\+ set_subset(Fj, Ti),
	update_circuit(GsIn, Gi, Gj, V, GsIn, GsOut).
cover_vector(mcf, _, [_|CIs], Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	cover_vector(mcf, _, CIs, Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut).
cover_vector(exf, _, [I|_], Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	I >= NumVars,
	function(I, GsIn, Gi),
	false_set(Gi, Fi),
	\+ set_member(V, Fi),
	true_set(Gi, Ti),
	\+ set_member(V, Ti),
	false_set(Gj, Fj),
	set_subset(Fj, Ti),
	update_circuit(GsIn, Gi, Gj, V, GsIn, GsOut).
cover_vector(exf, _, [_|CIs], Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	cover_vector(exf, _, CIs, Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut).
cover_vector(exmcf, _, [I|_], Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	I >= NumVars,
	function(I, GsIn, Gi),
	false_set(Gi, Fi),
	\+ set_member(V, Fi),
	true_set(Gi, Ti),
	\+ set_member(V, Ti),
	false_set(Gj, Fj),
	\+ set_subset(Fj, Ti),
	update_circuit(GsIn, Gi, Gj, V, GsIn, GsOut).
cover_vector(exmcf, _, [_|CIs], Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut) :-
	cover_vector(exmcf, _, CIs, Gj, V, NumVars, NumGs, GsIn, NumGs, GsOut).
cover_vector(nf, _, _, Gj, V, NumVars, NumGsIn, GsIn, NumGsOut, GsOut) :-
	NumGsOut is NumGsIn + 1,
	false_set(Gj, Fj),
	new_function_CIs(GsIn,
		function(NumGsIn,Fj,[V],[],[],[],[],[]),
		NumVars, Gs, Gi),
	update_circuit(Gs, Gi, Gj, V, Gs, GsOut).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

update_circuit([], _, _, _, _, []).
update_circuit([function(K,Tk,Fk,CIk,IPk,ISk,Pk,Sk)|GsIn],
		Gi, Gj, V, Gs,
		[function(K,Tko,Fko,CIko,IPko,ISko,Pko,Sko)|GsOut]) :-
	Gi = function(I,_,Fi,_,IPi,ISi,Pi,_),
	Gj = function(J,_,Fj,_,_,_,_,Sj),
	set_union([I], Pi, PiI),
	set_union([J], Sj, SjJ),
	(K = J ->
		set_union(Tk, Fi, Tk2);
		Tk2 = Tk),
	(K = I ->
		set_union(Tk2, Fj, Tk3);
		Tk3 = Tk2),
	((set_member(K, IPi); set_member(K, ISi)) ->
		set_union(Tk3, [V], Tko);
		Tko = Tk3),
	(K = I ->
		set_union(Fk, [V], Fko);
		Fko = Fk),
	((set_member(K, Pi); K = I) ->
		set_difference(CIk, SjJ, CIk2);
		CIk2 = CIk),
	((set_member(I, CIk), set_member(V, Fk)) ->
		set_difference(CIk2, [I], CIk3);
		CIk3 = CIk2),
	(K = I ->
		exclude_if_vector_in_false_set(CIk3, Gs, V, CIk4);
		CIk4 = CIk3),
	(K = J ->
		set_difference(CIk4, [I], CIko);
		CIko = CIk4),
	(K = J ->
		set_union(IPk, [I], IPko);
		IPko = IPk),
	(K = I ->
		set_union(ISk, [J], ISko);
		ISko = ISk),
	(set_member(K, SjJ) ->
		set_union(Pk, PiI, Pko);
		Pko = Pk),
	(set_member(K, PiI) ->
		set_union(Sk, SjJ, Sko);
		Sko = Sk),
	update_circuit(GsIn, Gi, Gj, V, Gs, GsOut).

exclude_if_vector_in_false_set([], _, _, []).
exclude_if_vector_in_false_set([K|CIsIn], Gs, V, CIsOut) :-
	function(K, Gs, Gk),
	false_set(Gk, Fk),
	set_member(V, Fk), !,
	exclude_if_vector_in_false_set(CIsIn, Gs, V, CIsOut).
exclude_if_vector_in_false_set([K|CIsIn], Gs, V, [K|CIsOut]) :-
	exclude_if_vector_in_false_set(CIsIn, Gs, V, CIsOut).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

add_necessary_functions(NumVars, NumGsIn, GsIn, NumGsOut, GsOut) :-
	add_necessary_functions(NumVars, NumVars, NumGsIn, GsIn,
					NumGsOut, GsOut).

add_necessary_functions(NumGs, _, NumGs, Gs, NumGs, Gs) :- !.
add_necessary_functions(K, NumVars, NumGsIn, GsIn, NumGsOut, GsOut) :-
	function(K, GsIn, Gk),
	function_type(NumVars, NumGsIn, GsIn, Gk, nf, V), !,
	false_set(Gk, Fk),
	new_function_CIs(GsIn,
		function(NumGsIn,Fk,[V],[],[],[],[],[]),
		NumVars, Gs, Gl),
	function(K, Gs, Gk1),
	update_circuit(Gs, Gl, Gk1, V, Gs, Gs1),
	NumGs1 is NumGsIn + 1,
	K1 is K + 1,
	add_necessary_functions(K1, NumVars, NumGs1, Gs1, NumGsOut, GsOut).
add_necessary_functions(K, NumVars, NumGsIn, GsIn, NumGsOut, GsOut) :-
	K1 is K + 1,
	add_necessary_functions(K1, NumVars, NumGsIn, GsIn, NumGsOut, GsOut).

new_function_CIs(GsIn, function(L,Tl,Fl,_,IPl,ISl,Pl,Sl), NumVars,
		[GlOut|GsOut], GlOut) :-
	new_function_CIs(GsIn, L, Fl, NumVars, GsOut, [], CIlo),
	GlOut = function(L,Tl,Fl,CIlo,IPl,ISl,Pl,Sl).

new_function_CIs([], _, _, _, [], CIl, CIl).
new_function_CIs([function(K,Tk,Fk,CIk,IPk,ISk,Pk,Sk)|GsIn], L, Fl, NumVars,
		[function(K,Tk,Fk,CIko,IPk,ISk,Pk,Sk)|GsOut], CIlIn, CIlOut) :-
	set_intersection(Fl, Fk, []), !,
	(K >= NumVars ->
		set_union(CIk, [L], CIko);
		CIko = CIk),
	new_function_CIs(GsIn, L, Fl, NumVars, GsOut, [K|CIlIn], CIlOut).
new_function_CIs([Gk|GsIn], L, Fl, NumVars, [Gk|GsOut], CIlIn, CIlOut) :-
	new_function_CIs(GsIn, L, Fl, NumVars, GsOut, CIlIn, CIlOut).

function_type(NumVars, NumGs, Gs, Gk, Type, Vector) :-
	true_set(Gk, Tk),
	select_vector(Tk, Gk, NumVars, NumGs, Gs,
		dummy, 0, nf, 999, _, Vector, Type, _).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  Cost and constraint predicates:

% very simple bound for now

test_bounds(_, NumGs, _) :-
	access(bound, Bound),
	NumGs < Bound.

update_bounds(_, NumGs, _) :-
	set(bound, NumGs).

% set and access for systems that don't support them
:- dynamic
	state_/2.
set(N, A) :-
	(retract(state_(N, _)) -> true ; true),
	asserta(state_(N, A)).

access(N, A) :-
	state_(N, A).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  Output predicates:

%  for now just dump everything

%output_results(NumVars, NumGs, Gs) :-
%	NumGates is NumGs - NumVars,
%	write(NumGates), write(' gates'), nl,
%	write_gates(Gs), nl,
%	write('searching for a better solution...'), nl, nl.

write_gates([]).
write_gates([Gi|Gs]) :-
	function_number(Gi, I),
	write('gate #'), write(I), write(' inputs:   '),
	immediate_predecessors(Gi, IPi),
	write(IPi), nl,
	write_gates(Gs).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%  Retrieve the specified function from the function list.
%  function(FunctionNumber, FunctionList, Function).
function(I, [Gi|_], Gi) :- function_number(Gi, I), !.
function(I, [_|Gs], Gi) :- function(I, Gs, Gi).

function_number(        function(I,_,_,_,_,_,_,_), I).
true_set(               function(_,T,_,_,_,_,_,_), T).
false_set(              function(_,_,F,_,_,_,_,_), F).
conceivable_inputs(     function(_,_,_,CI,_,_,_,_), CI).
immediate_predecessors( function(_,_,_,_,IP,_,_,_), IP).
immediate_successors(   function(_,_,_,_,_,IS,_,_), IS).
predecessors(           function(_,_,_,_,_,_,P,_), P).
successors(             function(_,_,_,_,_,_,_,S), S).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  Set operations assume that the sets are represented by an ordered list
%  of integers.

set_union([],     [],     []).
set_union([],     [X|L2], [X|L2]).
set_union([X|L1], [],     [X|L1]).
set_union([X|L1], [X|L2], [X|L3]) :-        set_union(L1, L2,     L3).
set_union([X|L1], [Y|L2], [X|L3]) :- X < Y, set_union(L1, [Y|L2], L3).
set_union([X|L1], [Y|L2], [Y|L3]) :- X > Y, set_union([X|L1], L2, L3).

set_intersection([],     [],     []).
set_intersection([],     [_|_],  []).
set_intersection([_|_],  [],     []).
set_intersection([X|L1], [X|L2], [X|L3]) :-    set_intersection(L1, L2,     L3).
set_intersection([X|L1], [Y|L2], L3) :- X < Y, set_intersection(L1, [Y|L2], L3).
set_intersection([X|L1], [Y|L2], L3) :- X > Y, set_intersection([X|L1], L2, L3).

set_difference([],     [],     []).
set_difference([],     [_|_],  []).
set_difference([X|L1], [],     [X|L1]).
set_difference([X|L1], [X|L2], L3) :-            set_difference(L1, L2,     L3).
set_difference([X|L1], [Y|L2], [X|L3]) :- X < Y, set_difference(L1, [Y|L2], L3).
set_difference([X|L1], [Y|L2], L3) :-     X > Y, set_difference([X|L1], L2, L3).

set_subset([],     _).
set_subset([X|L1], [X|L2]) :-        set_subset(L1, L2).
set_subset([X|L1], [Y|L2]) :- X > Y, set_subset([X|L1], L2).

set_member(X, [X|_]).
set_member(X, [Y|T]) :- X > Y, set_member(X, T).
