%
% mapping IX's ontology & object instances to
% KRAFT's schema & objects
%
:- use_module('util/cif_util').
:- use_module('util/print_cif').
:- use_module(library(lists)).

%
% to store created KRAFT objects & attributes
%
:- dynamic(object/2).		%will hold KRAFT object instances
:- dynamic(fnval/5).		%will hold KRAFT object attributes (instances)
:- dynamic(pfdm_schema/1).	%will hold P/FDM schema definition translated from IX
:- dynamic(pfdm_constraint/1).	%will hold P/FDM constraint instances
:- dynamic(depend/2).		%dependence information between class declaration
:- dynamic(counter/2).
:- prolog_flag(unknown,_,fail).	%cause undefined predicate to fail instead of raising exception

:- [ix].		%load IX's classes & instances


top:-
	clean_up,
	validate,		%validate IX ontology & fill in missing pieces (if needed)
	map_schema,		%map IX ontology (instance) into KRAFT schema
	map_instances,		%map IX object instances & attributes into KRAFT's
	determine_dependence,	%determine dependence between classes (for printing P/FDM schema)
	format("To dump P/FDM schema to screen, call the goal dump_pfdm_schema/0.~n",[]),
	format("To print P/FDM-friendly schema to screen, call the goal print_pfdm_schema/0.~n",[]),
	format("To generate a P/FDM-parsable Daplex schema, call the goal generate_pfdm_schema/1~n",[]),
	format("   with the name of the P/FDM module.~n",[]).

clean_up :-
	retractall(object(_,_)),
	retractall(fnval(_,_,_,_,_)),
	retractall(depend(_,_)),
	retractall(pfdm_constraint(_)),
	retractall(pfdm_schema(_)),
	retractall(counter(_,_)).

%
% validating IX ontology instance
%
% 1)	Check if all subclasses & superclasses in is_a/2 have a
%	corresponding class/1. If not, add a class/1 for it.
% 2)	Check all property/3.
%	Make sure that all classes mentioned in property/3 appear in a corresponding class/1.
% 3)	Check all instance_of/2.
%	Make sure that all classes mentioned in instance_of/2 appear in a class/1.
% 4)	Check all instance_att/3.
%	Make sure that all attributes are already specified in corresponding property/3
%	and instances are specified in instance_of/2.
%

%
% validate what IX told me about classes, properties & instances
%
validate :-
	fill_missing_classes,		%fill in missing class/1 from is_a/2 information
	validate_properties,		%validate all property/3
	validate_instances,		%validate all instance_of/2
	validate_instances_attributes.

%
% fill in missing class/1 implied by is_a/2 relationship
%
fill_missing_classes :-
	findall((Sub,Super),ix:is_a(Sub,Super),All),	%find all subclasses & superclasses in is_a/2
	fill_missing_classes(All).

fill_missing_classes([]).
fill_missing_classes([(C1,C2)|Cs]) :-
	(	ix:class(C1);			%either a class/1 already exist
		assertz(ix:class(C1))		%or assert one
	),
	!,
	(	ix:class(C2);
		assertz(ix:class(C2))
	),
	!,
	fill_missing_classes(Cs).


%
% check if all properties are attached to valid classes
%
validate_properties :-
	ix:property(Class,Property,_),						%take a property/3
	not(ix:class(Class)),							%if class/1 doesn't exist
	!,
	format("class [~a] of property [~a] not found~n",[Class,Property]),	%print error message
	fail.									%fail it
validate_properties.					%if all property/3 has a corresponding class/1, we'll reach here & succeed

%
% check if all instances are of valid classes
%
validate_instances :-
	ix:instance_of(Instance,Class),						%take a instance_of/2
	not(ix:class(Class)),							%if class/1 doesn't exist
	!,
	format("class [~a] of instance [~a] not found~n",[Class,Instance]),	%print error message
	fail.
validate_instances.					%if all instance_of/2 has a corresponding class/1, we'll reach here & succeed

%
% check is all instance attributes are of valid classes
%
validate_instances_attributes :-
	ix:instance_att(Instance,Property,_),						%take a instance_att/3
	(	not(ix:instance_of(Instance,Class));					%if either a instance_of/2 cannot be found
		not(ix:property(Class,Property,_))					%or a class/1 is missing
	),
	!,
	format("property [~a] of instance [~a] not found~n",[Property,Instance]),	%print error message
	fail.
validate_instances_attributes.		%if all instance_att/3 has a corresponding property/3, we'll reach here & succeed






%
% map IX ontology instance specification into KRAFT (P/FDM) schema spec
%

map_schema:-
	map_entities,		%map IX classes into P/FDM entity classes
	map_properties,		%map IX properties into P/FDM attributes
	map_relationships.	%map IX relationship definition into P/FDM schema

%
% look into class/1 and map into P/FDM entity classes declaration
%
map_entities :-
	ix:class(Class),					%get an IX class
	not(ix:is_a(Class,_)),					%no superclass defined
	assertz(pfdm_schema(declare(Class,entity))),		%then its superclass is P/FDM "entity"
	ix:key_of(Class,Key),					%look for any key_of definition of this class
	assertz(pfdm_schema(key_of(Class,Key))),		%if exist, then add P/FDM key_of declaration
	fail.							%fail to loop

map_entities :-
	ix:class(Class),					%get an IX class
	ix:is_a(Class,Superclass),				%it has a superclass
	assertz(pfdm_schema(declare(Class,Superclass))),	%assert P/FDM declaration
	ix:key_of(Class,Key),					%look for any key_of definition
	assertz(pfdm_schema(key_of(Class,Key))),		%if exist, add P/FDM key_of declaration
	fail.							%fail to loop

map_entities.

%
% look into property/3 and map into P/FDM function declaration
%
map_properties :-
	ix:property(Class,Prop_name,Default_value),				%get an IX property
	map_properties(Default_value,Cardinality,Type),				%get its cardinality & guess its type
	assertz(pfdm_schema(declare(Prop_name,Class,Cardinality,Type))),	%add a P/FDM function declaration
	fail.									%fail to loop
map_properties.
	
map_properties([Value|_],'->>',Type) :-		%value is a list, so it must be multi-valued
	!,
	guess_type(Value,Type).			%guess its type from the 1st element in the list

map_properties(Value,'->',Type) :-		%function is single-valued
	guess_type(Value,Type).			%guest its type

	

%
% find IX instances and map them into KRAFT's object/2
%

%
% mapping IX object instances into KRAFT objects
% when we map an object instance, we also have to check for missing attribute value
% if an attribute value is not defined for an instance, we'll have to take the default value (as defined in property/3)
% if a class property spec is not found, look through every superclass
%
/*
map_instances :-
	ix:instance_of(Instance,Class),
	assertz(object(Class,Instance)),				%asserta a KRAFT object
	get_attribute_value(Class,Instance,Value),
	guess_type(Value,Guess_type),					%get the attribute's type from its value
	assertz(fnval(Property,[Class],[Instance],Guess_type,Value)),	%assert KRAFT-style attribute value spec
	fail.

get_attribute_value(Class,Instance,Value) :-
	ix:instance_att(Instance,Property,Values),
	is_list(Value),
	member(Value,Values),
	is_subclass(ix,Class,Superclass),
	ix:property(Superclass,Property,_),
*/

%
% see if a class is a direct/indirect subclass of another one
%
is_subclass(ix,Subclass,Superclass) :-
	ix:is_a(Subclass,Superclass).

is_subclass(ix,Subclass,Superclass) :-
	ix:is_a(Subclass,Temp_class),
	is_subclass(ix,Temp_class,Superclass).

map_instances :-
	ix:instance_of(Instance,Class),
	assertz(object(Class,Instance)),	%assert KRAFT object
	ix:property(Class,Property,Default_value),			%get a property name
	get_property_value(Instance,Property,Default_value,Value),	%try to get property's value
	guess_type(Value,Guess_type),					%get the attribute's type from its value
	assertz(fnval(Property,[Class],[Instance],Guess_type,Value)),	%assert KRAFT-style attribute value spec
	fail.
map_instances.

%
% get the property value (attribute) of an instance
% if no value is specified, use the class property value as the default
%
get_property_value(Instance,Property,_,Value) :-
	ix:instance_att(Instance,Property,Value),
	!.

get_property_value(_,_,Default_value,Default_value).




%
% Relationship is like other classes in JC-B's model.
% They could be described by is_a/2 & property/3, etc. with instances.
%
% Map IX relationships into KRAFT, involving
% 1)	map relationship schema (e.g. how many entities are involved)
% 2)	map relatioship instances
%
% NB:	Relationship instances should be specified by instance_of/2 as in other classes.
%	However, as JC-B likes to use the "Prolog style", I just have to check if something
%	is a relationship instance.
%
map_relationships :-
	is_subclass(ix,Rel_class,relationship),		%get a subclass of relationship
	ix:property(Rel_class,arity,Arity),		%look for arity of relationship
	functor(Term,Rel_class,Arity),			%compose term to for matching relatinship instances
	map_relationship_class_attributes(Rel_class),	%map its attribute definition in P/FDM schema (functions)
	call(ix:Term),					%look for term representing an instance of this relationship
	map_relationship_instance(Rel_class,Term),	%map instances
	fail.						%fail to loop
map_relationships.

%
% find all att_domain/3 facts of a relationship class and assert P/FDM definitions of attributes
%
map_relationship_class_attributes(Rel_class) :-
	ix:att_domain(Rel_class,Attribute,Domain),		%get an att_domain/3
	assertz(pfdm_schema(declare(Attribute,Rel_class,'->',Domain))),	%add a function defintion for this relationship class
	fail.
map_relationship_class_attributes(_).


%
% map relationship instances into KRAFT style facts
%
map_relationship_instance(Rel_class,Term) :-
	make_up_instance_id(Rel_class,Rel_instance),				%make up an object ID for this instance
	assertz(object(Rel_class,Rel_instance)),				%assert the object/2 fact of this relationship instance
	ix:property(Rel_class,arity,Arity),					%get default arity of this relationship (class!)
	assertz(fnval(arity,[Rel_class],[Rel_instance],integer,Arity)),		%assert a fnval/5 for the "arity" attribute
	Term =.. [Rel_class|Args],						%get the list of arguments
	initialize_counter(argument,1),						%initialize argument counter
	findall(X,ix:att_domain(Rel_class,X,_),Names),				%find list of attribute names
	map_relationship_instance_arguments(Rel_class,Rel_instance,Names,Args).	%map arguments into KRAFT's fnval/5 facts

%
% map each argument of a "relationship instance" into a fnval/5 fact
%
map_relationship_instance_arguments(_,_,[]).
map_relationship_instance_arguments(Rel_class,Rel_instance,[Name|Names],[Result_object|As]) :-
	ix:instance_of(Result_object,Result_type),				%get related object type
	ix:instance_of(Result_object,Result_type),				%find type/class of related object
	assertz(fnval(Name,[Rel_class],[Rel_instance],Result_type,Result_object)),
	map_relationship_instance_arguments(Rel_class,Rel_instance,Names,As).		%proceed w/ the rest





%
% given a class name, make up an object id
%
make_up_instance_id(Class,Id) :-
	generate_counter(Class,Class,Id).


%
% initialize counter value
%
/*
initialize_counter(Counter_name,Value) :-
	counter(Counter_name,_),
	!,
	retractall(counter(Counter_name,_)),
	assertz(counter(Counter_name,Value)).
initialize_counter(Counter_name,Value) :-
	assertz(counter(Counter_name,Value)).
*/

%
% look for the counter/2 fact of a specific "counter" & return its value
%
/*
generate_counter(Counter_name,Value) :-
	counter(Counter_name,Value),					%counter/2 found
	!,
	retract(counter(Counter_name,Value)),				%remove old counter/2 fact
	New_value is Value+1,					%increment it
	assertz(counter(Counter_name,New_value)).			%assert new counter/2
generate_counter(Counter_name,1) :-		%counter/2 not found, start from 1
	assertz(counter(Counter_name,2)).	%next value is 2

%
% get a counter's value, returning w/ a prefix
%
generate_counter(Counter_name,Prefix,Result) :-
	generate_counter(Counter_name,Value),
	!,
	name(Prefix,Prefix_string),				%convert class name into a string
	name(Value,Value_string),
	append(Prefix_string,"_",Temp),				%append "_" to class name
	append(Temp,Value_string,Result_string),		%append counter value
	name(Result,Result_string).				%construct atom from string
*/


%
% guess the type of a value
%
guess_type(Value,integer) :-
	integer(Value),
	!.
guess_type(Value,float) :-
	number(Value),
	!.
guess_type(true,boolean) :-
	!.
guess_type(false,boolean) :-
	!.
guess_type(Value,string) :-
	atom(Value),
	!.
guess_type(Value,Class) :-
	ix:instance_of(Value,Class),
	!.
guess_type(_,unknown).



%
% map IX constraints into KRAFT's
%

map_constraints :-
	static_constraint(Pre_con,Con),		%static constraint w/ pre-condition
	map_constraint(forall,Pre_con,Con,Mapped),	%it's universally quantified
	assertz(constraint(Mapped)),
	fail.					%fail to loop
map_constraints :-
	static_constraint(Con),			%a static constraint w/ no pre-cond
	map_constraint(exist,Con,Mapped),		%is existentially quantified
	assertz(constraint(Mapped)),
	fail.					%fail to loop
map_constraints :-
	dynamic_constraint(Pre_con,Con),	%dynamic constraint w/ pre-cond
	map_constraint(forall,Pre_con,Con,Mapped),	%universally quantified
	assertz(constraint(Mapped)),
	fail.					%fail to loop
map_constraints :-
	dynamic_constraint(Con),		%dynamic constraint w/ no pre-cond
	map_constraint(exist,Con,Mapped),		%existentially quantified
	assertz(constraint(Mapped)),
	fail.					%fail to loop
map_constraints.


%
% map quantified constraints
%
map_constraint(forall,If,Then,Mapped) :-
	expand(If,If_expanded),			%expand IF part into a conjunction of goals
	expand(Then,Then_expanded),		%expand THEN part
	map_to_xcif(If_expanded,If_mapped,[],Temp_map),	%map IF to XCIF
	map_to_xcif(Then_expanded,Then_mapped,Temp_map,_),	%map THEN to XCIF
	find_vars(If_mapped,Vars_1),
	find_vars(Then_mapped,Vars_2),
	my_union(Vars_1,Vars_2,All_vars),
	Mapped=restrict_subquery(all(All_vars),If_mapped,Then_mapped).

map_constraint(exist,Pred,Mapped) :-
	expand(Pred,Pred_expanded),
	map_to_xcif(Pred_expanded,Pred_mapped,[],_),	%map THEN to XCIF
	find_vars(Pred_mapped,All_vars),
	Mapped=restrict_subquery(exist(All_vars),Pred_mapped).

%
% expand a predicate (JCB's relationship) into its definition in an IX ontology
%
% when we expand a predicate, it may have conjunction of subgoals
% we'll convert them all into a list of goals/terms
%
expand((G,Gs),All_expanded) :-				%a conjunction of goals
	!,
	expand(G,G_expanded),				%expand G
	expand(Gs,Gs_expanded),				%and Gs
	append(G_expanded,Gs_expanded,All_expanded).	%make it a list
expand([],[]) :-					%an empty list
	!.
expand([G|Gs],All_expanded) :-				%a list of goals
	!,
	expand(G,G_expanded),
	expand(Gs,Gs_expanded),
	append(G_expanded,Gs_expanded,All_expanded).	%make it a list
expand(Head,Expanded) :-
	ix:description((Head :- Body)),			%get JC-B's clause of relationship definition
	!,
	expand(Body,Body_expanded),
	Expanded=[Head|Body_expanded].			%add Head back to the expanded list
expand(G,[G]).						%no expansion possible


%
% mapping IX constructs to XCIF
%

map_to_xcif([],[],Var_map,Var_map).
map_to_xcif([I|Is],Mapped,Old_map,New_map) :-
	map_to_xcif(I,X,Old_map,Temp_map),
	map_to_xcif(Is,Xs,Temp_map,New_map),
	append_cif(X,Xs,Mapped).

%
% instance_of/2
%
map_to_xcif(Ix,Xcif,Old_map,New_map) :-
	Ix=instance_of(Instance,Class),					%instance_of/2
	var(Instance),							%not instantiated yet
	match_var(Daplex_var,Instance,Old_map,New_map),			%depending on Instance is seen or not, create or match
	Xcif=[generate(Daplex_var,Class)].

map_to_xcif(Ix,Xcif,Old_map,New_map) :-
	Ix=instance_of(Instance,Class),					%instance_of/2
	nonvar(Instance),						%instantiated yet
	match_var(Daplex_var,Instance,Old_map,New_map),			%depending on Instance is seen or not, create or match
	Xcif=[	generate(Daplex_var,Class),
		expression([],[Daplex_var],expr(=,Daplex_var,Instance))
		].

%
% instance_att/3
%
%map_to_xcif(Ix,Xcif,Old_var_map,New_var_map) :-
%	Ix=instance_att(Instance,Class),				%instance_att/3
%	Xcif=[	generate(Var,Class),
%		expression([],[var(1)],expr(=,var(1),Instance))
%		].

%
% a user-defined predicate
%
map_to_xcif(Ix,Xcif,Old_map,New_map) :-
	%
	% 1st, we make sure that the "predicate" to map is a subclass of "relationship"
	%
	Ix=Rel_pred,						%get IX's predicate (relationship)
	Rel_pred=..[Rel_class|Args],				%get list of args
	is_subclass(ix,Rel_class,relationship),			%it is an IX relationship
	%
	% then we make up a XCIF to "generate" a Daplex var which is an instance of this relationship class
	%
	match_var(Daplex_rel_instance,_,Old_map,Temp_map),	%generate a new Daplex var
	X=generate(Daplex_rel_instance,Rel_class),		%a bit of XCIF to generate the relationship instance in Daplex
	%
	% now make up XCIF constructs to associate arguments
	%
	findall(F,(pfdm_schema(declare(F,Rel_class,'->',_)),not(F=arity)),Arg_names),	%find all argument names in a list
	map_to_xcif(Rel_class,Daplex_rel_instance,Args,Arg_names,Xs,Temp_map,New_map),
	append_cif(X,Xs,Xcif).					%append them together

%
% map the list of arguments to XCIF
%
map_to_xcif(_,_,[],[],[],Old_map,Old_map).			%end of list

map_to_xcif(Rel_class,Daplex_rel_instance,[A|As],[Name|Names],Xcif,Old_map,New_map) :-
	var(A),										%argument is NOT instantiated
	match_var(Daplex_arg,A,Old_map,Temp_map),					%find arg's Daplex var from map
	pfdm_schema(declare(Name,Rel_class,_,Result_type)),
	X=generate(Name,[Rel_class],[Daplex_rel_instance],Result_type,Daplex_arg),	%XCIF corresponding to this construct
	map_to_xcif(Rel_class,Daplex_rel_instance,As,Names,Xs,Temp_map,New_map),	%proceed w/ the rest
	append_cif(X,Xs,Xcif).								%combine results

map_to_xcif(Rel_class,Daplex_rel_instance,[A|As],[Name|Names],Xcif,Old_map,New_map) :-
	nonvar(A),					%argument is instantiated
	match_var(Daplex_arg,A,Old_map,Temp_map),					%find arg's Daplex var from map
	pfdm_schema(declare(Name,Rel_class,_,Result_type)),
	X=[	generate(Name,[Rel_class],[Daplex_rel_instance],Result_type,Daplex_arg),	%XCIF corresponding to this construct
		expression([],[Daplex_arg],expr(=,Daplex_arg,A))
		],
	map_to_xcif(Rel_class,Daplex_rel_instance,As,Names,Xs,Temp_map,New_map),	%proceed w/ the rest
	append_cif(X,Xs,Xcif).								%combine results

%
% match or create a Prolog var
%
% if Prolog_var is already in the map, match Daplex_var
% otherwise create a mapping pair and add it into the list
%
match_var(Daplex_var,Prolog_var,Old_map,Old_map) :-
	match_var(Daplex_var:Prolog_var,Old_map),		%matched, map remains unchanged
	!.
match_var(var(Index),Prolog_var,Old_map,New_map) :-		%not matched
	generate_counter(daplex_var,Index),			%create new Daplex var
	New_map=[var(Index):Prolog_var|Old_map].		%put new pair into map

%
% match a Daplex or Prolog
% if Daplex var is NOT instantiated, then we assuming looking for Daplex var
% otherise we assuming looking for Prolog var
%
match_var(Daplex_var:Prolog_var,Map) :-
	nonvar(Daplex_var),				%Daplex var instantiated
	member(Daplex_var:Prolog_var,Map).		%unify Prolog_var w/ the one in the map

match_var(Daplex_var:Prolog_var,Map) :-
	var(Daplex_var),
	match_var2(Daplex_var:Prolog_var,Map).		%match Daplex var

match_var2(Daplex_var:Prolog_var,[Daplex_var:P|_]) :-
	Prolog_var==P,					%Prolog vars are the same
	!.
match_var2(Daplex_var:Prolog_var,[_|Ms]) :-
	match_var2(Daplex_var:Prolog_var,Ms).		%not found, look into tail of map






%
% dump P/FDM schema from pfdm_schema/1 facts
%
%
dump_pfdm_schema :-
	ix:class(Class),
	not(pfdm_schema(declare(Class,_))),		%not a subclass of another IX class
	format("declare ~q ->> entity~n",[Class]),
	dump_pfdm_schema(Class),
	nl,
	fail.

dump_pfdm_schema :-
	pfdm_schema(declare(Subclass,Superclass)),
	format("declare ~q ->> ~q~n",[Subclass,Superclass]),
	dump_pfdm_schema(Subclass),
	fail.
dump_pfdm_schema.

dump_pfdm_schema(Class) :-
	pfdm_schema(declare(Fname,Class,Cardinality,Result_class)),
	format("declare ~q(~q) ~q ~q~n",[Fname,Class,Cardinality,Result_class]),
	fail.
dump_pfdm_schema(_).

%
% generate P/FDM-friendly schema
%
generate_pfdm_schema(File) :-
	name(File,File_string),
	append(File_string,".schema",With_extension),
	name(Filename,With_extension),
	tell(Filename),
	format("create temporary module ~q~n",[File]),
	(	print_pfdm_schema;
		true
	),
	told.


%
% print P/FDM schema in a P/FDM-friendly manner
%
print_pfdm_schema :-
	findall(X,ix:class(X),All_classes),	%find all IX classes
	sort_into_trees(All_classes,[],Trees),	%sort classes into binary trees
	print_pfdm_schema(Trees).		%print P/FDM-friendly schema


%
% printing a list of trees
%
print_pfdm_schema([]) :-			%end of list
	write(';'),
	nl.

print_pfdm_schema([T|Ts]) :-
	print_pfdm_schema(T),
	print_pfdm_schema(Ts).

%
% printing a tree
%
print_pfdm_schema(nil).				%reach a leaf

print_pfdm_schema(tree(Left,Class,Right)) :-
	print_pfdm_schema(Left),		%print left sub-tree
	print_pfdm_class(Class),		%print class at current node
	print_pfdm_schema(Right).		%print right sub-tree

print_pfdm_class(Class) :-
	pfdm_schema(declare(Class,Superclass)),
	format("declare ~q ->> ~q~n",[Class,Superclass]),
	pfdm_schema(declare(Fname,Class,Cardinality,Result_class)),
	format("declare ~q(~q) ~q ~q~n",[Fname,Class,Cardinality,Result_class]),
	pfdm_schema(key_of(Class,Key)),
	format("key_of ~q is ~q~n",[Class,Key]),
	fail.

print_pfdm_class(Class) :-
	not(pfdm_schema(declare(Class,_))),
	format("declare ~q ->> entity~n",[Class]),
	pfdm_schema(declare(Fname,Class,Cardinality,Result_class)),
	format("declare ~q(~q) ~q ~q~n",[Fname,Class,Cardinality,Result_class]),
	fail.

print_pfdm_class(_) :-
	nl.


%
% sort a list of classes in trees
% each tree contains a set of classes w/ dependence
%
sort_into_trees([],Tree,Tree).		%nothing to sort

sort_into_trees([X|Xs],[],Trees) :-		%empty tree list
	sort_into_trees(Xs,[tree(nil,X,nil)],Trees).	%create new tree, then proceed

sort_into_trees([X|Xs],Old_trees,New_trees) :-
	member(Tree,Old_trees),			%take a tree in the list
	Tree=tree(_,Node,_),		%get node value & left right subtrees
	in_same_tree(Node,X),		%X should be in same tree as Node
	!,
	sort_into_tree(X,Tree,New_tree),	%sort X into this tree
	delete(Old_trees,Tree,Temp_trees),
	sort_into_trees(Xs,[New_tree|Temp_trees],New_trees).

sort_into_trees([X|Xs],Old_trees,New_trees) :-
	sort_into_tree(X,nil,X_tree),			%create a new tree
	sort_into_trees(Xs,[X_tree|Old_trees],New_trees).	%proceed to sort
%
% sort X into a tree
%
sort_into_tree(X,nil,tree(nil,X,nil)).
sort_into_tree(X,tree(Left,Node,Right),tree(Left,Node,New_right)) :-
	depending_on(X,Node),
	!,
	sort_into_tree(X,Right,New_right).
sort_into_tree(X,tree(Left,Node,Right),tree(New_left,Node,Right)) :-
	depending_on(Node,X),
	!,
	sort_into_tree(X,Left,New_left).
sort_into_tree(X,tree(Left,Node,Right),tree(New_left,Node,Right)) :-
	sort_into_tree(X,Left,New_left).


in_same_tree(X,Y) :-
	depending_on(X,Y),
	!.
in_same_tree(X,Y) :-
	depending_on(Y,X),
	!.

in_same_tree(X,Y) :-
	depending_on(X,Z),
	depending_on(Y,Z),
	!.

in_same_tree(X,Y) :-
	depending_on(Z,X),
	depending_on(Z,Y).

%
% scan through pfdm_schema/1 facts and assert depend/2 facts
% that store dependence information between classes
%
% this is used to print a P/FDM parsable schema
%
determine_dependence :-
	pfdm_schema(declare(Subclass,Superclass)),
	assertz(depend(Subclass,Superclass)),
	fail.
determine_dependence :-
	pfdm_schema(declare(_,Class,_,Result_class)),
	ix:class(Result_class),				%only for IX classes, skipping string, integer, etc.
	assertz(depend(Class,Result_class)),
	fail.

determine_dependence.




%
% check if cyclic dependence occurs between classes A & B
%
cyclic_dependence(A,B) :-
	depend(A,B),
	depending_on(B,A).

depending_on(A,B) :-
	depend(A,B).
depending_on(A,B) :-
	depend(A,X),
	depending_on(X,B).

not(Goal) :-
	call(Goal),
	!,
	fail.
not(_).

/*
%
% append 2 CIF expressions
%
append_cif(C1,C2,Cif) :-
	my_union(C1,C2,Cif).
%
% a union/3 that works with list/single element
%
my_union(X,Y,Z) :-
is_list(X),
	is_list(Y),
	!,
	union(X,Y,Z).
my_union(X,Y,Z) :-
	is_list(Y),
	!,
	union([X],Y,Z).
my_union(X,Y,Z) :-
	is_list(X),
	!,
	union(X,[Y],Z).
my_union(X,Y,Z) :-
	union([X],[Y],Z).


union([],Y,Y).
union([H|T],Y,Z) :-
	member(H,Y),
	!,
	union(T,Y,Z).
union([H|T],Y,Z) :-
	union(T,[H|Y],Z).
*/

