/* ************************************************************************ */
/* PLEASE DO NOT MODIFY THE CODE BELOW unless you understand how this
interpreter works and how it is used to execute KnowledgeSeeker rules in
Prolog.  This file contains the KnowledgeSeeker rule interpreter 
for Edinburgh/Dec-10 style Prolog.  This file is included with the rules
that are generated under the Prolog rule generation facility. */


/* predict is the predicate to call to execute the rule interpreter.
	eg. predict([value('Sex', male), value(education, 12), value(region, ontario)], PredictedPayRate).  */
predict(Attrs, Prediction) :-
	find_matching_rules(Attrs, Rules),
	/* apply meta-rules to select the most appropriate rule to fire */
	apply_meta_rules(Rules, Rule),
	rule_conclusion(Rule, Prediction).


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


apply_meta_rules(Rules, Rule) :-
	most_specific_rule(Rules, Rule).

/* As a general meta-rule, select the most specific rule. */
most_specific_rule(Rules, Rule) :-
	/* all rules (including the universal rule) have a premise longer than -1 */
	longest_premise(Rules, Rule, null_rule, -1).

longest_premise([Rule|RestRules], Answer, _, OldLength) :-
	rule_premise(Rule, Premise),
	listlen(Premise, Length),
	Length > OldLength,
	longest_premise(RestRules, Answer, Rule, Length).
longest_premise([_|RestRules], Answer, OldLongest, OldLength) :-
	longest_premise(RestRules, Answer, OldLongest, OldLength).
longest_premise([], Answer, Answer, _).

/* the usual list length routine */
listlen([], 0).
listlen([_|Rest], Len) :-
	listlen(Rest, RestLen),
	Len is RestLen + 1.

/* find all Rules which match the given Attrs, i.e. all the rules that could
possibly be fired */
find_matching_rules(Attrs, Rules) :-
	do_find_rules(Attrs, [], Rules).

do_find_rules(Attrs, CurrentlyFound, AllRules) :-
	find_matching_rule(Attrs, NewRule),
	cwa_not(rule_member(NewRule, CurrentlyFound)),
	do_find_rules(Attrs, [NewRule|CurrentlyFound], AllRules).
do_find_rules(_, CurrentlyFound, CurrentlyFound).

/* the standard close-world assumption prolog not; negation as failure */
cwa_not(G) :-
	call(G), !,
	fail.
cwa_not(_).


rule_member(Rule, [FirstRule|_]) :-
	rule_id(Rule, Id),
	rule_id(FirstRule, Id).
rule_member(Rule, [_|RestRules]) :-
	rule_member(Rule, RestRules).	

find_matching_rule(Attrs, Rule) :-
	is_a_ks_rule(Rule),
	rule_premise(Rule, Conditions),
	all_rule_attrs_satisfied(Attrs, Conditions).

all_rule_attrs_satisfied(_, []).
all_rule_attrs_satisfied(Attrs, [Condition|RestOfConditions]) :-
	condition_satisfied(Attrs, Condition),
	all_rule_attrs_satisfied(Attrs, RestOfConditions).

is_a_ks_rule(Rule) :-
	ks_rule_base(Rule).

/* the rule structure -- condition and conclusion pair */
rule_conclusion(rule(_, _, Conclusion), Conclusion).
rule_premise(rule(_, Conditions, _), Conditions).
rule_id(rule(Id, _, _), Id).

condition_satisfied([Attr|_], Condition) :-
	attr_condition_match(Attr, Condition).
condition_satisfied([_|RestOfAttrs], Condition) :-
	condition_satisfied(RestOfAttrs, Condition).

attr_condition_match(Attr, Condition) :-
	bind_attr(Attr, Condition),
	evaluate_condition(Condition).

bind_attr(value(AttrId, Attr), condition(var_info(AttrId, Attr), _)).

evaluate_condition(condition(_, ConditionCode)) :-
	call(ConditionCode).

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

