/*
Nepochopil som presne z tvojej spravy, kde chces aby sa
volala procedura error_parsing, ci
(1) po prvom volani DEEM pred allow_all_edges
(2) po prvom volani DEEM po  allow_all_edges
(3) po druho volani DEEM, tj. uplne na zaver
Ja som ju dal na (3) - skontroluj !
*/
 

% interpret of deterministic machine

/* last change(s)
   20. 1.1995 odladene
   16. 1.1995 viacnasobny vystup
   19.12.1994 ladenie - chyby 1,2
   16.12.1994 operator \= pre sicstus
   15.12.1994 v1.1
   25.11.1994 po schodzke
   24.11.1994

chyby:
5) 20.1.95 nefunguje 'hezke2 a bohate2 di2vky' - asi tretia faza DEEM, 
   ktora ma vychadzat z povodneho vstupu, nedostane dobre data v db. 

4) 20.1.95 viacnasobne sa pyta, 'start_parsing' sa splni viackrat (?)

3) 16.1.1995 viacnasobny vystup reklamovany Rosenom.
Pravdepodobny dovod: Jager nemazal moje informacie o hranach, pretoze 
prislusna procedura clean_db/0 nebola popisana v interface. 
Navrh odstranenia: procedura clean_db/0 sa musi volat
pred novym vstupom (resp. pred prvym volanim init_info_edge 
pre novy vstup)
- doplneny popis interface

1) 
make_context_1 je volana v mode (-,?) vynimocne, spravne ma byt (+,-),
ale pretoze nepoznam mena pravidiel aktivnych v danej faze, je prvy 
argument volny. Dochadza tam k falosnej substitucii, kedy sa za 
volnu premennu dosadi prazdny zoznam.
Odstranenie: davat tam platny zoznam (rosen) alebo pocitat s volnou
premennou. Platny zoznam znamena definovat korektne predikat 
phase_rules/2. 

2) 
compose_edge nefunguje na "hezkou di2vkou" 
*/


% ?? znamena este nedokoncene, prip. nejasne
% ! Prezrite si poznamky na konci

?- op( 900 , xfx , isset).

% phases of DS.
/* % structures:
% phases_ds( Name, List_of_phases).
% phase_rules(Phase_identifier, List_of_rules).
Ex.:
phases_ds(ds1, [adj_completition, noun_completition]).
phase_rules( adj_completition, [adj_rule_1, adj_rule_2, ...]).
phase_rules( noun_completition, [...]).
% adj_rule_1 je meno pravidla.
*/

phases_ds(ds1, [noun_completion]).
phase_rules( noun_completion, [nucleus_expansion_for_nouns,
				phrase_termination_for_nouns,
				adjective_NP,
				numeral_NP,
				preposition_NP]).



% main predicate called by PJ
start_all_parsing :-   /* without machine name yet */
        get_input_nodes(Nodes),   /* externally */
        start_DS_phases(ds1,Nodes),       /* all phases of DS */
        change_constituent_types, /* preparation */
        start_DEEM_parsing,            /* DEEM */
        ( exist_final_edge ->     % ?? doplnit
          true
        ; allow_all_edges,        /* recovering all starting edges */
          start_DEEM_parsing,     /* DEEM for all edges */
          error_parsing           /* kontrola chyb v1.1 */
        ).

%phases_ds( ds1, [phase1]). /* popis stroja */
%phase_rules( phase1, [_,_] ).  /* popis phase */ 
  /* zatial berie vsetko, co je v DB ?? */

start_DS_phases(Name, Nodes):-
        phases_ds(Name, Phase_list),
        do_phases(Phase_list, Nodes).

do_phases([],_).
do_phases([Phase|Phases], Nodes):-
        set_phase_cxt(Phase),    /* also context */
        det_machine(Nodes,Nodes1),
        % get_active_nodes(Nodes,Nodes1), %% ??
        do_phases( Phases, Nodes1).

set_phase_cxt(Phase_name):-
        (retract(current_phase(_,_)), fail; true),     /* may not be in db */
        phase_rules(Phase_name, Phase_rules),
        make_context_1(Phase_rules,Cxt),
        assert( current_phase(Phase_name,Cxt)).
get_phase_cxt(Phase_name, Cxt):-
        current_phase(Phase_name,Cxt).

% meta-main:
% start_DS_phases/2 - for all phases

% main : det_machine( +Nodes, -Nodes)
det_machine( Nodes, Out_nodes):-
	Nodes=[N1,N2|Nodes1],		% start from 3rd node
	do_nodes(  [N1,N2], Nodes1, Out_nodes). % ?? co su vysledky?

% do_nodes/3 ( Done_nodes, Open_nodes, Out_nodes)
% takes first Node from Open_nodes, adds it to the end of Done_nodes
% and start HEAD expansion
% (corresponds to first cycle)
do_nodes(  Done, [], Done ).
do_nodes(  Done, [N|Nodes], Out_nodes ):-
	append( DoneX, [Last], Done),
	expand_head(  DoneX, [Last,N] ),
	append(Done, [N], Done1),
	pruning( Done1, Done2), 	% pruning (only global)
	do_nodes(  Done2, Nodes, Out_nodes). % only pruned nodes

% expand_head/2 ( +Open, +Done)
% (correspond to second, internal cycle)
expand_head(  [], _).
expand_head(  Open, Done):-
	append( Open1, [Last], Open), 	% from right to left
	Done1=[Last|Done],
	make_edges(  Done1, _Continue), 	% without local pruning,
		% Done don't change; may stop, if no new edges ??
	(  true   % ?? bezpecne ukoncenie ? Continue = yes
	-> expand_head(  Open1, Done1)
	;  true
	).

% make_edges/2 (  Nodes, Continue_cond)
make_edges(  Nodes, yes):-
	append(_, [Last], Nodes), 	% extract last node
	Nodes = [First|_],		% extract first node
        get_edge( E1, First, X),	% from First to X
	get_edge( E2, X,     Last),	% from X to Last
        get_phase_cxt(_,Cxt),           % Cxt from DB
	compose_edges(  E1, E2, ListE, Cxt), 	% DB changes
        \=( ListE, []), !,                 % at least one solution
        update_new_edges_info(ListE, E1,E2 ).
make_edges( _Nodes, no).


update_new_edges_info([], _E1,_E2).
% update_new_edges_info([d(E,Compl)|ListE], E1,E2 ):-
update_new_edges_info([E=Compl|ListE],E1,E2 ):- 
        make_new_edge_info(E,Compl, E1,E2 ),
        update_new_edges_info(ListE, E1,E2 ).

% make_new_edge_info/2
make_new_edge_info( E, Compl, E1,E2):-
	% standard information are asserted in DB
	put_info_edge( E, free /*Activity*/, Compl, E1, E2),
	tab_compl_act(Compl, Activity),
	update_parent(  Activity, E1),
	update_parent(  Activity, E2).

tab_compl_act(complete, used).
tab_compl_act(incomplete, restorable).

% update_parent(  Activity, E)
update_parent( _, none):-!.	% none is special edge
update_parent(  used, E):- !,	% E will be 'used' also
	get_info_edge(  E, Info),
	Info = d5(E, Act, _Compl, E1, E2),
	change_info_edge( E, used),
	(  Act = restorable
	-> update_parent(  used, E1),	% recursively
	   update_parent(  used, E2)
	;  true
	).
update_parent(  restorable, E):-	% E will be 'restorable', if not 'used'
	get_info_edge(  E, Info),
	Info = d5(E, Act, _Compl, _E1, _E2),
	(  Act = free 			% need to change
	-> change_info_edge( E, restorable)
	;  true				% without change
	).

pruning(  Done1, Done2):-
	append( _, [Last], Done1), Done1 = [First|_],  	% get First, Last,
	joined_to_start(  [], [First], Out1),
	joined_to_end(    [], [Last],  Out2),
	Done2 isset Out1 * Out2, %% intersection = connected to start AND end
	change_incomplete_edges,
	change_used_edges,
	% sort(Done2), !
	change_unconnected_edges( Done1, Done2). % but must be in Done1

% joined_to_start( Closed_nodes, Open_nodes, Output_nodes)
joined_to_start(  Closed, [], Closed).
joined_to_start(  Closed, [From|Open], Out):-
	(setof( To,
		E^get_edge( E, From, To),
		Nodes)
	; Nodes = []
	), !,
	Nodes1 isset Nodes - Open - Closed,
	Open1  isset Open + Nodes1,
        append(Closed,[From],Closed1),
	joined_to_start( Closed1, Open1, Out).

joined_to_end(  Closed, [], Closed).
joined_to_end(  Closed, [To|Open], Out):-
	(setof( From,
		E^get_edge( E, From, To),
		Nodes)
	; Nodes = []
	), !,
	Nodes1 isset Nodes - Open - Closed,
	Open1  isset Open + Nodes1,
	joined_to_end(  [To|Closed], Open1, Out).

change_incomplete_edges :-
	get_info_edge( E, Info),
	Info = d5(E, _Act, incomplete, _E1, _E2),
	change_info_edge(E, pruned),
        hide_edge(E),  /* schovanie u PJ */
	fail.
change_incomplete_edges.

change_used_edges :-
	get_info_edge( E, Info),
	Info = d5(E, used, _Compl, _E1, _E2),
	change_info_edge(E, pruned),
        hide_edge(E),  /* schovanie u PJ */
	fail.
change_used_edges.

change_unconnected_edges(Previous, Nodes) :-
	get_edge(E, From, To),
 	( member( From, Previous), member(To, Previous)
        ->
	  ( member( From, Nodes), member(To, Nodes)
	  -> true					% connected, no change
	  ;  change_info_edge(E, pruned),		% unconnected -> pruned
             hide_edge(E)  /* schovanie u PJ */
	  )
	; true ),
	fail.
change_unconnected_edges(_Previous, _Nodes).

%%% isset - set operation in Pascal fashion
X isset L       :- (L= [_|_];L=[]), !, X=L.
X isset A + B   :- !, XA isset A, XB isset B, set_union(XA,XB,X).
X isset A - B   :- !, XA isset A, XB isset B, set_difference(XA,XB,X).
X isset A * B   :- !, XA isset A, XB isset B, set_intersection(XA,XB,X).

set_intersection([],_,[]).
set_intersection([X|Xs],Ys,[X|Zs]):-
        member(X,Ys), !,
        set_intersection(Xs,Ys,Zs).
set_intersection([_|Xs],Ys,Zs):-
        set_intersection(Xs,Ys,Zs).

set_union([],Ys,Ys).
set_union([X|Xs],Ys,Zs):-
        member(X,Ys), !,
        set_union(Xs,Ys,Zs).
set_union([X|Xs],Ys,[X|Zs]):-
        set_union(Xs,Ys,Zs).

set_difference([],_,[]).
set_difference([X|Xs],Ys,Zs):-
        member(X,Ys), !,
        set_difference(Xs,Ys,Zs).
set_difference([X|Xs],Ys,[X|Zs]):-
        set_difference(Xs,Ys,Zs).

% initialization and cleaning
?- dynamic info_activity_edge/2,
	   info_edge/4.
?- dynamic current_phase/2.

init_info_edges:-
  get_edge(E, _, _),
  init_info_edge(E),
  fail.

init_info_edge(E):-
  put_info_edge(E, free, complete, none, none).

clean_db:-
	retractall(info_activity_edge(_,_)),
	retractall(info_edge(_,_,_,_)).

% mine edge interface
get_info_edge(  E, d5( E, Activity, Compl, Par1, Par2)):-
	info_activity_edge( E, Activity),
	info_edge( E, Compl, Par1, Par2).

put_info_edge( E, Activity, Compl, Parent1, Parent2):-
	assert(info_activity_edge( E, Activity)),
	assert(info_edge( E, Compl, Parent1, Parent2)).

change_info_edge(E, Act):-
	nonvar(Act), 			% must be done
	info_activity_edge(E, Act_old),
	( \=( Act, Act_old)
	-> retract(info_activity_edge(E, _)),
	   assert(info_activity_edge(E, Act))
	;  true
	).

l:-listing(info_edge),
   listing(info_activity_edge),
   listing(current_phase).

%% Kontext zavrhnuty !

/********************

Verze 2, 18.11.1995

Popis interface dohodnuteho s P.Jagerom

1. cast, ktoru tvori PJ a pouziva JH:

procedura: get_edge/3 (?Edge, ?From, ?To)
- k hrane to vyda pociatocny a koncovy vrchol alebo pri inych volaniach
- k vrcholu (jednomu z nich) to vracia postupne vsetky hrany,
ktore v nom zacinaju, resp. koncia.
- vydava postupne vsetky hrany
!!  Je vhodne , aby hrany, ktore dostavam, mali symbolicke oznacenia
(tj. male a kratke informacie). Ja s nimi nerobim nic ine, nez ze ich
predavam nasledujucej procedure:

procedura: compose_edges( +E1, +E2, -List, +Cxt)
z hran E1 a E2 vytvori zoznam vsetkych spolocnych hran do List.
  Polozky vysledneho zoznamu List maju tvar d(E, Compl), kde E
je hrana a Compl je informacia o kompletnosti 'complete/incomplete'.
  Argument Cxt je vstupna informacia vyzadovana PJ kvoli efektivnosti,
a obsahuje okrem aj informacie o aktivnych hranach. Jeho tvar
a vyrobenie viz make_context_1/2.
  ! Zahrna to aj pripad, ked sa E2 prevteli na novu
nelexikalnu hlavu a az potom sa spaja s E1.

procedure: make_context_1(+Seznam_pravidel, -Cxt)
naprogramuje PJ, Seznam_pravidel je obycajny zoznam. Cxt sa pouziva
iba v compose_edge a jeho strukturu pozna iba PJ.

procedure: get_input_nodes(-List). /* v.2 **
vyda cely utrideny seznam List vsech uzlu v chart-grafu, platny pro puvodni,
tj syntakticke hrany ze vstupu.

procedury pre (docasne) rusenie a obnovovanie hran v DS
procedure: hide_edge(E)
hrana E sa povazuje sa zrusenu alebo nevytvorenu (pre
nepouzitu nekompletnu).

procedure: allow_all_edges/0.
povoluje vsetky hrany pred druhym volanim  DEEM.

procedure: start_DEEM_parsing
-- zmenene 15.12.94 v1.1
spustenie DEEM na aktualny stav dhran v DB.

procedure: exist_final_edge/0
V DEEM testuje, ci existuje hrana zo zaciatku do konca.
V DS je nepotrebny

procedure: change_constituent_types/0
volana po ukonceni prace DS, pripravi nejake polozky v hranach
pre pracu DEEM

procedure: error_parsing/0            /* 15.12.94 kontrola chyb v1.1 **
volana po skonceni DEEM-analyzy na vstup predspracovany DS.
Vola sa pred "allow_all_edges" (OK ?)


2. cast, ktoru tvori JH a pouziva PJ:

// volanie metastroja
procedure: start_all_parsing/0 (/1?)
volane po nacitani potrebnych suborov do DB.
mozno casom .../1 s menom stroja.
robi: volanie DS; volanie DEEM na vysledok, volanie DEEM na vsetko.

// inicializacia mojich informacii o hranach
procedure: init_info_edges/0
Vtvorenie mojich informacii o vstupnych vsetkych hranach.

procedure: init_info_edge(E)
Pre jednu (typicky prave nacitanu) hranu E prida do DB moje
informacie o vstupnych hranach.

Z procedur init_info_edges/0 a init_info_edge(E) sa pouziva prave jedna.
Podla okolnosti si PJ zvoli vhodnu. (este nie je jasne, kde sa bude
volat - pri citani suboru alebo po nacitani vstupnych hran)

// cistenie mojej databaze
procedure: clean_db/0
pred pridanim informacii/hran o novej vete vymaze moje stare informacie
o hranach. Volat jeden krat pred novym spustenim parseru/vstupu vety.
*******

Otazky
- potrebujeme test na uspesny vypocet (aj v DS)
>
> Dva predikaty - jeden, ktery pro hranu zjisti zda tato je koncova
> (pokud by bylo jedno, pak napr. predpokladajici jiz zahrnutost celeho
> vstupu v teto hrane) a je -li poznamena to nekam (vhodny pouze pro
> volani v Ds, nebot DEEP uz v sobe neco podobneho ma), druhy ktery v
> kazdem case z poznamenanych informaci zjisti, zda takova hrana jiz
> byla vytvorena
Neslo by pouzit ten test z DEEM aj po praci DS. Hranove informacie by
predsa mali byt rovnake.
Ak ano -> zverejnit meno a argumenty.

zatial: $konec
uspeje ak, tam je koncova hrava a nemaju sa pustat dalsie casti systemu.


- tiskove vystupy ?
- zoznam hran, ktore zostali.
- mozno zoznam vsetkych zostrojenych hran.
- co vlastne vypisoval KO? To iste by malo stacit tu.

-- pozn:
- potrebujeme test na uspesny vypocet (aj v DS)
- seznam pravidel
- spytat sa na poradie vrcholov ???
- tiskove vystupy ?

*******************************************************

Stare navrhy -

//  (9.11) pouziva len hrany , ktore su povolene v danej faze
moze ich ziskat volanim
get_phase_list(Phase_name, List_of_active_rules).
-- Ine mozne riesenie:

Poziadavky na inicializaciu:

volat ?- init_info_edges.
alebo pre kazdu hranu E zavolat
?- put_info_edge(E, free, complete, none, none).
deklarovat (ak to je potreba)
?- dynamic info_activity_edge/2,
	   info_edge/4.

Poziadavky na interface:

ESTE: ostava dohodnut ??
- spojenie :
  - spravne volanie - so zoznamom vrcholov
  - vydanie vysledkov - zoznam hran platny po prechode det. automatu
- riadiaci system - ktory vola najprv DS, potom DEEM na vysledky DS
  a potom DEEM na vsetko
- mnoziny aktivnych pravidiel, fazy DS automatu

Update:

- pravidla musia mat svoje mena ( uzivatelske identifikatory), ktore sa
budu pouzivat pri popise, ku ktore faze pravidlo patri.

Koniec poziadavkov
-------------

v DB:
info_activity_edge(Edge, Activity)
info_edge(E, Compl, Parent1, Parent2)

Moje Info:
edge: for DEEM: Name, From, To, Desc
      for Ds:   activity (free, restorable, used, pruned)
		% free->used, free->restorable, restorable->used,
		% used->pruned, restorable->free, :incomplete->pruned
		completness (complete, incomplete) - no changes
		parent edges

Poznamky k algoritmu:
- pretoze pouzita hlava sa moze este pouzit v dalsom prechode
vnutorneho cyklu, musime rozlisovat pouzite a vyhodene hrany.
(alebo: pouzit sa moze pouzita hrana, len ak je to hlava
a konci v poslednom spracovavanom vrchole.)
- lokalne prerezavanie sa ukazalo ako nespravne, pretoze neodraza
globalnu ideu algoritmu, a to rozsirit hlavu co najviac dolava.
- ?? precislovavanie vrcholov - deje sa pri prerezavani
- ?? problem , ze hrana sa moze prevtelit na nelexikalnu hlavu by mal
riesit jager v compose_edge/4

komentare:
> > procedura: compose_edges( +E1, +E2, -List, +Cxt)
> > z hran E1 a E2 vytvori zoznam vsetkych spolocnych hran do List.
> >   ! Zahrna to aj pripad, ked sa E2 prevteli na novu
> > nelexikalnu hlavu a az potom sa spaja s E1.
> 
>  Prave zde bych se rad optal na lingvisticke pozadi - nebylo by 
--- To neni otazka pre mna.
> zahodno pro deterministicky automat zavest pravidlo minimalizace 
> nelexikality hlavy, tj. pokud existuje rozsireni, ktere nemusi za 
> hlavu brat celou frazi (E2), pak do vysledneho seznamu dat prave a 
> jen vsechna takovato rozsireni, neexistuje -li, pak seznam bude zas 
> obsahovat pouze rozsireni, kde hrana (pripadne fraze) E2 se vzala za 
> hlavu fraze nove.
> 
> priklad: E2 vypada:  x--x--x       E1 ma tvar z
>                      |  |
>                      y  X
>                      
>   a existuji pravidla: x-x  a take p-p, -p a p-p, kde x je hlavou 
>                        |           |         |
>                        z           z         x
> 
>   Potom by se vygenerovalo pouze  x--x--x--x
>                                   |  |  |
>                                   z  y  X
>   a jiz ne struktura: p--p--------p
>                       |  |
>                       z  x--x--x
>                          |  |
>                          y  X
> 

> > -- pozn:
> > - potrebujeme test na uspesny vypocet (aj v DS)
> 
> Dva predikaty - jeden, ktery pro hranu zjisti zda tato je koncova 
> (pokud by bylo jedno, pak napr. predpokladajici jiz zahrnutost celeho 
> vstupu v teto hrane) a je -li poznamena to nekam (vhodny pouze pro 
> volani v Ds, nebot DEEP uz v sobe neco podobneho ma), druhy ktery v 
> kazdem case z poznamenanych informaci zjisti, zda takova hrana jiz 
> byla vytvorena
> 
> > - seznam pravidel
> 
> Budu moci pro kontrolu vratit seznam pravidel prislych ze vstupu (zda 
> kazde existuje ..)
Mozno casom sa to bude hodit, zatial to nehodlam testovat.
> 
------ end v2
*/

