%
%   This file contains the source code for the second optimization, 
%   which implements the algorithm for query compilation
%  


:- import updat/1 from updates.
:- import append/3 from basics.

% define directive opt2_trans
opt2_trans(File_ctr) :-
                in_opt2(File_ctr,File1),
                see(File1),
                remove_upd1,
                insert_upd1(Preds),
                remove_graph1,
                remove_db_pred,
                opt_read_and_create_graph1(File1),
                seen,
                see(File1),
                out_opt2(File_ctr,FileOut2),
                unix('rm -f FileOut2 2>/dev/null', R1),
                tell(FileOut2),
                opt_read_and_compile2(File1,FileOut2),
                seen, told,
                opt_insert_file(FileOut2),
                see(File1),
                out_db(File_ctr,FileOut),
                unix('rm -f FileOut 2>/dev/null', R1),
                tell(FileOut),
                opt_read_and_compile1(File1,FileOut),
                seen, told,
                opt_insert_file(FileOut).

% parse transaction base rules repeatedly
% to create dependency graph
opt_read_and_create_graph1(FileIn) :-
        read(Term),
        (
        (Term == end_of_file) -> true
        ;
        opt_read_rule1(Term,FileIn),
        opt_read_and_create_graph1(FileIn)  ).

% read transaction base object file and write
% optimized object database file
opt_read_and_compile1(FileIn,FileOut) :-
        read(Term),
        (
        (Term == end_of_file) -> true
        ;
        opt_read_and_write_rule1(Term,FileIn,FileOut),
        opt_read_and_compile1(FileIn,FileOut)  ).
% read transaction base object file and write
% optimized transaction-base object file
opt_read_and_compile2(FileIn,FileOut) :-
        read(Term),
        (
        (Term == end_of_file) -> true
        ;
        opt_read_and_write_rule2(Term,FileIn,FileOut),
        opt_read_and_compile2(FileIn,FileOut)  ).

% determine edges from transaction base rules
opt_read_rule1(':-'(trans(Lhs),Rhs),_) :-
        Lhs =..Lname,
        Lname = [Name|_],
        insert_edge1(Name, Rhs).

opt_read_rule1(':-'(Lhs,Rhs),_).

% write optimized rules in database file
opt_read_and_write_rule1(':-'(trans(Lhs),Rhs),_,_) :-
        Lhs =..LLhs,
        LLhs = [Name|_],
        check_pred(Name),
        opt(Lhs, Rhs, PRhs),
        create_trans_db(Lhs, NLhs),
        write(NLhs),
        write(' :-  '),
        (islist(PRhs),
        write_list(PRhs)
        ;
        create_trans_db(PRhs, NPRhs),
        write(NPRhs),
        write('.')),
        nl,
        write(NLhs),
        write(' :-  '),
        write(Lhs),
        write('.'),
        nl,
        (islist(PRhs),
        write_list_pred_rules(PRhs)
        ;
        (updat(PRhs),
         create_trans_db(PRhs, NRhs),
         write(NRhs),
         write(' :-  '),
         write(PRhs),
         write('.'), nl
         ;
         not(updat(PRhs)))).

opt_read_and_write_rule1(':-'(Lhs,Rhs),_,_).

% write optimized rules in transaction base file
opt_read_and_write_rule2(':-'(trans(Lhs),Rhs),_,_) :-
        check_ref(Lhs),
        write(trans(Lhs)),
        write(' :-  '),
        optT(Rhs, PR, PRhs),
        write(PRhs),
        write('.'), nl,
       (islist(PR),
        write_list_pred_trans(PR)
        ;
        (updat(PR),
         create_trans_db(PR, NR),
         write(trans(NR)),
         write(' :-  '),
         write(PR),
         write('.'), nl
         ;
         not(updat(PR)))).

opt_read_and_write_rule2(':-'(trans(Lhs),Rhs),_,_) :-
        write(trans(Lhs)),
        write(' :-  '),
        optT(Rhs, PR, PRhs),
        write(PRhs),
        write('.'), nl.

opt_read_and_write_rule2(':-'(Lhs,Rhs),_,_) :-
        write(Lhs),
        write(' :-  '),
        write(Rhs),
        write('.'), nl.

opt(Lhs, Rhs, PRhs) :- not(iref1(Lhs,_)),
                       not(dref1(Lhs,_)),
                       remove_op(Rhs, PRhs).

% translate CTR terms into Prolog terms
optT(isolate(P), PR, R) :- not(iref1(P,_)),
                       not(dref1(P,_)),
                       remove_op(isolate(P), PR),
                       (islist(PR),
                        create_ctr_list(PR, NPR),
                        conv(NPR, R, ',')
                        ;
                        PR =..LPR,
                        LPR = [Name|_],
                        assert(db_pred(Name)),
                        create_trans_db(PR, NPR),
                        conv_one([NPR], R, '')).

optT(isolate(P), PR, isolate(R)) :- optT(P, PR, R).

optT(isolate1(P), PR, R) :- not(iref1(P,_)),
                        not(dref1(P,_)),
                        remove_op(isolate1(P), PR),
                       (islist(PR),
                        create_ctr_list(PR, NPR),
                        conv(NPR, R, ',')
                        ;
                        PR =..LPR,
                        LPR = [Name|_],
                        assert(db_pred(Name)),
                        create_trans_db(PR, NPR),
                        conv_one([NPR], R, 'call')).

optT(isolate1(P), PR, isolate1(R)) :- optT(P, PR, R).

optT(seq(L), PR, seq(L1)) :- optT_list(L, PR, L1).
optT(conc(L), PR, conc(L1)) :- optT_list(L, PR, L1).
optT(conc1(L), PR, conc1(L1)) :- optT_list(L, PR, L1).
optT(P, PR, P).

optT_list([], PR, []).
optT_list(L, PR, L1) :- L = [H0|Tail0],
                    L1 = [H1|Tail1],
                    optT(H0, PR, H1),
                    optT_list(Tail0, PR, Tail1).

% create translated clauses
remove_op(isolate(P), R) :- remove_op(P, R).
remove_op(isolate1(P), R) :- remove_op(P, R).
remove_op(seq(L), R) :- proc_list(L, R).
remove_op(conc(L), R) :- proc_list(L, R).
remove_op(conc1(L), R) :- proc_list(L, R).
remove_op(P, P).

proc_list([], []).
proc_list(L, L1) :- L = [H0|Tail0],
                    L1 = [H1|Tail1],
                    remove_op(H0, H1),
                    proc_list(Tail0, Tail1).

% build dependency graph
insert_edge1(Name, Rhs) :- appears(Rhs, X),
                          not(edge1(Name, X)),
                          not(Name = X),
                          assert(edge1(Name, X)),
                          fail.

insert_edge1(Name, Rhs).
insert_upd1(Preds) :- updat(Preds),
                     Preds =..Lpreds,
                     Lpreds = [Name|_],
                     assert(upd(Name)), fail.

insert_upd1(Preds).

% determine nodes for dependency graph
appears(conc(L), X) :- L = [Head|_],
                       appears(Head, X).

appears(conc(L), X) :- L = [_|Tail],
                       appears(conc(Tail), X).

appears(conc1(L), X) :- L = [Head|_],
                        appears(Head, X).

appears(conc1(L), X) :- L = [_|Tail],
                        appears(conc1(Tail), X).

appears(seq(L), X) :- L = [Head|_],
                      appears(Head, X).

appears(seq(L), X) :- L = [_|Tail],
                      appears(seq(Tail), X).

appears(isolate1(E), X) :- !, appears(E, X).

appears(isolate(E), X) :- !, appears(E, X).

appears(ins(X), Y) :- !, X =..Pred,
                      Pred = [Name|_],
                      Y = ins(Name).

appears(del(X), Y) :- !, X =..Pred,
                      Pred = [Name|_],
                      Y = del(Name).

appears(P, Y) :- P =..Pred,
                 Pred = [Name|_],
                 not(Name = conc),
                 not(Name = conc1),
                 not(Name = seq),
                 not(Name = isolate),
                 not(Name = isolate1),
                 atom(Name),
                 Y = Name.

% find connected nodes in dependency graph
reachable1(P, P).
reachable1(P, Q) :- edge1(P, Q).
reachable1(P, Q) :- edge1(P, R), reachable1(R, Q).

% query sets definition
qref1(Q, Q) :- Q =..Pred,
               Pred = [Name|_],
               upd(Name).
qref1(E, Q) :- appears(E, P), reachable1(P, Q), upd(Q).

qref2(E, Q) :- appears(E, P), reachable1(P, Q).

% insert sets definition
iref1(ins(Q), Q).
iref1(E, Q) :- appears(E, P), reachable1(P, ins(Q)).

% delete sets definition
dref1(del(Q), Q).
dref1(E, Q) :- appears(E, P), reachable1(P, del(Q)).

% cleanup before the algorithm begins
remove_upd1 :- retract(upd(X)), fail.
remove_upd1.

remove_db_pred :- retract(db_pred(X)), fail.
remove_db_pred.

% delete any previously created dependency graph
remove_graph1 :- retract(edge1(X, Y)), fail.
remove_graph1.

remove_dep1 :- retract(dep(1)).
remove_dep1.

remove(K) :- retract(dep(K)).
remove(K).

% create file names for input object files
in_opt2(FileIn,FileOut) :-
        name(FileIn,NameIn),
        append(NameIn,".opt1.o",NameOut),
        name(FileOut,NameOut).

% create file names for output optimized files
out_db(FileIn,FileOut) :-
        name(FileIn,NameIn),
        append(NameIn,".opt2.db",NameOut),
        name(FileOut,NameOut).

out_opt2(FileIn,FileOut) :-
        name(FileIn,NameIn),
        append(NameIn,".opt2.o",NameOut),
        name(FileOut,NameOut).

% load optimized rules in database
opt_insert_file(FileName) :-
        seeing(X),
        see(FileName),
        opt_read_and_add_line,
        see(X).

opt_read_and_add_line :-
        read(Line),
        (
        (Line == end_of_file)   -> true
        ;
         assert(Line),
        opt_read_and_add_line ).

% build new predicate names for database optimized file
create_trans_db(monitor(Lhs), monitor(NLhs)) :-
        not(islist(Lhs)),
        Lhs =..X,
        X = [Head|Tail],
        not(Head = conc),
        not(Head = conc1),
        not(Head = seq),
        not(Head = isolate),
        not(Head = isolate1),
        name(Head, NameHead),
        append(NameHead,"_ctr_db", OutHead),
        name(NewName, OutHead),
        Y = [NewName|Tail],
        NLhs =..Y.

create_trans_db(Lhs, NLhs) :-
        not(islist(Lhs)),
        Lhs =..X,
        X = [Head|Tail],
        not(Head = conc),
        not(Head = conc1),
        not(Head = seq),
        not(Head = isolate),
        not(Head = isolate1),
        name(Head, NameHead),
        append(NameHead,"_ctr_db", OutHead),
        name(NewName, OutHead),
        Y = [NewName|Tail],
        NLhs =..Y.

% translate rules for database optimized file
create_ctr_list([], []).
create_ctr_list(PR, NPR) :- flatten(PR, LR),
                            LR = [Head|Tail],
                            assert(db_pred(Head)),
                            NPR = [Head1|Tail1],
                            create_trans_db(Head, Head1),
                            create_ctr_list(Tail, Tail1).

write_list([]).
write_list(L) :- L = [Head|Tail],
                 create_trans_db(Head, NHead),
                 write(NHead),
                 (Tail = [],
                  write('.')
                  ;
                  write(',')),
                 L1 = Tail,
                 write_list(L1).

%write new rules in optimized database file
write_list_pred_rules([]).
write_list_pred_rules(L) :- L = [Head|Tail],
                            updat(Head),
                            create_trans_db(Head, NHead),
                            write(NHead),
                            write(' :- '),
                            write(Head),
                            write('.'),
                            nl,
                            L1 = Tail,
                            write_list_pred_rules(L1).

write_list_pred_rules(L) :- L = [Head|Tail],
                            not(updat(Head)),
                            L1 = Tail,
                            write_list_pred_rules(L1).

write_list_pred_trans([]).
write_list_pred_trans(L) :- L = [Head|Tail],
                            updat(Head),
                            create_trans_db(Head, NHead),
                            write(trans(NHead)),
                            write(' :- '),
                            write(Head),
                            write('.'),
                            nl,
                            L1 = Tail,
                            write_list_pred_trans(L1).

write_list_pred_trans(L) :- L = [Head|Tail],
                            not(updat(Head)),
                            L1 = Tail,
                            write_list_pred_trans(L1).

islist([]).
islist([_|_]).

conv([], [],_).

conv([First|Tail], NewList, Functor) :-
      (not(Tail = []),
       NewList =..[Functor, First, NewTail]
       ;
       NewList = First,
       NewTail = []),
      conv(Tail, NewTail, Functor).

conv_one([El], NewEl, F1) :-
   NewEl =..[F1, El].

flatten([], []).
flatten([Head|Tail], FlatList) :- flatten(Head, FlatHead),
                                  flatten(Tail, FlatTail),
                                  append(FlatHead, FlatTail, FlatList), !.
flatten(X, [X]).

check_ref(Lhs) :- iref1(Lhs,_).
check_ref(Lhs) :- dref1(Lhs,_).

check_pred(Name) :- db_pred(Name).
check_pred(Name) :- db_pred(Y),
                    qref2(Y, Name).

edge1(dummy, dummy) :- fail.
modifiable(dummy) :- fail.
upd(dummy) :- fail.
dep(dummy) :- fail.
db_pred(dummy) :- fail.