type_lookup(int, int).
type_lookup(float,float).
type_lookup(string, int).
type_lookup(id,int).

generate_code('/',_OutName) :-
    !, 
    write('Error: Will not write to root directory. Please copy source file 
    to a different directory.'),nl, fail.

generate_code(Directory,OutName) :-
    
    atomic_list_concat([Directory, '/', OutName, '.out'], PathFileOut),
    (access_file(PathFileOut, write) ->
        open(PathFileOut, write, OutStream),
        (generate_code_parts(OutStream) ->
            close(OutStream)
            ;
            write('Error: Code generation failed.'),nl,
            flush_output(OutStream),
            close(OutStream),
            fail
        )
        ;
        write('Error: Cannot access output file.'),nl
    )    
    .
    
generate_code_parts(OutStream) :-
    generate_original_program(OutStream),
    nl(OutStream),
    generate_decls(OutStream),
%    write(OutStream, '// Jump labels:'),nl(OutStream),
%findall(application_edge(InputFact, Number, Rule, NewHead, Calls, Equations, Assignments), application_edge(InputFact, Number, Rule, NewHead, Calls, Equations, Assignments), AppEdgeList),
%    generate_app_edges(AppEdgeList, OutStream)
    nl(OutStream),
    generate_inits(OutStream),
    nl(OutStream),nl(OutStream),
    write(OutStream, '// Loop Part'),
    nl(OutStream),
    write(OutStream, 'while(!backtrack_stack.is_empty()) {'),
    nl(OutStream), write(OutStream,'\t'),%tab(OutStream, 4),
    write(OutStream, 'switch(backtrack_stack.pop()) {'),
    %nl(OutStream), write(OutStream,'\t'),%tab(OutStream, 4),
    findall(application_edge(InputFact,Number,Rule,Head,Calls,Equations,Assignments), application_edge(InputFact,Number,Rule,Head,Calls,Equations,Assignments), AppEdgeList),
    generate_label_code(AppEdgeList, OutStream),
    nl(OutStream), write(OutStream, '\t'),
    write(OutStream, '}'),
    nl(OutStream),
    write(OutStream, '}'),
    nl(OutStream),
    nl(OutStream),
    write(OutStream, 'bu_timer.stop();'),
    nl(OutStream),
    write(OutStream, 'std::cout << "Bottom-Up Evaluation done. ";'),
    nl(OutStream),
    write(OutStream, 'std::cout << "Time: " << bu_timer.realtime_msec() << " ms " << "(" << bu_timer.cputime_msec() << "ms CPU)\\n\\n";'),
%    nl(OutStream),
%    nl(OutStream),
    %write(OutStream, '#ifdef	EVAL_PRINT\n
    %int num_rows     = p0_b.num_rows();\n
%	int num_pages    = p0_b.num_pages();\n
%	int hash_size    = p0_b.hash_size();\n
%	int entries_used = p0_b.entries_used();\n\n
%
%	std::cout << "Duplicate check table p0_b:\\n";\n
%	std::cout << "    Number of rows:         " << num_rows << "\\n";\n
%	std::cout << "    Number of memory pages: " << num_pages <<\n
%			" [" << PAGE_KB(num_pages) << " KB]\\n";\n
%	std::cout << "    Hash table size:        " << hash_size << "\\n";\n
%	std::cout << "    Entries Used:           " << entries_used <<\n
%			" [" << IDIV_PERCENT(entries_used,hash_size) << "%]\\n";\n
%	std::cout << "    Average Chain Length:   " <<\n
%			IDIV_WHOLE(num_rows, entries_used) << "." <<\n
%			IDIV_TENTH(num_rows, entries_used) << "\\n";\n
%	std::cout << "\\n";\n\n
%
%	num_rows     = p1_b.num_rows();\n
%	num_pages    = p1_b.num_pages();\n
%	hash_size    = p1_b.hash_size();\n
%	entries_used = p1_b.entries_used();\n\n
%
%	std::cout << "Duplicate check table p1_b:\\n";
%	std::cout << "    Number of rows:         " << num_rows << "\\n";\n
%	std::cout << "    Number of memory pages: " << num_pages <<\n
%			" [" << PAGE_KB(num_pages) << " KB]\\n";\n
%	std::cout << "    Hash table size:        " << hash_size << "\\n";\n
%	std::cout << "    Entries Used:           " << entries_used <<\n
%			" [" << IDIV_PERCENT(entries_used,hash_size) << "%]\\n";\n
%	std::cout << "    Average Chain Length:   " <<\n
%			IDIV_WHOLE(num_rows, entries_used) << "." <<\n
%			IDIV_TENTH(num_rows, entries_used) << "\\n";\n
%	std::cout << "\\n";\n
%#endif'),
    nl(OutStream),
    nl(OutStream),
    %write(OutStream, '#ifndef	EVAL_PRINT_RESULT'),
    %nl(OutStream),
    %write(OutStream, 'return -1;'),
    %nl(OutStream),
    %write(OutStream, '#endif'),
    nl(OutStream),
    write(OutStream, 'return num_results;')
    .
    
generate_original_program(OutStream) :-
    findall(rule(RuleNr, Head, Body), (transformed_rule(_First,RuleNr, _,Body, Head,_),numbervars((RuleNr,Head,Body))), OrigProg1),
    list_to_ord_set(OrigProg1, OrigProg),
    write(OutStream, '// Transformed program:'), nl(OutStream),
    generate_original_rules(OrigProg, OutStream),
     nl(OutStream).

generate_original_rules([],_).    
    
generate_original_rules([rule(RuleNr, Head, Body)|OrigProg], OutStream) :-
    numbervars(rule(RuleNr, Head, Body)),
    write(OutStream,'// '),
    write(OutStream, RuleNr),
    write(OutStream, ': '),
    write(OutStream, Head),
    %if
    (Body=[] -> 
        true
        ;
        write(OutStream, ' :- '),
        print_body(Body, OutStream)
    ),
    write(OutStream, '.'),
    nl(OutStream),
    generate_original_rules(OrigProg, OutStream).
    
print_body([], _). 
print_body([Lit|Body], OutStream) :-
	write(OutStream, Lit),
	((Body \= []) ->
		write(OutStream,', ')
		;
		true
	),!,
	print_body(Body, OutStream).
  
generate_decls(OutStream) :-
	
    %write(OutStream, '// Load data file, fill domains and EDB relations:'),
    %nl(OutStream),
    %write(OutStream, '#ifdef	EVAL_PRINT'),
    %nl(OutStream),
    %write(OutStream, 'std::cout << "Loading '" << datafile << "' ...\n";'),
    %nl(OutStream),
    %write(OutStream, 'perf_c load_timer;'),
    %nl(OutStream),
    %write(OutStream, 'load_timer.start();'),
    %nl(OutStream),
    %write(OutStream, '#endif'),
    %nl(OutStream),
    %write(OutStream, 'if(!load() return -1;)'),
    %nl(OutStream),
    write(OutStream, '#ifdef	EVAL_PRINT'),
    nl(OutStream),
    write(OutStream, 'std::cout << "Bottom-Up Evaluation starts ...\\n";'),
    nl(OutStream),
    write(OutStream, 'perf_c bu_timer;'),
    nl(OutStream),
    write(OutStream, 'bu_timer.start();'),
    nl(OutStream),
    write(OutStream, '#endif'),
    nl(OutStream),
	 nl(OutStream),
    %write(OutStream, '// Declaration Part'),
    %nl(OutStream),
    write(OutStream, '// Counter for number of results:'),
    nl(OutStream),
    write(OutStream, 'int num_results = 0;'),
    nl(OutStream),
    nl(OutStream),
    %write(OutStream, '// Relations for EDB body literals:'),
    %nl(OutStream),
    %%% already defined %%%
    write(OutStream, '// Relations for IDB predicates:'),
    findall(recPred(RecPred, Types), (recursive_pred(RecPred), idb_pred_types(RecPred, Types)), RecPredList),
    generate_idb_set_variables(RecPredList, OutStream),
    
    findall(rel(IDBPred, BindingPattern, Types), (cursor(IDBPred, _RuleNo, _LitNo, BindingPattern, Types), idb_pred(IDBPred,_)), CursorRels),
    list_to_set(CursorRels, CursorRelsSet),
    generate_idb_rel_variables(CursorRelsSet, OutStream),
    
    % find all variables and generate declarations for them
    nl(OutStream),
    nl(OutStream),
    write(OutStream, '// Variable declarations:'),
    nl(OutStream),
    findall(variable(VRuleNr, VarNr, Type), variable(VRuleNr, VarNr, Type), VarList),
    generate_var_decls(VarList, OutStream),
    nl(OutStream),
    % find all cursors and generate declarations for them
    write(OutStream, '// Cursor declarations:'),
    nl(OutStream),
    findall(cursor(Pred, CRuleNumber, BodyLiteralNumber, BindingPattern, Types), cursor(Pred, CRuleNumber, BodyLiteralNumber, BindingPattern, Types), CurList),
    generate_cur_decls(CurList, OutStream),
    nl(OutStream),
    write(OutStream, '// Stack declarations:'),
    nl(OutStream),
    % generate declaration for the backtrack stack
    write(OutStream, 'stack_c<int> backtrack_stack("backtrack_stack");'),
    nl(OutStream),
    
    % generate declarations for the value stacks
    write(OutStream, 'stack_c<int> int_value_stack("int_value_stack");'),
    nl(OutStream),
    write(OutStream, 'stack_c<int> cursor_stack("cursor_stack");')
    %write(OutStream, 'float_stack float_value_stack;'),
    %nl(OutStream),
    %write(OutStream, 'string_stack string_value_stack;'),
    %nl(OutStream),
    % find all application edges, each of which yields corresponding 
    % backtrack labels
    %nl(OutStream),
    %write(OutStream, '// Jump labels:'),
    %nl(OutStream),
    %findall(application_edge(InputFact, Number, Rule, NewHead, Calls, Equations, Assignments), application_edge(InputFact, Number, Rule, NewHead, Calls, Equations, Assignments), AppEdgeList),
    %generate_app_edges(AppEdgeList, OutStream)
    
    %write(OutStream, '*/'),
    %nl(OutStream)
    .
    
generate_idb_set_variables([],_).

generate_idb_set_variables([recPred(Pred, Types)|RecPreds], OutStream) :-
	(\+member(float,Types),\+member(string, Types) ->
	    length(Types, L),
	    (L=1 -> 
	        nl(OutStream),
	        write(OutStream, 'set_1_c '),
	        write(OutStream, Pred),
	        write(OutStream, '_set("'),
	        write(OutStream, Pred),
	        write(OutStream, '_set");')
	    ;
	    L=2 -> 
	        nl(OutStream),
	        write(OutStream, 'set_2_c '),
	        write(OutStream, Pred),
	        write(OutStream, '_set("'),
	        write(OutStream, Pred),
	        write(OutStream, '_set");')
	    ;
	    	write('Error: cannot handle set variable generation for predicate '),
    		write(Pred), write(Types), nl, fail
	    )
        
    ;
	write('Error: cannot handle set variable generation for predicate '),
    	write(Pred), write(Types), nl, fail
    ),
    generate_idb_set_variables(RecPreds, OutStream)
    .
    
generate_idb_rel_variables([], _OutStream).

generate_idb_rel_variables([rel(IDBPred, BindingPattern, Types)|CursorRels], OutStream) :-
	((BindingPattern = [f], (Types=[id]; Types=[int])) -> 
		nl(OutStream),
		write(OutStream, 'list_1_c '),
		write(OutStream, IDBPred),
		write(OutStream, '_f'),
		write(OutStream, '("'),
		write(OutStream, IDBPred),
		write(OutStream, '_f'),
		write(OutStream, '");')
	;(BindingPattern = [f,f], \+member(float,Types), \+member(string, Types)) ->
		nl(OutStream),
		write(OutStream, 'list_2_c '),
		write(OutStream, IDBPred),
		write(OutStream, '_ff'),
		write(OutStream, '("'),
		write(OutStream, IDBPred),
		write(OutStream, '_ff'),
		write(OutStream, '");')
	; 
	(BindingPattern = [b], (Types=[id]; Types=[int])) ->
		(\+recursive_pred(IDBPred) ->
			nl(OutStream),
			write(OutStream, 'set_1_c '),
			write(OutStream, IDBPred),
			write(OutStream, '_set'),
			write(OutStream, '("'),
			write(OutStream, IDBPred),
			write(OutStream, '_set'),
			write(OutStream, '");')
		;
			true
		)
	; (BindingPattern = [b, b], \+member(float,Types), \+member(string, Types)) ->
		(\+recursive_pred(IDBPred) ->
			nl(OutStream),
			write(OutStream, 'set_2_c '),
			write(OutStream, IDBPred),
			write(OutStream, '_set'),
			write(OutStream, '("'),
			write(OutStream, IDBPred),
			write(OutStream, '_set'),
			write(OutStream, '");')
		;
			true
		)
		
	; ((BindingPattern = [f, b];BindingPattern = [b, f]), \+member(float,Types), \+member(string, Types)) ->
	    nl(OutStream),
	    write(OutStream, 'rel_n_n_c '),
	    write(OutStream, IDBPred),
	    write(OutStream, '_'),
	    atomic_list_concat(BindingPattern, BP),
	    write(OutStream, BP),
	    write(OutStream, '("'),
	    write(OutStream, IDBPred),
	    write(OutStream, '_'),
	    write(OutStream, BP),
	    write(OutStream, '");')
	;
		write('Error in predicate generate_idb_rel_variables: Cannot handle predicate '),
		write(IDBPred),
		write(BindingPattern),
		write(Types),
		nl, fail
	),
	generate_idb_rel_variables(CursorRels, OutStream).
    
generate_var_decls([],_).

generate_var_decls([variable(ArgOne, VarNr, Type)|VarDecls], OutStream) :-
    type_lookup(Type, TargetType),
    write(OutStream, TargetType),
    write(OutStream, ' '), 
    write(OutStream, 'v_'),
    write(OutStream, ArgOne),
    write(OutStream, '_'),
    write(OutStream, VarNr),
    write(OutStream, ' = -1'), % CARE: WORKS ONLY FOR INTEGER VARIABLES!!!
    write(OutStream, ';'),nl(OutStream),
    generate_var_decls(VarDecls, OutStream).
    
%generate_var_decls([variable(RuleNr, VarNr, id)|VarDecls], OutStream) :-
%    write(OutStream, 'int '), !,
%    write(OutStream, 'v'),
%    write(OutStream, RuleNr),
%    write(OutStream, '_'),
%    write(OutStream, VarNr),
%    write(OutStream, ';'),nl(OutStream),
%    generate_var_decls(VarDecls, OutStream).
    
%generate_var_decls([variable(RuleNr, VarNr, float)|VarDecls], OutStream) :-
%    write(OutStream, 'float '), !,
%    write(OutStream, 'v'),
%    write(OutStream, RuleNr),
%    write(OutStream, '_'),
%    write(OutStream, VarNr),
%    write(OutStream, ';'),nl(OutStream),
%    generate_var_decls(VarDecls, OutStream).
    
%generate_var_decls([variable(RuleNr, VarNr, string)|VarDecls], OutStream) :-
%    write(OutStream, 'string '), !,
%    write(OutStream, 'v'),
%    write(OutStream, RuleNr),
%    write(OutStream, '_'),
%    write(OutStream, VarNr),
%    write(OutStream, ';'),nl(OutStream),
%    generate_var_decls(VarDecls, OutStream).
    
    
generate_cur_decls([],_).

% This generates very specialized cursors, only integer arguments, up to 2 arguments
generate_cur_decls([cursor(Pred, RuleNumber, BodyLiteralNumber, BindingPattern, Types)|CurDecls], OutStream) :-
%(\+ member(float, Types), \+member(string,Types), length(Types, L), L<3 ->
(\+ member(float, Types), length(Types, L), L<3 ->	% strings will be mapped to ints
(BindingPattern \= [b], BindingPattern \= [b,b] ->
    write(OutStream, 'cur_'),
    %write(OutStream, '_cursor'),
    (BindingPattern = [f] -> write(OutStream,'1');
    BindingPattern = [f,f] -> write(OutStream, '2');
    (BindingPattern = [b,f]; BindingPattern=[f,b]) -> write(OutStream, 'n_n');
    write('Error in generate_cur_decls: cannot handle cursor '),
    write(cursor(Pred, RuleNumber, BodyLiteralNumber, BindingPattern, Types)),
    nl, fail
    ),
    
    %write(OutStream, RuleNumber),
    %write(OutStream, '_'),
    %write(OutStream, BodyLiteralNumber),
    %atomic_list_concat(BindingPattern, BP),
    %write(OutStream, '_'),
    %write(OutStream, BP),
    write(OutStream, '_c'),
    
    write(OutStream, ' c'),
    %write(OutStream, Pred),
    %write(OutStream, '_'),
    write(OutStream, RuleNumber),
    write(OutStream, '_'),
    write(OutStream, BodyLiteralNumber),
    atomic_list_concat(BindingPattern, BP),
    
    write(OutStream, '_'),
    write(OutStream, BP),
    write(OutStream, '(&'),
    write(OutStream, Pred),
    atomic_list_concat(BindingPattern, BP),
    write(OutStream, '_'),
    write(OutStream, BP),
    write(OutStream,');'),nl(OutStream)
;
	true
)
;
write('Error in predicate generate_cur_decls: cannot handle cursor '),
write(cursor(Pred, RuleNumber, BodyLiteralNumber, BindingPattern, Types)),
nl, fail
),
    generate_cur_decls(CurDecls, OutStream).
    
generate_app_edges([],_).

% (transformed_rule(_First,RuleNr, _,Body, Head,_)
generate_app_edges([application_edge(input_fact(InFactList,_,_, _), Number, app_rule(RuleNumber, LitNumber, _Line), HeadFact, _Calls, _Equations, _Assignments)|AppEdges], OutStream) :-
    write(OutStream, '// '),
    write(OutStream, 'label '),
    write(OutStream, Number),
    
    %if
    (InFactList = [] ->
    (
        write(OutStream, ': applying rule '),
        write(OutStream, RuleNumber)
    )
    ;
    (
       InFactList = [InFact],
        InFact =.. [InPred|InArgs],
        convert_var_representation(InArgs, NewInArgs),
        NewInFact =.. [InPred|NewInArgs],
        write(OutStream, ': using symbolic fact '),
        write(OutStream, NewInFact),
        write(OutStream, ' with literal '),
        write(OutStream, LitNumber),
        nl(OutStream),
        write(OutStream,'\t'),
        write(OutStream, ' in rule '),
        (transformed_rule(_First, RuleNumber, _, Body, Head, _) ->
        generate_original_rules([rule(RuleNumber, Head, Body)], OutStream);
        write('Error: cannot find rule with number '),
        write(RuleNumber),
        nl, fail
        ),
        write(OutStream,'\t')
        %write(OutStream, RuleNumber),
        %write(OutStream, ': '),
    )
    ),
    HeadFact =.. [HeadPred|HeadArgs],
    convert_var_representation(HeadArgs, NewHeadArgs),
    NewHead =.. [HeadPred|NewHeadArgs],
    write(OutStream, ' resulting in symbolic fact '),
    write(OutStream, NewHead),
    nl(OutStream),
    generate_app_edges(AppEdges, OutStream).

convert_var_representation([], []).
    
convert_var_representation([v(ArgOne,VarNr,_)|ArgList], [Converted|NewArgList]) :-
    !,
    atomic_list_concat([v,'_',ArgOne,'_',VarNr], Converted),
    convert_var_representation(ArgList, NewArgList).
    
convert_var_representation([c(RuleNr, LitNo, ArgNo, BindingPattern, _Type)|ArgList], [Converted|NewArgList]) :-
    !,
    atomic_list_concat(BindingPattern, BP),
    ((BindingPattern = [f]; BindingPattern = [b,f]; BindingPattern =[f,f]) ->
        atomic_list_concat([c,RuleNr,'_',LitNo,'_',BP,'.c',ArgNo,'()'], Converted)
    ; (BindingPattern = [f,b], ArgNo = 1) ->
        atomic_list_concat([c,RuleNr,'_',LitNo,'_',BP,'.c',2,'()'], Converted)
    ;
        write('Error in predicate convert_var_representation: cannot handle cursor binding pattern '),
        write(BindingPattern),nl,
        fail
    ),
    convert_var_representation(ArgList, NewArgList).
  
convert_var_representation([Arg|ArgList], [Arg|NewArgList]) :-
    number(Arg),
    !,
    convert_var_representation(ArgList, NewArgList).
    
convert_var_representation([Arg|ArgList], [Arg2|NewArgList]) :-
    atom(Arg),
    !,
    atomic_list_concat(['"',Arg,'"'],Arg2),
    convert_var_representation(ArgList, NewArgList).
    
convert_var_representation([Arg|ArgList], [Arg2|NewArgList]) :-
    string_codes(Arg,_), !,
    atomic_list_concat(['"',Arg,'"'],Arg2),
    convert_var_representation(ArgList, NewArgList).
    
generate_inits(OutStream) :-
    nl(OutStream),
    write(OutStream, '// Initialisation Part'),
    %nl(OutStream),
    generate_stack_init(OutStream).
    
generate_stack_init(OutStream) :-
    find_idb_fact_start_labels(StartList1),
    findall(next_label(Number),application_edge(input_fact([],[],[], []), Number, _Rule, _Head, [_|_Calls], _Equations, _Assignments), StartList2),
    generate_push_next_labels(StartList1, 0, OutStream),
    generate_push_next_labels(StartList2, 0, OutStream).
    
find_idb_fact_start_labels(StartList) :-
    findall(Head, application_edge(input_fact([],[],[], []), _Number, _Rule, Head, [], _Equations, _Assignments),IdbFactList),
    find_next_labels(IdbFactList, StartList).
    
generate_push_next_labels([], _,_).

generate_push_next_labels([next_label(Number)|NextList], Indent, OutStream) :-
    generate_push_next_labels(NextList, Indent, OutStream),
    nl(OutStream), writeNumTabs(OutStream,Indent),
    write(OutStream, 'if(!backtrack_stack.push('),
    write(OutStream, Number),
    write(OutStream, ')) return -1;')
    %nl(OutStream), tab(OutStream, Indent)
    .

generate_label_code([], _OutStream).

% (transformed_rule(_First,RuleNr, _,Body, Head,_)    
generate_label_code([application_edge(input_fact(InFact,_ModifiedFact,InEquations, InAssignments), Number, app_rule(RuleNumber, LitNumber, _Line), Head, Calls, Equations, Assignments)|AppEdgeList], OutStream) :-
    application_edge_number(N),
    Head =.. [HeadPred|HeadArgs],
    convert_var_representation(HeadArgs, NewHeadArgs),
    
        
        %if
        (InFact = [], Calls = [] ->
            % Rule body is empty, do not generate code for this case
            true
            ;
            nl(OutStream), write(OutStream,'\t'),
            write(OutStream, 'case '),
            write(OutStream, Number),
            write(OutStream, ':'),
            (InFact \= [] ->
                write(OutStream, ' label_'),
                write(OutStream, Number),
                write(OutStream, ': '),
                nl(OutStream), write(OutStream,'\t'),
                write(OutStream, '// using  symbolic fact '),
                InFact = [NewInFact],
                NewInFact =.. [NewInHead|NewInArgs],
                convert_var_representation(NewInArgs, NewInArgs2),
                NewInFact2 =.. [NewInHead|NewInArgs2],
	            write(OutStream, NewInFact2),
	            write(OutStream, ' with literal '),
	            write(OutStream, LitNumber),
	            %nl(OutStream),
	            %write(OutStream,'\t'),
            	write(OutStream, ' in rule '),
            	nl(OutStream),
            	write(OutStream, '\t'),
            	(transformed_rule(_First, RuleNumber, _, Body, RuleHead, _) ->
	            generate_original_rules([rule(RuleNumber, RuleHead, Body)], OutStream);
	            write('Error: cannot find rule '), write(RuleNumber),nl, fail
	            ),
            	write(OutStream, '\t// resulting in symbolic fact '), 
            	
                
                NewHead =.. [HeadPred|NewHeadArgs],
                write(OutStream, NewHead),
                nl(OutStream),
                %tab(OutStream, 8),
                
                (InEquations \= [] ->
                    nl(OutStream),
                    writeNumTabs(OutStream,2),
                    write(OutStream, 'if('),
                    generate_equations(InEquations,2,OutStream),
                    write(OutStream, ')'),
                    nl(OutStream),
                    %tab(OutStream,8),
                    writeNumTabs(OutStream,2),
                    write(OutStream,'break;')
                    ;
                    true
                ),
                (recursive(Number) ->
                        nl(OutStream),
                        writeNumTabs(OutStream,2),
                        write(OutStream, '//recursive'),
                        %nl(OutStream),
                        %tab(OutStream,8),
                        % Store cursors
                        generate_store_cursors(Calls, OutStream),
                        
                        % Store previous values
                        append(InAssignments, Assignments, Assignments2),
                        generate_store_values(Assignments2, OutStream),
                        nl(OutStream),
                        writeNumTabs(OutStream,2),
                        write(OutStream,'// Push backtrack label to restore values'),
                        nl(OutStream),
                        writeNumTabs(OutStream,2),
                        write(OutStream, 'if(!backtrack_stack.push('),
                        Number2 is N*2 + Number,
                        write(OutStream, Number2),
                        write(OutStream, ')) return -1;')%,
                        %nl(OutStream),
                        %tab(OutStream,8)
                        ;
                        true
                ),
                    
                
                (InAssignments \= [] ->
                    %nl(OutStream),
                    %tab(OutStream,8),
                    
                    generate_assignments(InAssignments,2,OutStream)
                    ;
                    true
                )
                ;
                %true
                nl(OutStream), write(OutStream,'\t'),
                write(OutStream, '// applying rule '),
            	nl(OutStream),
            	write(OutStream, '\t'),
            	(transformed_rule(_First, RuleNumber, _, Body, RuleHead, _) ->
	            generate_original_rules([rule(RuleNumber, RuleHead, Body)], OutStream);
	            write('Error: cannot find rule '), write(RuleNumber),nl, fail
	            ),
            	write(OutStream, '\t// resulting in symbolic fact '), 
            	
                
                NewHead =.. [HeadPred|NewHeadArgs],
                write(OutStream, NewHead)
            ),
            
            % Rule body contains only edb literals
            %Open cursors
            %generate_open_cursors(Calls, OutStream),
            %nl(OutStream), tab(OutStream, 4),
            (Calls \=[] ->
                %nl(OutStream), tab(OutStream, 8),
                generate_open_fetch_cursors(Calls, Equations, Assignments, Number, Head, _HasContinuation, 2, OutStream)
                ;
                (answer_pred(AnswerPred), HeadPred=AnswerPred -> 
                    nl(OutStream),
                    write(OutStream,'\t\tnum_results++;')
                ;
                	generate_copy_code(Assignments, OutStream,2),
                    (recursive_pred(HeadPred) ->
                        nl(OutStream),
                        write(OutStream, '\t\tif('),
                        write(OutStream,HeadPred),
                        write(OutStream, '_set.insert('),
                        print_call_args(HeadArgs, OutStream),
                        write(OutStream, ')) {')
                    ;
                        true
                    ),
                    
                    findall(curRel(HeadPred, HeadArgs, BindingPattern, Types), cursor(HeadPred, _RuleNo, _LitNo, BindingPattern, Types), CurRelList),
                    list_to_set(CurRelList, CurRelSet),
                    generate_cur_rel_fill(CurRelSet, OutStream, 2),
                    %nl(OutStream), tab(OutStream, 8),
                    find_next_labels([Head],LabelList),
                    (LabelList = [next_label(Lab)|Labels] ->
                        nl(OutStream),
                        writeNumTabs(OutStream,2),
                        write(OutStream, '// Labels for next rules'),
                        %nl(OutStream),
                        %tab(OutStream, 8),
                        generate_push_next_labels(Labels, 2, OutStream),
                        nl(OutStream),
                        writeNumTabs(OutStream,2),
                        write(OutStream,'goto label_'),
                        write(OutStream, Lab),
                        write(OutStream, ';')
                        ;
                        true
                    ),
                    (recursive_pred(HeadPred) ->
                        nl(OutStream),
                        write(OutStream, '\t\t}')
                    ;    
                        true
                    )
                )
            ),
            
            
            nl(OutStream),
            writeNumTabs(OutStream,2),
            write(OutStream, 'break;')
        ),
    %nl(OutStream), tab(OutStream, 4),
    (recursive(Number) ->
        
        nl(OutStream),
        writeNumTabs(OutStream,1),
        write(OutStream, 'case '),
        
        RestoreNumber is Number+N*2,
        write(OutStream, RestoreNumber),
        write(OutStream, ':'),
        %nl(OutStream), tab(OutStream, 8),
        nl(OutStream),
        writeNumTabs(OutStream,1),
        write(OutStream,'// Label to restore values of label '),
        write(OutStream, Number),
        append(InAssignments, Assignments, Assignments2),
        generate_pop_values(Assignments2, OutStream),
        generate_pop_cursors(Calls, OutStream),
        nl(OutStream),
        writeNumTabs(OutStream,2),
        write(OutStream, 'break;')%,
        %nl(OutStream), tab(OutStream, 4)
        ;
        true
    ),
    generate_label_code(AppEdgeList, OutStream)
    .
 
% (HeadPred=answer -> 
%                    nl(OutStream),
%                    write(OutStream,'\t\tnum_results++;')
%                ; 
%                    (recursive_pred(HeadPred) ->
%                        nl(OutStream),
%                        write(OutStream, '\t\tif('),
%                        write(OutStream,HeadPred),
%                        write(OutStream, '_set.insert('),
%                        print_call_args(NewHeadArgs, OutStream),
%                        write(OutStream, ')) {')
%                    ;
%                        true
%                    ),
%                    
%                    % insert generated fact into cursor relations
%                    
%                    
%                    (recursive_pred(HeadPred) ->
%                        nl(OutStream),
%                        write(OutStream, '\t\t}')
%                    ;    
%                        true
%                    )
%                )
    
generate_open_fetch_cursors([],Equations, Assignments, Label, Head, HasContinuation, Indent,OutStream) :-
    
    (Equations \= [] ->
        nl(OutStream),
        writeNumTabs(OutStream,Indent),
        write(OutStream, 'if('),
        generate_equations(Equations, Indent, OutStream),
        write(OutStream, ')'), % closing if condition
        nl(OutStream),
        writeNumTabs(OutStream,Indent),
        write(OutStream, 'continue;')
        ;
        true
    ),
    Head =..[HeadPred|HeadArgs],
    %nl(OutStream),
    %tab(OutStream, Indent),
    (answer_pred(AnswerPred), HeadPred=AnswerPred -> 
        nl(OutStream),
        write(OutStream,'\t\tnum_results++;')
    ;
        
        %convert_var_representation(HeadArgs, NewHeadArgs),
        generate_assignments(Assignments, Indent, OutStream),
        (recursive_pred(HeadPred) ->
            nl(OutStream),
            write(OutStream, '\t\tif('),
            
            write(OutStream,HeadPred),
            write(OutStream, '_set.insert('),
            print_call_args(HeadArgs, OutStream),
            write(OutStream, ')) {')
        ;
            true
        ),
        
        findall(curRel(HeadPred, HeadArgs, BindingPattern, Types), cursor(HeadPred, _RuleNo, _LitNo, BindingPattern, Types), CurRelList),
        list_to_set(CurRelList, CurRelSet),
        generate_cur_rel_fill(CurRelSet, OutStream, Indent),
        
        % Push label to backtrack to continuation
        (HasContinuation == true ->
	        nl(OutStream),
	        writeNumTabs(OutStream,Indent),
	        write(OutStream, '// Push backtrack label to continuation'),
	        nl(OutStream),
	        writeNumTabs(OutStream,Indent),
	        application_edge_number(N),
	        Number2 is Label + N,
	        write(OutStream, 'if(!backtrack_stack.push('),
	        write(OutStream, Number2),
        	write(OutStream, ')) return -1;')
        ;
        	true
        ),
        %nl(OutStream),
        %tab(OutStream, Indent),
        %(recursive(Label) ->
        %    write(OutStream, '// Push backtrack label to restore values'),
        %    nl(OutStream),
        %    tab(OutStream, Indent),
        %    write(OutStream, 'backtrack_stack.push('),
            
        %    RestoreLabel is N*2 + Label,
        %    write(OutStream, RestoreLabel),
        %    write(OutStream, ');'),
        %    nl(OutStream),
        %    tab(OutStream, Indent)
        %    ;
        %    true
        %),
        find_next_labels([Head],LabelList),
        (LabelList = [next_label(Lab)|Labels] ->
            nl(OutStream),
            writeNumTabs(OutStream,Indent),
            write(OutStream, '// Labels for next rules'),
            %nl(OutStream),
            %tab(OutStream, Indent),
            generate_push_next_labels(Labels, Indent, OutStream),
            nl(OutStream),
            writeNumTabs(OutStream,Indent),
            write(OutStream,'goto label_'),
            write(OutStream, Lab),
            write(OutStream, ';')
            ;
            true
        ),
        (recursive_pred(HeadPred) ->
            nl(OutStream),
            write(OutStream, '\t\t}')
        ;    
            true
        )
    )
    
    .
    
generate_open_fetch_cursors([cursor_call(Pred, RuleNumber, BodyLitNumber, BindingPattern, CallArgs)|Calls],Equations, Assignments, Label,Head, HasContinuation, Indent, OutStream) :-
    %Open cursor
    (member('f',BindingPattern) ->
        nl(OutStream),
        writeNumTabs(OutStream,Indent),
        write(OutStream, 'c'),
        write(OutStream, RuleNumber),
        write(OutStream, '_'),
        write(OutStream, BodyLitNumber),
        write(OutStream, '_'),
        atomic_list_concat(BindingPattern, BP),
        write(OutStream, BP),
        write(OutStream, '.open('),
        print_call_args(CallArgs, OutStream),
        write(OutStream, ');')
     ; 
        nl(OutStream),
        writeNumTabs(OutStream,Indent),
        write(OutStream,'if (!'),
        write(OutStream, Pred),
        write(OutStream, '_'),
        (edb_pred(Pred,_) ->
	        atomic_list_concat(BindingPattern, BP),
	        write(OutStream, BP)
        ; 
        	write(OutStream, 'set')
        ),
        %atomic_list_concat(BindingPattern, BP),
        %write(OutStream,BP),
        write(OutStream,'.contains('),
        print_call_args(CallArgs, OutStream),
        write(OutStream,')) continue;')
    ),
    
    %fetch cursor
    (member('f', BindingPattern), noBPWithF(Calls) ->
        HasContinuation = true,
        nl(OutStream),
        writeNumTabs(OutStream,1),
        write(OutStream, 'case '),
        application_edge_number(N),
        Number2 is Label + N,
        write(OutStream, Number2),
        write(OutStream, ':'),
        nl(OutStream),
        writeNumTabs(OutStream,1),
        write(OutStream, '// Continuation for label '),
        write(OutStream, Label)        
        %nl(OutStream), tab(OutStream, Indent)
        ;
        true
    ),
    (member('f',BindingPattern) ->
        nl(OutStream),
        writeNumTabs(OutStream,Indent),
        write(OutStream, 'while (c'),
        %write(OutStream, Pred),
        %write(OutStream, '_'),
        write(OutStream, RuleNumber),
        write(OutStream, '_'),
        write(OutStream, BodyLitNumber),
        write(OutStream, '_'),
        atomic_list_concat(BindingPattern, BP),
        write(OutStream, BP),
        write(OutStream, '.fetch()) {')
    ; true
    ),
    %nl(OutStream),
    %writeNumTabs(OutStream,Indent),
    %write(OutStream,'{'),
    NewIndent is Indent + 1,
    %nl(OutStream),
    %tab(OutStream, NewIndent),
    
    generate_open_fetch_cursors(Calls, Equations, Assignments,Label, Head, HasContinuation, NewIndent, OutStream),
    
    (member('f',BindingPattern) ->
	    nl(OutStream),
	    writeNumTabs(OutStream, Indent),
	    write(OutStream, '}')
    ;
    	true
    )
    .

noBPWithF([]).

noBPWithF([cursor_call(_Pred, _RuleNumber, _BodyLitNumber, BindingPattern, _CallArgs)|Calls]) :-
	\+member('f',BindingPattern),
	noBPWithF(Calls).

generate_cur_rel_fill([], _, _).

generate_cur_rel_fill([curRel(HeadPred, HeadArgs, BindingPattern, Types)|CurRelSet], OutStream, Indent) :-
    (\+member(float, Types), \+member(string, Types) ->
        (BindingPattern = [f,b] ->
            nl(OutStream),
            writeNumTabs(OutStream, Indent),
            write(OutStream, 'if(!'),
            write(OutStream, HeadPred),
            write(OutStream, '_fb'),
            write(OutStream, '.insert('),
            HeadArgs = [Arg1, Arg2],
            print_call_args([Arg2, Arg1], OutStream), 
            write(OutStream, ')) return -1;')
        ;
        BindingPattern = [b,f] ->
            nl(OutStream),
            writeNumTabs(OutStream, Indent),
            write(OutStream, 'if(!'),
            write(OutStream, HeadPred),
            write(OutStream, '_bf'),
            write(OutStream, '.insert('),
            print_call_args(HeadArgs, OutStream), 
            write(OutStream, ')) return -1;')
        ;
        \+member(f,BindingPattern) ->
        	(\+recursive_pred(HeadPred) ->
	            nl(OutStream),
	            writeNumTabs(OutStream, Indent),
	            %write(OutStream, 'if(!'),
	            write(OutStream, HeadPred),
	            write(OutStream, '_set'),
	            write(OutStream, '.insert('),
	            print_call_args(HeadArgs, OutStream), 
	            write(OutStream, ');')
	            %) continue;')
	        ;
            		true
            	)
        ;
        \+member(b,BindingPattern) ->
            nl(OutStream),
            writeNumTabs(OutStream, Indent),
            write(OutStream, 'if(!'),
            write(OutStream, HeadPred),
            write(OutStream, '_'),
            atomic_list_concat(BindingPattern, BP),
            write(OutStream, BP),
            write(OutStream, '.insert('),
            print_call_args(HeadArgs, OutStream), 
            write(OutStream, ')) return -1;')
        ;
            write('Error in predicate generate_cur_rel_fill: cannot handle IDB relation '),
            write(rel(HeadPred, HeadArgs, BindingPattern, Types)),
            nl, fail
        )
    ;
        write('Error in predicate generate_cur_rel_fill: cannot handle IDB relation '),
        write(rel(HeadPred, HeadArgs, BindingPattern, Types)),
        nl, fail
    ),
    generate_cur_rel_fill(CurRelSet, OutStream, Indent).
    
%generate_open_cursors([], _).    
    
%generate_open_cursors([cursor_call(_Pred, RuleNumber, BodyLitNumber, BindingPattern, CallArgs)|Calls], OutStream) :-
%    write(OutStream, 'c'),
    %write(OutStream, Pred),
    %write(OutStream, '_'),
%    write(OutStream, RuleNumber),
%    write(OutStream, '_'),
%    write(OutStream, BodyLitNumber),
%    write(OutStream, '_'),
%    atomic_list_concat(BindingPattern, BP),
%    write(OutStream, BP),
%    write(OutStream, '.open('),
%    print_call_args(CallArgs, OutStream),
%    write(OutStream, ');'),
%    nl(OutStream), tab(OutStream, 8),
%    generate_open_cursors(Calls, OutStream)
%    .
    
%generate_fetch_cursors([],Equations, Assignments, Label, Head, Indent,OutStream) :-
%    
%    (Equations \= [] ->
%        write(OutStream, 'if('),
%        generate_equations(Equations, Indent, OutStream),
%        write(OutStream, ')'), % closing if condition
%        nl(OutStream),
%        tab(OutStream, Indent),
%        write(OutStream, 'continue;')
%        ;
%        true
%    ),
%    nl(OutStream),
%    tab(OutStream, Indent),
%    generate_assignments(Assignments, Indent, OutStream),
    % Push label to backtrack to continuation
%    write(OutStream, '// Push backtrack label to continuation'),
%    nl(OutStream),
%    tab(OutStream, Indent),
%    application_edge_number(N),
%    Number2 is Label + N,
    
%    write(OutStream, 'backtrack_stack.push('),
%    write(OutStream, Number2),
%    write(OutStream, ');'),
%    nl(OutStream),
%    tab(OutStream, Indent),
%    find_next_labels([Head],LabelList),
%    (LabelList = [next_label(Lab)|Labels] ->
%        write(OutStream, '// Labels for next rules'),
%        nl(OutStream),
%        tab(OutStream, Indent),
%        generate_push_next_labels(Labels, Indent, OutStream),
%        write(OutStream,'goto label_'),
%        write(OutStream, Lab),
%        write(OutStream, ';')
%        ;
%        true
%    )
%    .
    
%generate_fetch_cursors([cursor_call(_Pred, RuleNumber, BodyLitNumber, BindingPattern, CallArgs)|Calls],Equations, Assignments, Label,Head, Indent, OutStream) :-
%    (Calls = [] ->
%        write(OutStream, '// Continuation for label '),
%        write(OutStream, Label),
%        nl(OutStream), tab(OutStream, 4),
%        write(OutStream, 'case '),
%        application_edge_number(N),
%        Number2 is Label + N,
%        write(OutStream, Number2),
%        write(OutStream, ':'),         
%        nl(OutStream), tab(OutStream, Indent)
%        ;
%        true
%    ),
    
    
%    write(OutStream, 'while (c'),
    %write(OutStream, Pred),
    %write(OutStream, '_'),
%    write(OutStream, RuleNumber),
%    write(OutStream, '_'),
%    write(OutStream, BodyLitNumber),
%    write(OutStream, '_'),
%    atomic_list_concat(BindingPattern, BP),
%    write(OutStream, BP),
 %   write(OutStream, '.fetch()) {'),
 %   NewIndent is Indent + 4,
%    nl(OutStream),
%    tab(OutStream, NewIndent),
%    generate_fetch_cursors(Calls, Equations, Assignments,Label, Head, NewIndent, OutStream),
%    nl(OutStream),
%    tab(OutStream, Indent),
%    write(OutStream, '}')
%    .
    
generate_equations([], _Indent, _OutStream).
    
generate_equations([equal(X, Y)|Equations], Indent, OutStream) :-
    convert_equation_arg(X, XC),
    convert_equation_arg(Y, YC),
    nl(OutStream),
    writeNumTabs(OutStream,Indent),
    write(OutStream, XC),
    write(OutStream, ' != '),
    write(OutStream, YC),
    (Equations \= [] ->
        write(OutStream, ' || ')%,
        %nl(OutStream),
        %tab(OutStream, Indent)
        ;
        true
    ),
    generate_equations(Equations, Indent, OutStream)
    .
    
generate_assignments([],_,_).
    
generate_assignments([assign(X,Y)|Assignments], Indent, OutStream) :-
    convert_var_representation([X,Y], [XC, YC]),
    nl(OutStream),
    writeNumTabs(OutStream,Indent),
    write(OutStream, XC),
    write(OutStream, ' = '),
    write(OutStream, YC),
    write(OutStream, ';'),
    %write(OutStream, ' CHECK('),
    %write(OutStream, XC),
    %write(OutStream, ' >= 0, "'),
    %write(OutStream, ''),
    %(Assignments \= [] ->
        
    %    ;
    %    true
    %),
    %nl(OutStream),
    %tab(OutStream, Indent),
    generate_assignments(Assignments, Indent, OutStream).
    
generate_copy_code([],_,_).

generate_copy_code([assign(v(No1,Count1,Type),v(No2,Count2,Type))|Assignments], OutStream, Indent) :-
    convert_var_representation([v(No1,Count1,Type),v(No2,Count2,Type)], [XC, YC]),
    nl(OutStream),
    writeNumTabs(OutStream,Indent),
    write(OutStream, XC),
    write(OutStream, ' = '),
    write(OutStream, YC),
    write(OutStream, ';'),
    generate_copy_code(Assignments, OutStream, Indent).
    
generate_copy_code([assign(v(_,_,_),c(_,_,_,_,_))|Assignments], OutStream, Indent) :-
    
    generate_copy_code(Assignments, OutStream, Indent).
    
generate_store_cursors([],_).
    
generate_store_cursors([cursor_call(_Pred, RuleNumber, BodyLitNumber, BindingPattern, _CallArgs)|Calls], OutStream) :-
    generate_store_cursors(Calls, OutStream),
    (member(f,BindingPattern) ->
	    nl(OutStream),
	    writeNumTabs(OutStream,2),
	    write(OutStream, 'c'),
	    %write(OutStream, Pred),
	    %write(OutStream, '_'),
	    write(OutStream, RuleNumber),
	    write(OutStream, '_'),
	    write(OutStream, BodyLitNumber),
	    write(OutStream, '_'),
	    atomic_list_concat(BindingPattern, BP),
	    write(OutStream, BP),
    	    write(OutStream, '.push(&cursor_stack);')
    ;
    	    true
    )
    .
    
generate_pop_cursors([],_).
    
generate_pop_cursors([cursor_call(_Pred, RuleNumber, BodyLitNumber, BindingPattern, _CallArgs)|Calls], OutStream) :-
(member(f,BindingPattern) ->
    nl(OutStream),
    writeNumTabs(OutStream,2),
    write(OutStream, 'c'),
    %write(OutStream, Pred),
    %write(OutStream, '_'),
    write(OutStream, RuleNumber),
    write(OutStream, '_'),
    write(OutStream, BodyLitNumber),
    write(OutStream, '_'),
    atomic_list_concat(BindingPattern, BP),
    write(OutStream, BP),
    write(OutStream, '.pop(&cursor_stack);')
    %nl(OutStream),
    %tab(OutStream, 8),
;
	true
),
    generate_pop_cursors(Calls, OutStream)
    .

generate_store_values([], _).

generate_store_values([assign(v(A,B,Type),_)|Assignments], OutStream) :-
    generate_store_values(Assignments,OutStream),
    type_lookup(Type,TargetType),
    nl(OutStream),
    writeNumTabs(OutStream,2),
    write(OutStream, 'if(!'),
    write(OutStream, TargetType),
    write(OutStream, '_value_stack.push('),
    write(OutStream, 'v_'),
    write(OutStream, A),
    write(OutStream, '_'),
    write(OutStream, B),
    write(OutStream, ')) return -1;')%,
    %nl(OutStream),
    %tab(OutStream, 8)
    .
    
generate_pop_values([], _).

generate_pop_values([assign(v(A,B,Type),_)|Assignments], OutStream) :-
    
    type_lookup(Type,TargetType),
    nl(OutStream),
    writeNumTabs(OutStream,2),
    write(OutStream, 'v_'),
     write(OutStream, A),
    write(OutStream, '_'),
    write(OutStream, B),
    write(OutStream, ' = '),
    write(OutStream, TargetType),
    write(OutStream, '_value_stack.pop();'),
    %nl(OutStream),
    %tab(OutStream, 8),
    generate_pop_values(Assignments,OutStream)
    .

convert_equation_arg(v(ArgOne, VarNo, _), Converted) :-
    !, 
    atomic_list_concat([v,'_',ArgOne,'_',VarNo], Converted).

convert_equation_arg(c(RuleNo, BodyLitNo, ArgNo, BindingPattern, _), Converted) :-
    !, 
    atomic_list_concat(BindingPattern, BP),
    atomic_list_concat([c,RuleNo,'_',BodyLitNo,'_',BP,'.c',ArgNo,'()'], Converted).


convert_equation_arg(Arg, Arg) :-
    number(Arg), !.
    
convert_equation_arg(Arg, decode(Arg2)) :-
    atom(Arg), !,
    atomic_list_concat(['"',Arg,'"'],Arg2).
    
convert_equation_arg(Arg, Arg2) :-
    string_codes(Arg, _), \+atomic(Arg), !,
    atomic_list_concat(['"',Arg,'"'],Arg2).
    
print_call_args(CallArgs, OutStream) :-
    convert_var_representation(CallArgs, NewCallArgs),
    print_call_args_rec(NewCallArgs, OutStream).
    
print_call_args_rec([],_).

print_call_args_rec([Arg|Args], OutStream) :-
    write(OutStream, Arg),
    (Args = [] ->
        true;
        write(OutStream, ', ')
    ),
    print_call_args_rec(Args, OutStream).
    
find_next_labels([], []).

find_next_labels([SymbolicFact|SymbolicFacts], LabelList) :-
    findall(next_label(Number), application_edge(input_fact([SymbolicFact], _ModifiedFact, _InEquations, _InAssignments), Number, _Rule, _Head, _Calls, _Equations, _Assignments), LList1),
    find_next_labels(SymbolicFacts, LList2),
    append(LList1, LList2, LabelList).

writeNumTabs(_,0).
    
writeNumTabs(Stream,Num) :-
    Num > 0,
    write(Stream, '\t'),
    Num2 is Num-1,
    writeNumTabs(Stream,Num2).
