% ===================================
% Deklaratív Programozás 3. gyakorlat
% ===================================

% I. rész: Vágó és vezérlési eljárások
% ------------------------------------

:- use_module(library(lists)).

% fogyo_prefix(L, P): P az L nem üres számlista lehető leghosszabb olyan prefixuma 
% (kezdőszelete), amely szigorúan monoton csökkenő.

% GyV: Az alábbi négy változat közül elég 2-3-t elmondani, feliratni.
% GyV: Ugyanez vonatkozik (arányosan) a többi többváltozatos példára

% Feltételes szerkezettel.
fogyo_prefix1([X|L], [X|P]) :-
	(   L = [Y|_], X > Y ->  fogyo_prefix1(L, P)
	;   P = []
	).

% Ugyanez vágóval.
fogyo_prefix2([X,Y|L], P) :-
	X > Y, !, P = [X|P1], fogyo_prefix2([Y|L], P1).
fogyo_prefix2([X|_], [X]).

% Ugyanez "hacker" megoldással, append megoldássorendje számit
fogyo_prefix3(L, P) :-
	append(P, R, L),
	last(P, X),
	(   R = []
	;   R = [Y|_], X =< Y
	),
	!.

% Egy kicsit egyszerűbb "hacker" megoldás
fogyo_prefix4(L, P) :-
	append(P, R, L),
	last(P, X),
	\+ (R = [Y|_], X > Y),
	!.

% II. rész: Univ
% --------------
% reszkif(Kif, R): Kif részkifejezése az R
% Megjegyzés: Részkifejezésként mindig megengedjük a teljes kifejezést,
% kivéve ha _valódi_ részkifejezésről beszélünk.
reszkif1(Kif, Kif).
reszkif1(Kif, R) :-
	compound(Kif),
	Kif =.. [_|Args],
	member(A, Args),
	reszkif1(A, R).

% | ?- reszkif1(f(a,[1,3]), R).
%    R = f(a,[1,3]) ? ; R = a ? ; R = [1,3] ? ; R = 1 ? ;
%    R = [3] ? ; R = 3 ? ; R = [] ? ; no

reszkif2(Kif, Kif).
reszkif2(Kif, R) :-
	compound(Kif),
	functor(Kif, _, N),
	between(1, N, I),
	arg(I, Kif, A),
	reszkif2(A, R).

% between(+N, +M, I): N =< I =< M, I egész (ahol N, M egészek).
between(N, M, N) :-
	N =< M.
between(N, M, I) :-
	N < M,
	N1 is N+1,
	between(N1, M, I).

% int_inside(X, I): Az X kifejezés részkifejezése az I egész szám.
int_inside1(X, I) :-
	(   integer(X) -> X = I
	;   compound(X),
	    X =.. [_|Args],
	    member(A, Args),
	    int_inside1(A, I)
	).

% | ?- int_inside1(f(a,[1,3]), I).
%    I = 1 ? ; I = 3 ? ; no


int_inside2(X, I) :-
	reszkif2(X, I), integer(I).

% all_ints(X): Az X kifejezés összes egyszerű (nem-struktúra)
% részkifejezése egész szám.

all_ints1(X) :- integer(X).
all_ints1(X) :- compound(X),
	X =..[_|Args],
	args_all_ints(Args).

% args_all_ints(L): Az L lista minden X elemére fennáll az all_ints(X) 
% feltétel, azaz minden X elem minden egyszerű részkifejezése egész szám.
args_all_ints([]).
args_all_ints([X|L]) :-
	all_ints1(X), args_all_ints(L).

all_ints2(X) :-
	integer(X).
all_ints2(X) :-
	compound(X), X =.. [_|Args],
	\+ (   member(A, Args), \+ all_ints2(A)
	   ).
        % forall(member(A, Args), all_ints2(A)).

% GyV: A forall szerepelt az előadason!
% P minden megoldására teljesül Q.
forall(P, Q):-
	\+ ( P, \+ Q).

all_ints3(X) :-
	forall((reszkif2(X, R),\+compound(R)), integer(R)).

%    Írjon Prologban egy eljárást, amely egy tetszőleges Prolog kifejezésben
%    minden egész számot eggyel nagyobbra cserél. Vigyázat: a kifejezésben változók
%    is előfordulhatnak!

%    % novelt(+Kif, ?Kif1): Kif1 ugyanolyan Prolog kifejezés mint Kif, csak
%    % minden egész szám helyett az eggyel nagyobb szám szerepel benne.

%    Példa: ?- novelt(f(1, X, 2, [3,4.0]), Kif1).   
%    válasz:	Kif1 = f(2, X, 3, [4,4.0])

novelt(Kif, NKif) :-
	integer(Kif), !, NKif is Kif+1.
novelt(Kif, NKif) :-
	compound(Kif), !,
	Kif =.. [F|Args],
	lista_novelt(Args, NArgs),
	NKif =.. [F|NArgs].
novelt(Kif, Kif).

lista_novelt([], []).
lista_novelt([K|L], [NK|NL]) :-
	novelt(K, NK),
	lista_novelt(L, NL).


%    Írjon olyan Prolog eljárást, amely egy adott atomban keres egy abban
%    előforduló legalább kétkarakteres olyan folytonos részt, amely csupa
%    kisbetűből áll, és maximális, azaz egyik irányban sem terjeszthető ki
%    kisbetűvel!  Visszalépéskor legyen hajlandó az összes ilyen részatomot
%    felsorolni!				           

%    % szava(Atom, Szo): Az Atom atomnak a Szo atom egy olyan folytonos része,
%    % amely csupa kisbetűből áll, maximális, és legalább kétbetűs.
%    % :- pred szava(atom::in, atom::out, int::out).

%    | ?- szava('a gyogy* 6ott', S).
% 				      S = gyogy; S = ott; no

szava1(Atom, Szo) :-
	atom_codes(Atom, L),
	kodlista_szava(L, SzL),
	atom_codes(Szo, SzL).

% kodlista_szava(KL, SL): a KL kódlistának az SL lista egy maximális, legalább
% kételemű, csupa kisbetűből álló folytonos része.
kodlista_szava([K|L], SzL) :-
	kisbetu(K), 
	max_szo(L, SzL1, M), !,
	(   SzL = [K|SzL1]
	;   kodlista_szava(M, SzL)
	).
kodlista_szava([_|L], SzL) :-
	kodlista_szava(L, SzL).

% K egy kisbetű kódja.
kisbetu(K) :-
	K >= 0'a, K =< 0'z.

% max_szo(L, SzL, M): Az L kódlista elején levő maximális hosszúságú,
% legalább egyelemű kisbetűsorozat SzL, az L fennmaradó része M.
max_szo([K|L], [K|SzL], M) :-
	kisbetu(K),
	(   max_szo(L, SzL, M) -> true
	;   SzL = [], M = L
	).

szava2(Atom, Szo) :-
	atom_codes(Atom, L),
	SzL = [_,_|_],
	append(L1, L2, L),
	append(SzL, L3, L2),
	forall(member(X,SzL), kisbetu(X)),
	\+ (last(L1, K), kisbetu(K)),
	\+ (L3 = [K|_], kisbetu(K)),
	atom_codes(Szo, SzL).

%    Az alábbi változat csak abban tér el az előzőtől, hogy a megtalált szó
%    indexpozicióját is kiadja.
%    Írjon olyan Prolog eljárást, amely egy adott atomban keres egy abban
%    előforduló legalább kétkarakteres olyan folytonos részt, amely csupa
%    kisbetűből áll, és maximális, azaz egyik irányban sem terjeszthető ki
%    kisbetűvel!  Az eljárás adja ki a fenti feltételeknek megfelelő részatomot, 
%    és annak kezdő indexpozicióját is (1-től számozva)! Visszalépéskor 
%    legyen hajlandó az összes ilyen részatomot felsorolni!				           

%    % szava(Atom, Szo, Index): Az Atom atomban az Index kezdőpozición a Szo
%    % áll, amely csupa kisbetűből álló maximális, legalább kétbetűs atom.
%    % :- pred szava(atom::in, atom::out, int::out).

%    | ?- szava('a gyogy* 6ott', S, I).
% 				      I = 3, S = gyogy; I = 11, S = ott; no

szava1(Atom, Szo, I) :-
	atom_codes(Atom, L),
	kodlista_szava(L, SzL, 1, I),
	atom_codes(Szo, SzL).

% kodlista_szava(KL, SL, I0, I): a KL kódlistának az SL lista egy maximális, 
% maximális, legalább kételemű, csupa kisbetűből álló, az (I-I0+1)-edik pozíción
% kezdődő folytonos része (a lista első eleme az 1. pozíción van).
kodlista_szava([K|L], SzL, I0, I) :-
	kisbetu(K), 
	max_szo(L, SzL1, M), !,
	(   SzL = [K|SzL1], I = I0
	;   length(SzL1, Len), 	I1 is I0+Len+1,
	    kodlista_szava(M, SzL, I1, I)
	).
kodlista_szava([_|L], SzL, I0, I) :-
	I1 is I0+1,
	kodlista_szava(L, SzL, I1, I).

szava2(Atom, Szo, I) :-
	atom_codes(Atom, L),
	SzL = [_,_|_],
	append(L1, L2, L),
	append(SzL, L3, L2),
	forall(member(X,SzL), kisbetu(X)),
	\+ (last(L1, K), kisbetu(K)),
	\+ (L3 = [K|_], kisbetu(K)),
	length(L1, I0),
	I is I0+1,
	atom_codes(Szo, SzL).
	
%     Írjon Prologban egy olyan eljárást, amely egy tetszőleges Prolog struktúra-
%     kifejezés összes olyan valódi részstruktúráját felsorolja, amelynek neve
%     megegyezik az egész kifejezés nevével. Nem számít valódi részstruktúrának
%     maga az egész kifejezés, és a benne előforduló atomok. Vigyázat, a
%     kifejezésben változók is előfordulhatnak!						
    
%     % nevu_resze(Kif, Resz): Resz a Kif-nek olyan valódi részstruktúrája,
%     % amelynek neve megegyezik Kif nevével.
%     % :- pred nevu_resze(any::in, any::out).

%     Példa: 
%     | ?- nevu_resze(f(1,f(2), f(f(X)), g(f)), R).   
%                                            R = f(2); R = f(f(X)); R = f(X)


nevu_resze(K, R) :-
	compound(K), functor(K, Nev, _),
	nevu_resze(K, Nev, R).

% K egy struktúrakifejezés, R annak valódi részstruktúrája, melynek neve Nev.
nevu_resze(K, Nev, R) :-
	K =.. [_|Args],
	member(Arg, Args), compound(Arg), 
	(   functor(Arg, Nev, _), R = Arg
	;   nevu_resze(Arg, Nev, R)
	).

