
%		      Deklaratív Programozás gyakorlat
%		     Prolog programozás: listák, gráfok
%		                MEGOLDÁSOK
% ---------------------------------------------------------------


% 1.  Beszúrás rendezett listába

%     % insert_ord(+RL0, +Elem, RL): Az RL monoton növő számlista úgy áll
%     % elő, hogy az RL0 szigorúan növő számlistába beszúrjuk az Elem számot,
%     % feltéve hogy Elem nem eleme az RL0 listának; egyébként RL = RL0.

%     | ?- insert_ord([1,3,5,8], 6, L).
%     L = [1,3,5,6,8] ? ;
%     no
%     | ?- insert_ord([1,3,5,8], 3, L).
%     L = [1,3,5,8] ? ;
%     no

%     Használjon feltételes szerkezetet!

% insert_ord(+RL0, +Elem, ?RL): Az RL monoton növő számlista úgy áll
% elő, hogy az RL0 szigorúan növő számlistába beszúrjuk az Elem számot,
% feltéve hogy Elem nem eleme az RL0 listának; egyébként RL = RL0.
insert_ord([], E, [E]).
insert_ord(L0, E, L) :-
	L0 = [X|L1],
	(   X > E -> L = [E|L0]
	;   X =:= E -> L = L0
	;   L = [X|L2],
	    insert_ord(L1, E, L2)
	).

% insert_ord_1(+RL0, +Elem, ?RL): ugyanaz mint
%   insert_ord(+RL0, +Elem, ?RL), de feltételes szerkezet használata
% nélkül. Kevésbé hatékony, mert választási pontot hagy maga után.
insert_ord_1([], E, [E]).
insert_ord_1([E|L0], E, [E|L0]).
insert_ord_1([X|L0], E, [E,X|L0]) :-
	X > E.
insert_ord_1([X|L0], E, [X|L]) :-
	X < E,
	insert_ord_1(L0, E, L).


% ---------------------------------------------------------------------------

% A 'graph' adatstruktúrát a következő Mercury-szerű típusdefiníciókkal
% definiáljuk: 

% % :- type graph == list(edge).
% % :- type edge ---> node-node.
% % :- type node == atom.

% Eszerint egy Prolog kifejezés a 'graph' típusba tartozik, ha X-Y alakú
% struktúrák listája, ahol X és Y névkonstansok (atomok).

% Az [a1-b1,a2-b2,...,an-bn] 'graph' típusú kifejezés azt az irányítatlan
% gráfot írja le, amelynek csomópontjai  a1,..,an,b1,...,bn, és 
% egy (irányítatlan) él vezet ai és bi között, minden i=1,...,n esetén.
% (Megjegyzés: az így megadott gráfoknak nyilván nem lehet izolált pontja.)

% Például az  [a-b,a-c], [a-c,b-a], [b-a,a-c], [c-a,a-b] stb.  mind
% ugyanazt a (matematikai értelemben vett) gráfot írják le.

% Az [a-b,a-c,b-c,b-d,b-e,c-d,c-e,d-e] kifejezés az alábbi gráfot írja le:

%               a
%              / \
%             /   \
%            /     \
%           b-------c
%           |\     /|
%           |  \ /  |
%           |  / \  |
%           |/     \|
%           d-------e

% Gyerekkorukban találkozhattak azzal a feladattal, hogy ezt a gráfot egy
% folytonos vonallal rajzolják meg.

% Egy 'graph' típusú [a1-b1,a2-b2,...,an-bn] Prolog listát folytonos
% vonalnak hívunk, ha b1=a2, b2=a3, ...,    b(n-1) = an.


% 2.  Írja meg az alábbi fejkommentnek  megfelelő  draw/2 Prolog eljárást

%     % draw(+G, -L): Az L folytonos vonal "megrajzolja" a  G gráfot, azaz az
%     % L folytonos vonal ugyanazt a matematikai értelemben vett gráfot írja
%     % le, mint a G Prolog kifejezés.

%     Tehát a "| ?- draw(G, L)." hívás, ahol G adott és L egy változó,
%     felsorolja L-ben az összes olyan folytonos vonalat, amely "megrajzolja"
%     G-t.

%     Törekedjék minél egyszerűbb megoldásra, nem kell a hatékonysággal
%     foglalkoznia. Használhatja a lists könyvtár eljárásait.

%     | ?- draw([a-b,a-c], L).
%     L = [b-a,a-c] ? ;
%     L = [c-a,a-b] ? ;
%     no
%     | ?- draw([a-b,a-c,b-c,b-d,b-e,c-d,c-e,d-e], L), L = [d-e|_].
%     L = [d-e,e-b,b-a,a-c,c-b,b-d,d-c,c-e] ? ;
%     L = [d-e,e-b,b-a,a-c,c-d,d-b,b-c,c-e] ? ;
%     L = [d-e,e-b,b-c,c-a,a-b,b-d,d-c,c-e] ? ;
%     L = [d-e,e-b,b-c,c-d,d-b,b-a,a-c,c-e] ? ;
%     L = [d-e,e-b,b-d,d-c,c-a,a-b,b-c,c-e] ? ;
%     L = [d-e,e-b,b-d,d-c,c-b,b-a,a-c,c-e] ? ;
%     L = [d-e,e-c,c-a,a-b,b-c,c-d,d-b,b-e] ? ;
%     L = [d-e,e-c,c-a,a-b,b-d,d-c,c-b,b-e] ? ;
%     L = [d-e,e-c,c-b,b-a,a-c,c-d,d-b,b-e] ? ;
%     L = [d-e,e-c,c-b,b-d,d-c,c-a,a-b,b-e] ? ;
%     L = [d-e,e-c,c-d,d-b,b-a,a-c,c-b,b-e] ? ;
%     L = [d-e,e-c,c-d,d-b,b-c,c-a,a-b,b-e] ? ;

%     no


:- use_module(library(lists), [select/3]).

% draw(+G, -L): Az L folytonos vonal "megrajzolja" a  G gráfot, azaz az
% L folytonos vonal ugyanazt a matematikai értelemben vett gráfot írja
% le, mint a G Prolog kifejezés.
draw(G, L) :-
	draw(G, _, L).

% draw(+G,  ?KP, -L):  Az L  folytonos vonal  a KP  kezdőpontból  indul és
% "megrajzolja"  a  G  gráfot,  azaz   az  L  folytonos  vonal  ugyanazt  a
% matematikai értelemben vett gráfot írja le, mint a G Prolog kifejezés.
draw([], _, []).
draw(G, P, [P-Q|L]) :-
	select_edge(P, Q, G, G1),
	draw(G1, Q, L).

% select_edge(P, Q, G, G1): 
% A G irányítatlan gráfból elhagyható a P-Q él és marad a G1 gráf.
select_edge(P, Q, G, G1) :-
	select(E, G, G1),
	(   E = P-Q
	;   E = Q-P
	).

% draw1(+G, -L): ugyanaz, mint draw/2, de csak egy
% segédeljárás használatával (select_edge/4)
draw1([], []).
draw1(G, [P-Q|L]) :-
	select_edge(P, Q, G, G1),
	(   G1 = [] -> L = []
	;   L = [Q-_|_],
	    draw1(G1, L)
	).

% draw2(+G, -L): ugyanaz, mint draw/2, csak segédeljárás nélkül.
draw2([], []).
draw2(G, [A-B|L]) :-
	select(E, G, G1),
	(   E = A-B
	;   E = B-A
	),
	(   G1 = [] -> L = []
	;   L = [B-_|_],
	    draw2(G1, L)
	).


% 3.  (Otthoni feladat)

%     Írjon Prolog eljárást egy gráf fokszámlistájának előállítására. A
%     fokszámlista típusa:

%     % :- type degrees == list(node_degree).
%     % :- type node_degree --> node - degree.
%     % :- type degree == int.

%     A fokszámlista tehát egy olyan lista, amelyenek elemei Cs-N alakú
%     párok, ahol Cs a gráf egy csomópontja, és N a Cs csomópont
%     fokszáma. A csomópontok tetszőleges sorrendben szerepelhetnek a
%     fokszámlistában.

%     % degree_list(G, Ds): A G gráf fokszámlistája Ds.

%     | ?- degree_list([b-a,a-c], Ds).
%     Ds = [b-1,a-2,c-1] ? ; no

% degree_list(G, Ds): A G gráf fokszámlistája Ds.
degree_list([], []).
degree_list([A-B|G], Ds) :-
	degree_list(G, Ds0),
	incr_node_degree(Ds0, A, Ds1),
	incr_node_degree(Ds1, B, Ds).

% incr_node_degree(Ds0, A, Ds): A Ds0 fokszámlistából A fokszámának eggyel
% való növelésével áll elő a Ds fokszámlista.
incr_node_degree([], A, [A-1]).
incr_node_degree([N-D|Ds0], A, Ds) :-
	(   A = N -> D1 is D+1, Ds = [A-D1|Ds0]
	;   Ds = [N-D|Ds1],
	    incr_node_degree(Ds0, A, Ds1)
	).

% degree_list2(G, Ds): A G gráf fokszámlistája Ds. (Jobbrekurzív változat)
degree_list2(G, Ds) :-
	degree_list2(G, [], Ds).

% degree_list2(G, Ds0, Ds): A G gráf fokszámlistáját Ds0 elé
% fűzve kapjuk Ds-t.
degree_list2([], Ds0, Ds0).
degree_list2([A-B|G], Ds0, Ds) :-
	incr_node_degree(Ds0, A, Ds1),
	incr_node_degree(Ds1, B, Ds2),
	degree_list2(G, Ds2, Ds).


% 4.  (Otthoni feladat)

%     Írjon idraw/2 néven egy Prolog eljárást, amelynek jelentése azonos a
%     2. feladatban szereplő draw/2 eljárással! Törekedjék minél hatékonyabb
%     megoldásra! Használhatja az ugraphs könyvtárat.  

:- use_module(library(ugraphs)).

idraw(G, L) :-
	vertices_edges_to_ugraph([], G, Graph),
	symmetric_closure(Graph, SGraph),
	reduce(SGraph, [_]),	% összefüggő a gráf
	degree_list2(G, Ds0),
	(   select(A-D1, Ds0, Ds1), D1 mod 2 =:= 1 ->
	    select(B-D2, Ds1, Ds2), D2 mod 2 =:= 1,
	    \+ ( member(_C-D3, Ds2), D3 mod 2 =:= 1 ),
	    (   L = [A-_|_]
	    ;   L = [B-_|_]
	    )
	;   true
	),
	draw(G, L).
	
% 5.  Írjon Prolog eljárást amely a bemenetként kezdődő listáról eldönti,
%     hogy egy platóval kezdődik-e, és ha igen, visszaadja a maximális plató
%     hosszát és az ezutáni (maradék) elemek listáját.
% 
%     % pl_kezdetu(L, Len, M): Az atomokból álló L lista egy Len hosszúságú
%     % maximális platóval kezdődik, amelyet az M maradéklista követ.
% 
%     | ?- pl_kezdetu([a,b,a,c,c,c,b,b], Len, M).
%     no
%     | ?- pl_kezdetu([c,c,c,b,b], Len, M).
%     Len = 3, M = [b,b] ? ; no
%     | ?- pl_kezdetu([b,b], Len, M).
%     Len = 2, M = [] ? ; no
%     | ?- pl_kezdetu([b], Len, M).
%     no
%     | ?- pl_kezdetu([], Len, M).
%     no
%     | ?-

% maxazonos(L0, M, A, N0, N): Az L0 lista elejerol leszedheto k db A es
% marad egy nem A-val kezdodo M, tovabba N=N0+k.
maxazonos(L0, M, A, N0, N) :-
    (   L0 = [A|L1] ->
        N1 is N0+1,
    	 maxazonos(L1, M, A, N1, N)
    ;   M = L0, N = N0
    ).

pl_kezdetu([A,A|L], Len, M) :-
    maxazonos(L, M, A, 2, Len).

% Kevésbé hatékony, de segédeljárás nélküli.
pl_kezdetu1([A,A], 2, []).
pl_kezdetu1([A|L], Len, M) :-
	L = [A|L1],
	L1 = [B|_],
	(  B = A -> pl_kezdetu1(L, Len1, M), Len is Len1+1
	;  Len = 2, M = L1
	).

% 6. Írjon olyan Prolog eljárást, amely felsorolja atomok egy adott
%    listájában található maximális platókat, megadva a plató hosszát és az
%    ismétlődő elemet.
% 
%    % plato(+L, ?Len, ?X): Az L listában található egy Len hosszúságú,
%    % X elemekből képzett maximális plató.
% 
%    | ?- plato([a,b,b,b,b,a,a,c,b,b], Len, X).
%    Len = 4, X = b ? ;
%    Len = 2, X = a ? ;
%    Len = 2, X = b ? ;
%    no

plato(L, Len, X) :-
    L = [X0|L1],
    (   pl_kezdetu(L, Len0, M) ->
	    (   X = X0, Len = Len0
	    ;   plato(M, Len, X)
	    )
    ;   plato(L1, Len, X)
    ).

:- use_module(library(lists),[last/2]).

plato2(L, Len, X) :-
	L2 = [X,X|_],
	append(L1, L23, L),
	append(L2, L3, L23),
	\+ L3 = [X|_],
	\+ last(L1, X),
	length(L2, Len).

	

% 
% 7. (Otthoni feladat)
% 
%    Az előző feladat kiterjesztéseként írjon olyan Prolog eljárást, amely
%    felsorolja atomok egy adott listájában található maximális platókat,
%    megadva a plató kezdőindexét (1-től számozva), hosszát és az ismétlődő
%    elemet. 
% 
%    % plato(L, I, Len, X): Az L listában az I-edik elemtől kezdődően 
%    % egy X elemekből képzett, Len hosszúságú maximális plató található.
% 
%    | ?- plato([a,b,b,b,b,a,a,c,b,b], I, Len, X).
%    I = 2, Len = 4, X = b ? ;
%    I = 6, Len = 2, X = a ? ;
%    I = 9, Len = 2, X = b ? ;
%    no


plato(L, I, Len, X) :-
    plato(L, 1, I, Len, X).


plato(L, I0, I, Len, X) :-
    L = [X0|L1],
    (   pl_kezdetu(L, Len0, M) ->
	    (   X = X0, Len = Len0, I = I0
	    ;   I1 is I0+Len0, plato(M, I1, I, Len, X)
	    )
    ;   I1 is I0+1, plato(L1, I1, I, Len, X)
    ).
