% min,max functions
min(N1, N2, N) :- N1 =< N2, N is N1, !; N2 < N1, N is N2, !.
max(N1, N2, N) :- N1 >= N2, N is N1, !; N2 > N1, N is N2, !.

% delete from list function.
del(X, [X|T], T).
del(X, [Y|T], [Y|T1]) :- del(X, T, T1).

%Fuzzification functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%Takes form [[Linguistic Variable, crisp input], [2nd linguistic variable, crisp input], ...
%	     [nth linguistic variable, crisp input]].

fuzzify([],[]).
fuzzify([[LING_VAR, CRISP_VALUE] | T], [[LING_VAR | FUZZY_MEMBERSHIP_LIST] | T2] ) :- 
A =.. [LING_VAR, L], A, fuzzymem_list(L, CRISP_VALUE, FUZZY_MEMBERSHIP_LIST),
fuzzify(T,T2).

fuzzymem_list([H], CV, [[H,H1]]) :- B =.. [H, H1, CV], B, !.
fuzzymem_list([H|T], CV, [[H,H1] | T1]) :-  B =.. [H, H1, CV], B, 
				        fuzzymem_list(T, CV, T1).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Clipping
clip([FUZZY_VALUE, CRISP_VALUE], MuClip, Mu) :- X =.. [FUZZY_VALUE, Mu, CRISP_VALUE], X, Mu < MuClip, !.
clip([FUZZY_VALUE, CRISP_VALUE], MuClip, MuClip) :- X =.. [FUZZY_VALUE, Mu, CRISP_VALUE], X, Mu >= MuClip, !.

% Scaling
scale([FUZZY_VALUE, CRISP_VALUE], MuClip, Mu2) :- X =.. [FUZZY_VALUE, Mu, CRISP_VALUE], X, Mu2 is Mu*MuClip , !.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Limiting list to single entries for outputs with mu values

distinct_values_in_lists(L1) :- member([X,Val1], L1), member([X,Val2], L1), Val1 =\= Val2.
crop_list(L1, L1) :- not(distinct_values_in_lists(L1)), !.
crop_list(L1, L3) :- member([X,Val1], L1), member([X,Val2], L1), Val1<Val2, del([X,Val1],L1,L2), 
			 crop_list(L2,L3), !.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Operators if, and, or, then for use in defining rules
:- op(150, xfy, and).
:- op(150, xfy, or).
:- op(400, fx, if).
:- op(250, xfy, then).

get_fuzzy_value(A, L, VALUE) :-
A =.. [LV, FUZZY_VALUE], 
member([LV|T],L), member([FUZZY_VALUE,VALUE],T).

get_fuzzy_value(A and B, L, VALUE) :-
get_fuzzy_value(A, L, VAL1), get_fuzzy_value(B, L, VAL2),
min(VAL1, VAL2, VALUE).

get_fuzzy_value(A or B, L, VALUE) :-
get_fuzzy_value(A, L, VAL1), get_fuzzy_value(B, L, VAL2),
max(VAL1, VAL2, VALUE).

%Rules to be written in the form "if ANTECEDENTS then CONSEQUENCE."

apply_rule(if A then C, L, VALUE, C) :-
if A then C, get_fuzzy_value(A, L, VALUE).

rule_consequence([C,VALUE],L) :- apply_rule(_, L, VALUE, C).


%%%%%%%%%%%%%% Aggregation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

get_agg_value([[X,MuClip]], CRISP_VALUE, Mu, CorS) :- X =.. [_,FUZZY_VALUE],
 					     T =.. [CorS, [FUZZY_VALUE, CRISP_VALUE], MuClip, Mu], T, !.

get_agg_value([[X,MuClip]|T],CRISP_VALUE, Mu, CorS) :- X =.. [_,FUZZY_VALUE],
					     Q =.. [CorS,[FUZZY_VALUE, CRISP_VALUE], MuClip, Mu1], Q,
					     get_agg_value(T, CRISP_VALUE, Mu2, CorS), max(Mu1,Mu2,Mu).

area(_, R1, R2, _, 0, _) :- R1 >= R2, !.
area(L, R1, R2, STEP, AREA, CorS) :- R11 is (R1 + STEP), get_agg_value(L, R1, Mu, CorS), AREA1 is (Mu*STEP),
				 area(L, R11, R2, STEP, AREA2, CorS), 
				 AREA is (AREA1 + AREA2).

warea(_, R1, R2, _, 0, _) :- R1 >= R2, !.
warea(L, R1, R2, STEP, WAREA, CorS) :- R11 is (R1 + STEP), get_agg_value(L, R1, Mu, CorS), WAREA1 is (Mu*STEP*(R1+STEP/2)),
				 warea(L, R11, R2, STEP, WAREA2, CorS), 
				 WAREA is (WAREA1 + WAREA2).

cog(L, R1, R2, STEP, CorS, COG) :- area(L, R1, R2, STEP, AREA, CorS), warea(L, R1, R2, STEP, WAREA, CorS), 
			     COG is (WAREA/AREA).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%% Putting it all together %%%%%%%%%%%%

fuzzify_and_apply_rules(INPUTS, L1) :-
fuzzify(INPUTS, L), setof(RC, rule_consequence(RC,L), L2), crop_list(L2,L1).

% Enter INPUTS in the form
% L = [[linguistic variable, crisp value], ... ]
% CorS = Clip or Scale Answer
% START = start point for output
% END = end point for output
% STEP = steps to take for riemann integration
% COG = resultant centre of gravity

fuzzify_ruleevaluate_aggregate_defuzzify(INPUTS, CorS, START, END, STEP, COG) :-
fuzzify_and_apply_rules(INPUTS, L), cog(L, START, END, STEP, CorS, COG).

end_of_file.