% -----------------------------------------------------------------------------
%  (C) Altran Praxis Limited
% -----------------------------------------------------------------------------
% 
%  The SPARK toolset is free software; you can redistribute it and/or modify it
%  under terms of the GNU General Public License as published by the Free
%  Software Foundation; either version 3, or (at your option) any later
%  version. The SPARK toolset is distributed in the hope that it will be
%  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
%  Public License for more details. You should have received a copy of the GNU
%  General Public License distributed with the SPARK toolset; see file
%  COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
%  the license.
% 
% =============================================================================

%###############################################################################
% PURPOSE
%-------------------------------------------------------------------------------
% Support facilities for applying any user defined proof rules to discharge
% VCs.
%###############################################################################

%###############################################################################
% MODULE
%###############################################################################

%:- module(userules,
%          [apply_user_defined_proof_rules/0]).

%###############################################################################
% DEPENDENCIES
%###############################################################################

:- use_module('data__formats.pro',
              [add_state/2,
               add_type/2]).

%###############################################################################
% TYPES
%###############################################################################

:- add_type('OperationType',
            [conc,
             rule_phase_inference,
             fact('FactId_Atom'),
             hyp('HypId_Atom')]).

:- add_type('RuleNameType',
            ['Filename_CharList : Rulename_CharList(Int)']).

:- add_type('RuleMatchType',
            [
                rewrite('Expr', 'ProvedConditionType_List'),
                inference('ProvedConditionType_List')
            ]).

:- add_type('ProvedConditionType',
            proved('Expr', 'HypId_Int_List', 'FactId_Int_List')).

:- add_type('RuleSortType',
            [user_rewrite_rule, user_inference_rule]).

%###############################################################################
% DATA
%###############################################################################

:- add_state(get_fact_from_rule,
             get_fact_from_rule('Expr',
                                'Int',
                                'OperationType',
                                'RuleNameType',
                                'RuleMatchType',
                                'RuleSort')).

:- add_state(get_candidate_fact,
             get_candidate_fact('Int',
                                'Expr')).

:- add_state(used_unique_reference,
             used_unique_reference('Atom',
                                   'Int')).
%###############################################################################
% PREDICATES
%###############################################################################








%===============================================================================
% apply_user_defined_proof_rules
%
% Three phases.
%===============================================================================

apply_user_defined_proof_rules :-
    i_am_using_rule(udr0),
    prune_all_fact_from_rule,
    prune_all_unique_references,
    retractall(proved_by_user_rules),
    fail.

%-------------------------------------------------------------------------------
% Phase 1
%-------------------------------------------------------------------------------

apply_user_defined_proof_rules :-
    i_am_using_rule(udr1),
    get_conc(Goal, x, N),
    \+ get_proved_conc(N),
    try_to_prove_via_inference_rule(Goal),
    fail.

apply_user_defined_proof_rules :-
    record_any_proved_conclusions,
    proved_all_conclusions,
    !.

apply_user_defined_proof_rules :-
    i_am_using_rule(udr2),
    get_conc(Goal, x, N),
    \+ get_proved_conc(N),
    try_to_prove_via_rewrite_rule(Goal),
    fail.

apply_user_defined_proof_rules :-
    record_any_proved_conclusions,
    proved_all_conclusions,
    !.

apply_user_defined_proof_rules :-
    i_am_using_rule(udr3),
    create_new_facts_from_hypotheses,
    fail.

apply_user_defined_proof_rules :-
    record_any_proved_conclusions,
    proved_all_conclusions,
    !.

%-------------------------------------------------------------------------------
% Phase 2
%-------------------------------------------------------------------------------

apply_user_defined_proof_rules :-
    i_am_using_rule(udr4),
    get_conc(Goal, x, N),
    \+ get_proved_conc(N),
    try_to_prove_via_inference_rule(Goal),
    fail.

apply_user_defined_proof_rules :-
    record_any_proved_conclusions,
    proved_all_conclusions,
    !.

apply_user_defined_proof_rules :-
    i_am_using_rule(udr5),
    get_conc(Goal, x, N),
    \+ get_proved_conc(N),
    try_to_prove_via_rewrite_rule(Goal),
    fail.

apply_user_defined_proof_rules :-
    record_any_proved_conclusions,
    proved_all_conclusions,
    !.

apply_user_defined_proof_rules :-
    i_am_using_rule(udr6),
    create_new_facts_from_existing_facts,
    fail.

apply_user_defined_proof_rules :-
    record_any_proved_conclusions,
    proved_all_conclusions,
    !.

%-------------------------------------------------------------------------------
% Phase 3
%-------------------------------------------------------------------------------

apply_user_defined_proof_rules :-
    i_am_using_rule(udr7),
    get_conc(Goal, x, N),
    \+ get_proved_conc(N),
    try_to_prove_via_inference_rule(Goal),
    fail.

apply_user_defined_proof_rules :-
    record_any_proved_conclusions,
    proved_all_conclusions,
    !.

apply_user_defined_proof_rules :-
    i_am_using_rule(udr8),
    get_conc(Goal, x, N),
    \+ get_proved_conc(N),
    try_to_prove_via_rewrite_rule(Goal),
    fail.

apply_user_defined_proof_rules :-
    record_any_proved_conclusions,
    proved_all_conclusions,
    !.

% always succeeds (eventually)
apply_user_defined_proof_rules:-
    !.

%-------------------------------------------------------------------------------
% record_any_proved_conclusions
%
% Determines which (if any) conclusions have been proved by the previous
% application of user rules. Records the conclusions as proved and promotes any
% rules used to prove side conditions of rules to hypotheses.
% This clause should be invoked after the completion of each attempt
% in proving a Goal thus avoiding the unnecessary application of rules
% to conclusions which are already proved.
%-------------------------------------------------------------------------------

record_any_proved_conclusions:-
    i_am_using_rule(udr9),
    get_conc(Goal, x, N),
    \+ get_proved_conc(N),
    is_a_fact_from_rule(_Ref, From, Goal, Name, RuleMatchDetails, RuleSort),
    record_conclusion_proved_by_rules(N, From, Goal, Name,
                                      RuleMatchDetails, RuleSort),
    fail.

record_any_proved_conclusions:-
    !.

%-------------------------------------------------------------------------------
% record_conclusion_proved_by_rules(+Id_Int, +From, +Goal, +Name,
%                                   +RuleMatchDetails, +RuleSort)
%-------------------------------------------------------------------------------

record_conclusion_proved_by_rules(N, From, Goal, Name,
                                  RuleMatchDetails, RuleSort) :-
    issue_message('Applied rule ', Name),
    add_proved_conc(N),
    issue_proved_message(N),
    determine_facts_and_promote_to_hypotheses(From, RuleMatchDetails,
                                              NewRuleMatchDetails),
    assert_log_fact(rule_proved_conc,
                    [N, Goal, Name, NewRuleMatchDetails, RuleSort]),
    mark_whether_proved_by_user_rules(RuleSort),
    !.

%-------------------------------------------------------------------------------
% mark_whether_proved_by_user_rules(+Rule_Type)
%-------------------------------------------------------------------------------

mark_whether_proved_by_user_rules(user_inference_rule) :-
    !,
    add_fact(proved_by_user_rules).

mark_whether_proved_by_user_rules(user_rewrite_rule) :-
    !,
    add_fact(proved_by_user_rules).

mark_whether_proved_by_user_rules(_) :-
    !.

%###############################################################################
% INFERENCE RULES.
%###############################################################################

%===============================================================================
% try_to_prove_via_inference_rule(+Goal)
%
% Attempt to prove goal through inference rules.
% Process all inference rules where all the variables in Conditions are ground
% first and then (in second clause) where some of the variables  in
% Conditions are not ground.
%===============================================================================

try_to_prove_via_inference_rule(Goal) :-
    % Goal is ground.
    inference_rule_match(Name, Goal, Conditions, RuleSort),
    var_free(Conditions),
    fulfil(Conditions, ProvedConditions, []),
    var_free(ProvedConditions),
    % success: don't backtrack and retry
    !,
    record_rule_success(conc, Goal, Name, RuleSort,
                        inference(ProvedConditions), _).

try_to_prove_via_inference_rule(Goal) :-
    % Goal is ground.
    inference_rule_match(Name, Goal, Conditions, RuleSort),
    \+ var_free(Conditions),
    fulfil(Conditions, ProvedConditions, []),
    var_free(ProvedConditions),
    % success: don't backtrack and retry
    !,
    record_rule_success(conc, Goal, Name, RuleSort,
                        inference(ProvedConditions), _).

%###############################################################################
% REWRITE RULES.
%###############################################################################

%===============================================================================
% try_to_prove_via_rewrite_rule(+Goal)
%
% Attempt to prove goal through rewrite rules.
%===============================================================================

try_to_prove_via_rewrite_rule(Goal):-
    % Goal is ground.
    rewrite_rule_match(Name, Goal, NewGoal, Conditions, RuleSort),
    var_free(Conditions),
    fulfil([NewGoal|Conditions], ProvedConditions, []),
    % once the side-conditions have been met
    novars(NewGoal),
    var_free(ProvedConditions),
    % success: don't backtrack and retry */
    !,
    record_rule_success(conc, Goal, Name, RuleSort,
                        rewrite(NewGoal, ProvedConditions), _).

try_to_prove_via_rewrite_rule(Goal):-
    % Goal is ground.
    rewrite_rule_match(Name, Goal, NewGoal, Conditions, RuleSort),
    \+ var_free(Conditions),
    fulfil([NewGoal|Conditions], ProvedConditions, []),
    % once the side-conditions have been met
    novars(NewGoal),
    var_free(ProvedConditions),
    % success: don't backtrack and retry */
    !,
    record_rule_success(conc, Goal, Name, RuleSort,
                        rewrite(NewGoal, ProvedConditions), _).

%===============================================================================
% create_new_facts_from_hypotheses
%===============================================================================

create_new_facts_from_hypotheses:-
    get_hyp(Formula, x, N),
    \+ know_eliminated(N),
    create_new_fact(hyp(N), Formula).

%===============================================================================
% create_new_facts_from_existing_facts
%===============================================================================

create_new_facts_from_existing_facts:-
    copy_existing_facts,
    get_candidate_fact(N, Formula),
    create_new_fact(fact(N), Formula).

%===============================================================================
% copy_existing_facts
%===============================================================================

copy_existing_facts:-
    prune_all_candidate_fact,
    fail.

copy_existing_facts:-
    get_fact_from_rule(Fact, N, _, _, _, _),
    Fact \= true,
    add_candidate_fact(N, Fact),
    fail.

copy_existing_facts :-
    !.

%===============================================================================
% create_new_fact(+HypOrFact, +Formula)
%===============================================================================

create_new_fact(HypOrFact, Formula):-
    rewrite_rule_match(Name, Formula, NewFormula, Conditions, RuleSort),
    fulfil(Conditions, ProvedConditions, []),
    nonvar(NewFormula),
    dont_already_know(NewFormula),
    var_free(ProvedConditions),
    record_rule_success(HypOrFact, NewFormula, Name, RuleSort,
                        rewrite(Formula, ProvedConditions), _).

%===============================================================================
% inference_rule_match(+Name, +Goal, -Conditions, -RuleSort)
%
% Find a user-defined inference rule and provide its generalised form.
%===============================================================================

inference_rule_match(Name, Goal, Conditions, RuleSort) :-
    nonvar(Goal),
    functor(Goal, Op_Atom, Args_Int),
    !,
    functor(Goal1, Op_Atom, Args_Int),
    inference_rule_match_x(Name, Goal1, Conditions1, RuleSort),
    add_conds(Args_Int, Goal, Goal1, Conditions1, Conditions).

%===============================================================================
% inference_rule_match_x(+Name, +Goal, -Conditions, -RuleSort)
%
%===============================================================================

inference_rule_match_x(Name, Goal, Conditions, inference_rule):-
    inference_rule(Name, Goal, Conditions).

inference_rule_match_x(Name, Goal, Conditions, user_inference_rule):-
    user_inference_rule(Name, Goal, Conditions).

%===============================================================================
% add_conds(+N, +A_Goal, +B_GOAL, +OldList, -NewList)
%
% Unify lists arguments in A_Goal and B_Goal creating NewList from OldList
%===============================================================================

add_conds(0, _Goal, _Goal1, Conditions_List, Conditions_List):-
    !.

add_conds(N, Goal, Goal1, OldConditions_List, NewConditions_List):-
    arg(N, Goal, Arg),
    arg(N, Goal1, Arg),
    !,
    M is N-1,
    add_conds(M, Goal, Goal1, OldConditions_List, NewConditions_List).

add_conds(N, Goal, Goal1, OldConditions_List, [Arg=Arg1 | NewConditions_List]):-
    arg(N, Goal, Arg),
    arg(N, Goal1, Arg1),
    !,
    M is N-1,
    add_conds(M, Goal, Goal1, OldConditions_List, NewConditions_List).

%===============================================================================
% record_rule_success(+From, +Goal, +Name, +RuleMatchDetails, -Ref_Int)
%
% From,                   -- Operation for which the rule was applied
% Goal,                   -- Goal satisfied
% Name,                   -- Name of applied rule
% RuleMatchDetails,       -- How the rule was applied
% Ref                     -- The fact number corresponding to
%                            the proof of the Goal
%===============================================================================

record_rule_success(_, Goal, _Name, _RuleSort, _RuleMatchDetails, FactNumber) :-
    % already known: don't add again
    get_fact_from_rule(Goal, FactNumber, _, _, _, _),
    !.

record_rule_success(From, Goal, Name, RuleSort, RuleMatchDetails, Ref) :-
    get_unique_reference(fact, Ref),
    add_fact_from_rule(Goal, Ref, From, Name, RuleMatchDetails, RuleSort),
    !.

%===============================================================================
% rewrite_rule_match(Name, +Goal, -NewGoal, -NewConditions, -RuleSort)
%
% Apply the rewrite rule Name to Goal:
%   - NewGoal: is result of using the rewrite rule on Goal,
%   - NewConditions: list of conditions of the rule
%   - RuleSort: type of rule.
%
% Find a user-defined rewrite rule that matches and provide its generalised
% form.
%
% User-defined rewrite rules OR non-ground rewrite rules coming
% from the RLS file are both applicable here.
%===============================================================================

rewrite_rule_match(Name, Goal, NewGoal, NewConditions, RuleSort):-
    nonvar(Goal),
    rewrite_rule_match_x(Name, Goal, NewGoal, NewConditions, RuleSort).

rewrite_rule_match_x(Name, Goal, NewGoal, NewConditions, RuleSort):-
    rewrite_rule_match_y(Name, OldExp, NewExp, Conditions, RuleSort),
    pattern_match_rule(Goal, OldExp, NewExp, NewGoal, Conditions, NewConditions).

rewrite_rule_match_x(Name, Goal, NewGoal, NewConditions, RuleSort):-
    rewrite_rule_match_y(Name, NewExp, OldExp, Conditions, RuleSort),
    nonvar(OldExp),
    pattern_match_rule(Goal, OldExp, NewExp, NewGoal, Conditions, NewConditions).

rewrite_rule_match_y(Name, OldExp, NewExp, Conditions, user_rewrite_rule):-
    user_rewrite_rule(Name, OldExp, NewExp, Conditions).

rewrite_rule_match_y(Name, OldExp, NewExp, Conditions, nonground_replace_rule):-
    nonground_replace_rule(Name, OldExp, NewExp, Conditions).

%===============================================================================
% pattern_match_rule(Goal, OldExp, NewExp, NewGoal, Conditions, Conditions)
%
%===============================================================================

pattern_match_rule(Goal, Goal, NewExpr, NewExpr, Conditions, Conditions).

pattern_match_rule(Goal, OldExp, NewExp, NewGoal, Conditions, NewConditions) :-
    ground(Goal),
    \+ atomic(Goal),
    %
    % The use of =.. in creating term NewGoal is justified as the code
    % needs to copy the arguments and the functor from Goal.
    %
    Goal =.. [Op|Args],
    pattern_match_rule_list(Args, OldExp, NewExp, NewArgs,
                            Conditions, NewConditions),
    NewGoal =.. [Op|NewArgs].

%===============================================================================
% pattern_match_rule_list([OldArg|Rest], OldExp, NewExp,
%                         [NewArg|Rest], Conditions, NewConditions)
%
%===============================================================================

pattern_match_rule_list([OldArg|Rest], OldExp, NewExp,
                        [NewArg|Rest], Conditions, NewConditions) :-
    find_pattern_match(OldArg, OldExp, NewExp, NewArg, [], ExtraConditions),
    append(ExtraConditions, Conditions, NewConditions).

pattern_match_rule_list([Arg|OldRest], OldExp, NewExp,
                        [Arg|NewRest], Conditions, NewConditions) :-
    pattern_match_rule_list(OldRest, OldExp, NewExp, NewRest,
                            Conditions, NewConditions).

%===============================================================================
% find_pattern_match(OldArg, OldExp, NewExp, NewArg, Conditions, NewConditions)
%
%===============================================================================

find_pattern_match(OldArg, OldExp, NewExp, NewArg, Conditions, NewConditions) :-
    pattern_match_rule(OldArg, OldExp, NewExp, NewArg, Conditions, NewConditions).

find_pattern_match(OldArg, OldExp, _NewExp, OldArg, Conditions, Conditions) :-
    ground(OldArg),
    functor(OldArg, Op, _),
    nonvar(OldExp),
    functor(OldExp, Op, _).

%===============================================================================
% Solve a rule match's side conditions (handling ground conditions first).
%
% Inference rule strategy (based on that in the proof checker):
% F may_be_deduced_from GOALS.
%
% 1. Split GOALS into fully-instantiated-goals (i.e. primary goals) and
%    partially-instantiated-goals (i.e. secondary goals).
%
% 2. Attempt to satisfy all primary goals. Either:
%
%   a. All were satisfied.  Then attempt to satisfy secondary goals.
%      As soon as one becomes satisfied, split the remainder into primaries
%      and secondaries and attempt to satisfy them in the same way (i.e.
%      recursively).
%
%   b. At least one can be shown to be false.  This rule-match will never
%      succeed in its current instantiation, so cut and fail.
%
%   c. Some were not satisfied.  Leave secondary goals and backtrack.
%===============================================================================

%===============================================================================
% fulfil(Goals, ProvedGoals, UnsatisfiedGoals)
%===============================================================================

fulfil([], [], []):-
    !.

fulfil(Goals, ProvedGoals, UnsatisfiedGoals):-
    split(Goals, Primaries, Secondaries),
    try_to_satisfy(Primaries, ProvedPrimaries, UnsatisfiedPrimaries),
    fulfil_x(UnsatisfiedPrimaries, Secondaries, ProvedSecondaries,
             UnsatisfiedSecondaries),
    % All conditions must be ground.
    var_free(UnsatisfiedSecondaries),
    append(ProvedPrimaries, ProvedSecondaries, ProvedGoals),
    append(UnsatisfiedPrimaries, UnsatisfiedSecondaries, UnsatisfiedGoals).

%===============================================================================
%fulfil_x(+UnsatisfiedPrimaries, +Secondaries, +ProvedSecondaries,
%         +UnsatisfiedSecondaries).
%===============================================================================

fulfil_x([], Secondaries, ProvedSecondaries, UnsatisfiedSecondaries):-
    match_up(Secondaries, ProvedSecondaries, UnsatisfiedSecondaries).

fulfil_x(UnsatisfiedPrimaries, Secondaries, [], Secondaries):-
    UnsatisfiedPrimaries \= [].

%===============================================================================
% split(Goal_List, Primary_List, Secondary_List)
%===============================================================================

split([], [], []):-
    !.

split([G|Gs], [G|Ps], Ss) :-
    novars(G),
    !,
    split(Gs, Ps, Ss),
    !.

split([G|Gs], Ps, [G|Ss]) :-
    split(Gs, Ps, Ss),
    !.

%===============================================================================
% try_to_satisfy(+Primary_GoalList, +ProvedPrimary_GoalList,
%                +UnsatisifiedPrimary_GoalList).
%===============================================================================

try_to_satisfy([], [], []):-
    !.

try_to_satisfy([G|Gs], [proved(G,[],[])|Ps], Us) :-
    nonvar(G),
    G=goal(D),
    !,
    evaluate_immediate_condition(D),
    !,
    try_to_satisfy(Gs, Ps, Us),
    !.

try_to_satisfy([G|_], _, _) :-
    % stop immediately if so
    does_not_typecheck_as_boolean(G),
    !,
    fail.

try_to_satisfy([G|Gs], [proved(G,Hs,Fs)|Ps], Us) :-
    rule_phase_infer(G, Hs, Fs),
    !,
    try_to_satisfy(Gs, Ps, Us),
    !.

try_to_satisfy([G|_], _, _) :-
    simplify(not G, NotG),
    % if a ground goal is deducibly false, stop now
    infer(NotG, _),
    !,
    fail.

try_to_satisfy([G|Gs], Ps, [G|Us]) :-
    try_to_satisfy(Gs, Ps, Us),
    !.

%===============================================================================
% match_up(Goals, ProvedGoals, UnsatisfiedGoals)
%===============================================================================

match_up([], [], []):-
    !.

match_up(Goals, ProvedGoals, UnsatisfiedGoals):-
    seek_solutions(Goals, Proved, Unsatisfied),
    % ensure list of unsatisfied goals is shrinking
    Goals \= Unsatisfied,
    split(Unsatisfied, Primaries, Secondaries),
    try_to_satisfy(Primaries, ProvedPrimaries, UnsatisfiedPrimaries),
    append(Proved, ProvedPrimaries, ProvedSoFar),
    match_up(Secondaries, ProvedSecondaries, UnsatisfiedSecondaries),
    append(ProvedSoFar, ProvedSecondaries, ProvedGoals),
    append(UnsatisfiedPrimaries, UnsatisfiedSecondaries, UnsatisfiedGoals).

%===============================================================================
% seek_solutions(+Goal_ExpList,
%                -ProvedGaols_ExpList,
%                -UnsatisfiedGoal_ExpList)
%
% Try to prove all the goals in Goal_ExpList
%   * ProvedGoals_ExpList contains the list of all proved goals;
%   * UnsatisfiedGoal_ExpList contains the list of all unproven goals.
%===============================================================================

seek_solutions([], [], []):-
    !.

% Case when H_Goal_Exp is provable.
seek_solutions([H_Goal_Exp|T_Goal_ExpList],
               [proved(H_Goal_Exp, Hs, Fs)|ProvedGoal_ExpList],
               UnsatisfiedGoal_ExpList):-
    do_satisfy_goal(H_Goal_Exp, Hs, Fs),
    seek_solutions(T_Goal_ExpList, ProvedGoal_ExpList, UnsatisfiedGoal_ExpList).

% Case when H_Goal_Exp is not provable.
seek_solutions([H_Goal_Exp|T_Goal_ExpList],
               ProvedGoal_ExpList,
               [H_Goal_Exp|UnsatisfiedGoal_ExpList]) :-
    seek_solutions(T_Goal_ExpList, ProvedGoal_ExpList, UnsatisfiedGoal_ExpList).


%===============================================================================
% do_satisfy_goal(+Goal_Exp, +HypId_IntList, +FactId_IntList)
%===============================================================================

do_satisfy_goal(G, [], []) :-
    nonvar(G),
    G = goal(Pred),
    !,
    evaluate_immediate_condition(Pred),
    !.

do_satisfy_goal(Goal_Exp, Hs, Fs) :-
    % Save Goal in the database.
    retractall(current_sat_goal(_)),
    asserta(current_sat_goal(Goal_Exp)),
    !,
    try_satisfy_goal(Goal_Exp, [], Hs, Fs).

%===============================================================================
% try_satisfy_goal(+Goal_Exp, Instance_ExpList, HypId_IntList, FactId_IntList)
%===============================================================================

try_satisfy_goal(G, InstanceList, H, F) :-
    current_sat_goal(Goal),
    % Find a solution to satisfy Goal which may ground some of the variables
    % in Goal.
    satisfy_goal(Goal, Hs, Fs),
    % Ensure we have not gone down this path before.
    \+ is_in(Goal, InstanceList),
    !,
    try_satisfy_goal_x(G, Goal, InstanceList, H, F, Hs, Fs).

%===============================================================================
% try_satisfy_goal_x(+A_Goal_Exp, +B_Goal_Exp, +Instance_ExpList,
%                    +A_HypId_IntList,  -B_HypId_IntList,
%                    +A_FactId_IntList, -B_FactId_IntList)
%===============================================================================

% Found a solution.
try_satisfy_goal_x(Goal, Goal, _InstanceList, H, F, H, F).

% On backtracking from try_satisfy_goal, find another possible solution to
% satisfy goal.
try_satisfy_goal_x(G, Goal, InstanceList, H, F, _Hs, _Fs):-
    try_satisfy_goal(G, [Goal|InstanceList], H, F).

%===============================================================================
% satisfy_goal_x(+Goal, -Hs, -Fs)
%===============================================================================

satisfy_goal(A=B, Hs, Fs) :-
    novars(A),
    simplify(A,X),
    A\=X,
    !,
    satisfy_goal(X=B, Hs, Fs).

satisfy_goal(A=B, Hs, Fs) :-
    novars(B),
    simplify(B,X),
    B\=X,
    !,
    satisfy_goal(A=X, Hs, Fs).

satisfy_goal(A<>B, Hs, Fs) :-
    novars(A),
    simplify(A,X),
    A\=X,
    !,
    satisfy_goal(X<>B, Hs, Fs).

satisfy_goal(A<>B, Hs, Fs) :-
    novars(B),
    simplify(B,X),
    B\=X,
    !,
    satisfy_goal(A<>X, Hs, Fs).

satisfy_goal(A>=B, Hs, Fs) :-
    novars(A),
    simplify(A,X),
    A\=X,
    !,
    satisfy_goal(X>=B, Hs, Fs).

satisfy_goal(A>=B, Hs, Fs) :-
    novars(B),
    simplify(B,X),
    B\=X,
    !,
    satisfy_goal(A>=X, Hs, Fs).

satisfy_goal(A<=B, Hs, Fs) :-
    novars(A),
    simplify(A,X),
    A\=X,
    !,
    satisfy_goal(X<=B, Hs, Fs).

satisfy_goal(A<=B, Hs, Fs) :-
    novars(B),
    simplify(B,X),
    B\=X,
    !,
    satisfy_goal(A<=X, Hs, Fs).

satisfy_goal(A>B, Hs, Fs) :-
    novars(A),
    simplify(A,X),
    A\=X,
    !,
    satisfy_goal(X>B, Hs, Fs).

satisfy_goal(A>B, Hs, Fs) :-
    novars(B),
    simplify(B,X),
    B\=X,
    !,
    satisfy_goal(A>X, Hs, Fs).

satisfy_goal(A<B, Hs, Fs) :-
    novars(A),
    simplify(A,X),
    A\=X,
    !,
    satisfy_goal(X<B, Hs, Fs).

satisfy_goal(A<B, Hs, Fs) :-
    novars(B),
    simplify(B,X),
    B\=X,
    !,
    satisfy_goal(A<X, Hs, Fs).

satisfy_goal(G, Hs, Fs) :-
    var(G),
    !,
    satisfy_goal_var_goal(G, Hs, Fs).

satisfy_goal(G, Hs, []) :-
    fact(G, Hs).

satisfy_goal(Goal, [], [F]) :-
    get_fact_from_rule(Goal, F, _, _, _, _).

satisfy_goal(not G, Hs, Fs) :-
    nonvar(G),
    satisfy_goal_not(not G, H),
    satisfy_goal(H, Hs, Fs).

%-------------------------------------------------------------------------------
% A and B
%-------------------------------------------------------------------------------

satisfy_goal(A and B, Hs, Fs) :-
    novars(A),
    !,
    rule_phase_infer(A, H1, F1),
    satisfy_goal(B, H2, F2),
    merge_sort(H1, H2, Hs),
    merge_sort(F1, F2, Fs).

satisfy_goal(A and B, Hs, Fs) :-
    novars(B),
    !,
    rule_phase_infer(B, H1, F1),
    satisfy_goal(A, H2, F2),
    merge_sort(H1, H2, Hs),
    merge_sort(F1, F2, Fs).

satisfy_goal(A and B, Hs, Fs) :-
    satisfy_goal(A, H1, F1),
    satisfy_goal(B, H2, F2),
    merge_sort(H1, H2, Hs),
    merge_sort(F1, F2, Fs).

%-------------------------------------------------------------------------------
% A or B
%-------------------------------------------------------------------------------

satisfy_goal(A or B, Hs, Fs) :-
    novars(A),
    !,
    satisfy_goal_A_or_B(A, B, Hs, Fs).

satisfy_goal(A or B, Hs, Fs) :-
    novars(B),
    !,
    satisfy_goal_A_or_B(B, A, Hs, Fs).

satisfy_goal(A or _B, Hs, Fs) :-
    satisfy_goal(A, Hs, Fs).

satisfy_goal(_A or B, Hs, Fs) :-
    satisfy_goal(B, Hs, Fs).

%-------------------------------------------------------------------------------
% A -> B
%-------------------------------------------------------------------------------









satisfy_goal(A -> B, Hs, Fs) :-
    novars(A),
    !,
    satisfy_goal_A_implies_B_novars_A(A -> B, Hs, Fs).

satisfy_goal(A -> B, Hs, Fs) :-
    novars(B),
    !,
    satisfy_goal_A_implies_B_novars_B(A -> B, Hs, Fs).

satisfy_goal(A -> B, Hs, Fs) :-
    satisfy_goal_A_implies_B(A -> B, Hs, Fs).

%-------------------------------------------------------------------------------
% A <-> B
%-------------------------------------------------------------------------------

satisfy_goal(A <-> B, [], []) :-
    satisfy_goal_A_iff_B(A, B).

satisfy_goal(A <-> B, Hs, Fs) :-
    satisfy_goal(A -> B, H1, F1),
    satisfy_goal(B -> A, H2, F2),
    merge_sort(H1, H2, Hs),
    merge_sort(F1, F2, Fs).

%-------------------------------------------------------------------------------
% A=B
%-------------------------------------------------------------------------------

satisfy_goal(A=B, [], []) :-
    novars(B),
    satisfy_goal_A_equals_B(A, B).

satisfy_goal(A=B, [], []) :-
    novars(A),
    satisfy_goal_A_equals_B(B, A).

%-------------------------------------------------------------------------------
% A<>B
%-------------------------------------------------------------------------------

satisfy_goal(A<>B, Hs, []) :-
    fact(A>B, Hs).

satisfy_goal(A<>B, Hs, []) :-
    fact(A<B, Hs).

satisfy_goal(A<>B, [], [F]) :-
    get_fact_from_rule(A>B, F, _, _, _, _).

satisfy_goal(A<>B, [], [F]) :-
    get_fact_from_rule(B<A, F, _, _, _, _).

satisfy_goal(A<>B, [], [F]) :-
    get_fact_from_rule(A<B, F, _, _, _, _).

satisfy_goal(A<>B, [], [F]) :-
    get_fact_from_rule(B>A, F, _,  _, _, _).

%-------------------------------------------------------------------------------
% A>B
%-------------------------------------------------------------------------------

satisfy_goal(A>B, Hs, Fs) :-
    satisfy_goal(A>=B, H1, F1),
    novars(A<>B),
    rule_phase_infer(A<>B, H2, F2),
    merge_sort(H1, H2, Hs),
    merge_sort(F1, F2, Fs).

%-------------------------------------------------------------------------------
% A<B
%-------------------------------------------------------------------------------

satisfy_goal(A<B, Hs, Fs) :-
    satisfy_goal(A<=B, H1, F1),
    novars(A<>B),
    rule_phase_infer(A<>B, H2, F2),
    merge_sort(H1, H2, Hs),
    merge_sort(F1, F2, Fs).

%-------------------------------------------------------------------------------
% A>=B
%-------------------------------------------------------------------------------

satisfy_goal(A>=B, Hs, []) :-
    fact(A>B, Hs).

satisfy_goal(A>=B, Hs, []) :-
    fact(B<A, Hs).

satisfy_goal(A>=B, [], [F]) :-
    get_fact_from_rule(A>B, F, _, _, _, _).

satisfy_goal(A>=B, [], [F]) :-
    get_fact_from_rule(B<A, F, _, _, _, _).

satisfy_goal(A>=B, Hs, Fs) :-
    satisfy_goal(A=B, Hs, Fs).

%-------------------------------------------------------------------------------
% A<=B
%-------------------------------------------------------------------------------











satisfy_goal(A<=B, Hs, []) :-
    fact(A<B, Hs).

satisfy_goal(A<=B, Hs, []) :-
    fact(B>A, Hs).

satisfy_goal(A<=B, [], [F]) :-
    get_fact_from_rule(A<B, F, _, _, _, _).

satisfy_goal(A<=B, [], [F]) :-
    get_fact_from_rule(B>A, F, _, _, _, _).

satisfy_goal(A<=B, Hs, Fs) :-
    satisfy_goal(A=B, Hs, Fs).

%-------------------------------------------------------------------------------
% X=A+B
%-------------------------------------------------------------------------------

satisfy_goal(X=A+B, [], []) :-
    novars(X),
    satisfy_goal_a_plus_b(X=A+B).

%-------------------------------------------------------------------------------
% X=A-B
%-------------------------------------------------------------------------------

satisfy_goal(X=A-B, [], []) :-
    novars(X),
    satisfy_goal_a_minus_b(X=A-B).

%-------------------------------------------------------------------------------
% Support predicate for X=A+B
%-------------------------------------------------------------------------------

satisfy_goal_a_plus_b(X=A+B) :-
    novars(B),
    A=X-B.

satisfy_goal_a_plus_b(X=A+B) :-
   novars(A),
   B=X-A.

%-------------------------------------------------------------------------------
% Support predicate for X=A-B
%-------------------------------------------------------------------------------

satisfy_goal_a_minus_b(X=A-B) :-
   novars(B),
   A=X+B.

satisfy_goal_a_minus_b(X=A-B) :-
   novars(A),
   B=A-X.

%-------------------------------------------------------------------------------
% Support predicate for var(G)
% satisfy_goal_var_goal(+Goal, +HypId_List, +FactId_List)

satisfy_goal_var_goal(Goal, [Hyp_Id], []):-
    get_hyp(Goal, x, Hyp_Id),
    integer(Hyp_Id).

satisfy_goal_var_goal(Goal, [], [Fact_Id]):-
    get_fact_from_rule(Goal, Fact_Id, _, _, _, _),
    Goal \= true.

%-------------------------------------------------------------------------------
% satisfy_goal_not(not +Goal, -NotGoal).
%-------------------------------------------------------------------------------

satisfy_goal_not(not not Goal, Goal).

satisfy_goal_not(not A=B, A<>B).

satisfy_goal_not(not A<>B, A=B).

satisfy_goal_not(not A>B, A<=B).

satisfy_goal_not(not A<B, A>=B).

satisfy_goal_not(not A>=B, A<B).

satisfy_goal_not(not A<=B, A>B).

%-------------------------------------------------------------------------------
% satisfy_goal_A_or_B(A or B, Hs, Fs).
%-------------------------------------------------------------------------------

satisfy_goal_A_or_B(A, _B, Hs, Fs):-
    rule_phase_infer(A, Hs, Fs).

satisfy_goal_A_or_B(_A, B, Hs, Fs):-
    satisfy_goal(B, Hs, Fs).

%-------------------------------------------------------------------------------
% satisfy_goal_A_implies_B_novars_A(A -> B, Hs, Fs).
%-------------------------------------------------------------------------------

satisfy_goal_A_implies_B_novars_A(A -> _B, Hs, Fs):-
    simplify(not A, NotA),
    rule_phase_infer(NotA, Hs, Fs).

satisfy_goal_A_implies_B_novars_A(_A -> B, Hs, Fs):-
    satisfy_goal(B, Hs, Fs).

%-------------------------------------------------------------------------------
% satisfy_goal_A_implies_B_novars_B(A -> B, Hs, Fs).
%-------------------------------------------------------------------------------

satisfy_goal_A_implies_B_novars_B(_A -> B, Hs, Fs):-
    rule_phase_infer(B, Hs, Fs).

satisfy_goal_A_implies_B_novars_B(A -> _B, Hs, Fs):-
    satisfy_goal(not A, Hs, Fs).

%-------------------------------------------------------------------------------
% satisfy_goal_A_implies_B(A -> B, Hs, Fs).
%-------------------------------------------------------------------------------

satisfy_goal_A_implies_B(A -> _B, Hs, Fs):-
    satisfy_goal(not A, Hs, Fs).

satisfy_goal_A_implies_B(_A -> B, Hs, Fs):-
    satisfy_goal(B, Hs, Fs).

%-------------------------------------------------------------------------------
% satisfy_goal_A_iff_B(A <-> B, Hs, Fs).
%-------------------------------------------------------------------------------

satisfy_goal_A_iff_B(A, B):-
    novars(B),
    var(A),
    simplify(B, B1),
    A=B1.

satisfy_goal_A_iff_B(A, B):-
    novars(A),
    var(B),
    simplify(A, A1),
    B=A1.

%-------------------------------------------------------------------------------
% satisfy_goal_A_equals_B_novars_B(A=B, Hs, Fs).
%-------------------------------------------------------------------------------

satisfy_goal_A_equals_B(A, B):-
    var(A),
    rational_expression(B),
    \+ base_rational(B),
    evaluate_rational_expression(B, A).

satisfy_goal_A_equals_B(A, B):-
    A=B.


%===============================================================================
% rule_phase_infer(Goal, Hs, Fact_List)
%
% If Goal is ground and can be directly inferred from the
% (also ground) Condition of an existing inference rule
% then try to infer that Condition directly
%
% In particular, if we're trying to prove a conclusion
%
%     s__tail(s) >= 0
%
% and we have a hypothesis
%
%     word__always_valid(s)
%
% and 2 rules
%     X >= 0 may_be_deduced_from [ word__always_valid(X) ].
%     word__always_valid(s__tail(s)) may_be_deduced_from [word__always_valid(s)].
%
% Then this special case allows the relevant reasoning to
% be established.
%
% This is only a partial solution, though.  In future,
% a more general solution that allows multiple __tail()'s
% to be "stripped away" should be sought.
%===============================================================================

rule_phase_infer(Goal, Hs, [Fact]) :-
    ground(Goal),
    inference_rule(Name, Goal, [Condition]),
    ground(Condition),
    infer(Condition, Hs),
    record_rule_success(rule_phase_inference, Goal, Name, inference_rule,
                        inference([proved(Condition, Hs, [])]), Fact),
    !.

rule_phase_infer(Goal, Hs, [Fact]) :-
    ground(Goal),
    user_inference_rule(Name, Goal, [Condition]),
    ground(Condition),
    infer(Condition, Hs),
    record_rule_success(rule_phase_inference, Goal, Name, user_inference_rule,
                        inference([proved(Condition, Hs, [])]), Fact),
    !.

% More generally.
rule_phase_infer(Goal, Hs, []) :-
    infer(Goal, Hs),
    !.

rule_phase_infer(Goal, [], [F]) :-
    is_a_fact_from_rule(F, _, Goal, _, _, _),
    !.

%===============================================================================
% is_a_fact_from_rule(+FactRef, +From, +Goal, +Name, +Details, +RuleSort)
%
% FactRef: a unique reference (a fact number) for the fact.
% From:    Operation for which the rule was applied. May be:
%               conc
%               rule_phase_inference
%               fact(N) (a fact number)
%               hyp(N)  (a hyp number)
%
% Goal:    The goal satisfied. These are logically hypotheses terms
%          that are known to be true.
%
% Name:    Always hyp(HypNo) - the hypnumber will contain what used to be
%          stored in 'Goal'.
%
% Details:  Rule match details - always empty
%
% RuleSort: As above
%===============================================================================

is_a_fact_from_rule(F, From, Goal, Name, Details, RuleSort) :-
    get_fact_from_rule(Goal, F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X=Y,  Name, Details, RuleSort) :-
    get_fact_from_rule(Y=X, F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X<=Y, Name, Details, RuleSort) :-
    get_fact_from_rule(X<Y, F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X<=Y, Name, Details, RuleSort) :-
     get_fact_from_rule(Y>=X, F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X<=Y, Name, Details, RuleSort) :-
     get_fact_from_rule(Y>X, F, From,  Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X>=Y, Name, Details, RuleSort) :-
     get_fact_from_rule(X>Y,  F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X>=Y, Name, Details, RuleSort) :-
     get_fact_from_rule(Y<=X, F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X>=Y, Name, Details, RuleSort) :-
     get_fact_from_rule(Y<X,  F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X<Y,  Name, Details, RuleSort) :-
     get_fact_from_rule(Y>X,  F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X>Y,  Name, Details, RuleSort) :-
     get_fact_from_rule(Y<X,  F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X<>Y, Name, Details, RuleSort) :-
     get_fact_from_rule(Y<>X, F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X<>Y, Name, Details, RuleSort) :-
     get_fact_from_rule(X<Y,  F, From, Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X<>Y, Name, Details, RuleSort) :-
     get_fact_from_rule(X>Y, F, From,  Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X<>Y, Name, Details, RuleSort) :-
     get_fact_from_rule(Y<X, F, From,  Name, Details, RuleSort).

is_a_fact_from_rule(F, From, X<>Y, Name, Details, RuleSort) :-
     get_fact_from_rule(Y>X, F, From,  Name, Details, RuleSort).

%-------------------------------------------------------------------------------
% dont_already_know(+Formula_Exp)
%-------------------------------------------------------------------------------

dont_already_know(Formula) :-
    % discard as a candidate new fact, if so
    rule_phase_infer(Formula, _, _),
    !,
    fail.

dont_already_know(_Formula) :-
    % otherwise succeed
    !.

%-------------------------------------------------------------------------------
% does_not_typecheck_as_boolean(+Boolean_Exp)
%
% Fail if G is an boolean expression; Otherwise succeeds.
%-------------------------------------------------------------------------------

does_not_typecheck_as_boolean(G) :-
    checkhastype(G, boolean),
    !,
    fail.

does_not_typecheck_as_boolean(_).

%-------------------------------------------------------------------------------
% evaluate_immediate_condition(+Cond)
%
% Succeed if the condition can be satisfied, fail otherwise.
%-------------------------------------------------------------------------------

evaluate_immediate_condition(X):-
    var(X),
    !,
    fail.

evaluate_immediate_condition((X,Y)) :-          % and
    evaluate_immediate_condition(X),
    !,
    evaluate_immediate_condition(Y).

evaluate_immediate_condition((X;_)) :-          % or: lhs
    evaluate_immediate_condition(X).

evaluate_immediate_condition((_;Y)) :-          % or: rhs
    evaluate_immediate_condition(Y).

evaluate_immediate_condition(integer(N)) :-
    novars(N),
    signed_integer(N).

evaluate_immediate_condition(integer(N)) :-
    integer(N).

evaluate_immediate_condition(intexp(N)) :-
    novars(N),
    intexp(N).

evaluate_immediate_condition(checktype(E,T)) :-
    novars(E),
    evaluate_immediate_condition_checktype(T),
    !,
    checktype(E,T).

evaluate_immediate_condition(simplify(E,V)) :-
    novars(E),
    evaluate_immediate_condition_simplify(V),
    !,
    simplify(E,V).

%-------------------------------------------------------------------------------
% evaluate_immediate_condition_checktype(+E,+T)
%-------------------------------------------------------------------------------

evaluate_immediate_condition_checktype(T) :-
    var(T).

evaluate_immediate_condition_checktype(T) :-
    atom(T).

%-------------------------------------------------------------------------------
% evaluate_immediate_condition_simplify(+V)
%-------------------------------------------------------------------------------

evaluate_immediate_condition_simplify(V) :-
    var(V).
evaluate_immediate_condition_simplify(V) :-
    novars(V).

%-------------------------------------------------------------------------------
% determine_facts_and_promote_to_hypotheses(From, RuleMatchDetails,
%                                          NewRuleMatchDetails) :-
%
% Promote facts into the hypotheses and convert references to them.
%-------------------------------------------------------------------------------

determine_facts_and_promote_to_hypotheses(From, RuleMatchDetails,
                                          NewRuleMatchDetails) :-
    determine_facts_and_promote_to_hypotheses_x(RuleMatchDetails,
                                                Conds,
                                                NewRuleMatchDetails,
                                                NewConds),
    !,
    gather_facts_list([from(From)|Conds], FactNos),
    sort(FactNos, SortedFactNos),
    promote_to_hypotheses(SortedFactNos),
    convert_conditions_list(Conds, NewConds).

%-------------------------------------------------------------------------------
% determine_facts_and_promote_to_hypotheses_x(+RuleMatchDetails, +Conds,
%                                             -NewRuleMatchDetails, -NewConds).
%-------------------------------------------------------------------------------

determine_facts_and_promote_to_hypotheses_x(inference(Conds), Conds,
                                            inference(NewConds), NewConds).

determine_facts_and_promote_to_hypotheses_x(rewrite(Goal, Conds), Conds,
                                            rewrite(Goal, NewConds), NewConds).

%-------------------------------------------------------------------------------
% gather_facts_list([H|T], FactsId_IntList)
%-------------------------------------------------------------------------------

gather_facts_list([], []).

gather_facts_list([H|T], Facts) :-
    gather_facts(H, F1),
    !,
    gather_facts_list(T, F2),
    !,
    append(F1, F2, Facts).

%-------------------------------------------------------------------------------
% gather_facts(A, Id_IntList).
%-------------------------------------------------------------------------------

gather_facts(from(fact(N)), [N]).
gather_facts(from(_), []).
gather_facts(proved(_, _, F), F).
gather_facts(_, []).

%-------------------------------------------------------------------------------
% promote_to_hypotheses(Id_IntList)
%-------------------------------------------------------------------------------

promote_to_hypotheses([]).

promote_to_hypotheses([H_Id_Int|T_Id_IntList]) :-
    promote_fact_to_hypothesis(H_Id_Int),
    !,
    promote_to_hypotheses(T_Id_IntList).

%-------------------------------------------------------------------------------
% promote_to_hypotheses(IntList)
%-------------------------------------------------------------------------------











%Add hyp for fact not seen before.

promote_fact_to_hypothesis(Id_Int) :-
    get_fact_from_rule(Fact, Id_Int, From, Name, RuleMatchDetails, RuleSort),
    promote_fact_to_hypothesis_x(RuleMatchDetails, Conds,
                                 NewRuleMatchDetails, NewConds),
    !,
    convert_conditions_list(Conds, NewConds),
    add_hyp(Fact, x, HypNo),
    issue_message('Applied rule ', Name),
    assert_log_fact(applied_rule,
                    [HypNo, Fact, Name, NewRuleMatchDetails, RuleSort]),
    !,
    %Replace the processed fact, with a form that records the mapping.  The
    %mapping form has empty RuleMatchDetails, and thus will not trigger the
    %adding of a hypothesis again.
    prune_fact_from_rule(Fact, Id_Int, From, Name, RuleMatchDetails, RuleSort),
    add_fact_from_rule(true, Id_Int, From, hyp(HypNo), [], RuleSort),
    % record the mapping
    !.

%Silently skip over a fact that has been seen before.
promote_fact_to_hypothesis(Id_Int) :-
    get_fact_from_rule(true, Id_Int, _From, hyp(_HypNo), [], _RuleSort),
    !.




%-------------------------------------------------------------------------------
% promote_fact_to_hypothesis_x(+RuleMatchDetails, Conds,
%                              -NewRuleMatchDetails, -NewConds)
%-------------------------------------------------------------------------------

promote_fact_to_hypothesis_x(inference(Conds), Conds,
                             inference(NewConds), NewConds).

promote_fact_to_hypothesis_x(rewrite(Goal, Conds), Conds,
                             rewrite(Goal, NewConds), NewConds).

%===============================================================================

%###############################################################################
% UTILITY PREDICATES
%###############################################################################

%===============================================================================
% convert_conditions_list(+Cond_List, -Cond_List)
%===============================================================================

convert_conditions_list([], []).

convert_conditions_list([Cond|Conds], [NewCond|NewConds]) :-
    convert_condition(Cond, NewCond),
    !,
    convert_conditions_list(Conds, NewConds).

%===============================================================================
% convert_condition(proved(+Formula_Exp, +Id_IntList, +Fs),
%                          proved(+Formula_Exp, -NewId_IntList, [])) :-
%===============================================================================

convert_condition(proved(Formula_Exp, A_HypId_IntList, F_HypId_IntList),
                  proved(Formula_Exp, NewHypId_IntList, [])) :-
    lookup_fact_mappings(F_HypId_IntList, B_HypId_IntList),
    append(A_HypId_IntList, B_HypId_IntList, A_B_HypId_IntList),
    !,
    sort(A_B_HypId_IntList, NewHypId_IntList).

%===============================================================================
% lookup_fact_mappings(+ExprList, -Id_IntList)
%===============================================================================

lookup_fact_mappings([], []).

lookup_fact_mappings([H_Exp|T_ExpList], [H_Int|T_IntList]) :-
    get_fact_from_rule(true, H_Exp, _, hyp(H_Int), [], _),
    !,
    lookup_fact_mappings(T_ExpList, T_IntList).

lookup_fact_mappings([H_Exp|T_ExpList], [H_Int|T_IntList]) :-
    get_fact_from_rule(Fact_Exp, H_Exp, _, _, _, _),
    get_hyp(Fact_Exp, _, H_Int),
    !,
    lookup_fact_mappings(T_ExpList, T_IntList).

%===============================================================================

%===============================================================================
% add_fact(+Fact_Atom)
%
% Add fact if not already known.
%===============================================================================

add_fact(Fact_Atom) :-
    call(Fact_Atom),
    !.

add_fact(Fact_Atom) :-
    assertz(Fact_Atom),
    !.

%===============================================================================

%###############################################################################
% Predicates for managing temporary state.
%###############################################################################

%===============================================================================
% Temporary state: used_unique_reference/2
%===============================================================================

:- dynamic(used_unique_reference/2).

%===============================================================================
% get_unique_reference(+Label_Atom, -Id_Int)
%
% Returns unique reference for given label by incrementing the last
% reference by one if a reference for the label exists; otherwise, 1.
%===============================================================================

get_unique_reference(Label, N) :-
    retract(used_unique_reference(Label, M)),
    !,
    N is M+1,
    asserta(used_unique_reference(Label, N)),
    !.

get_unique_reference(Label, 1) :-
    asserta(used_unique_reference(Label, 1)),
    !.

%===============================================================================
% prune_all_unique_references
%
% Retract all used_unique_reference from the database.
%===============================================================================

prune_all_unique_references:-
    retractall(used_unique_reference(_, _)),
    !.

%===============================================================================

%===============================================================================
% Temporary state: get_fact_from_rule
%===============================================================================

:- dynamic get_fact_from_rule/6.

%===============================================================================
% Schema for get_fact_from_rule
% get_fact_from_rule(Goal_Exp, RefId_Int,
%                    From, Name_CharList, RuleMatchDetails, RuleSort)
%
% Goal_Exp - The goal satisfied. It seems that these are logically hypotheses
%            terms that are known to be true.
%
% RefId_Int - a unique reference number for the fact.
%
% From - Operation for which the rule was applied. May be:
%         conc
%         rule_phase_inference
%         fact(N) (a fact number)
%         hyp(N)  (a hyp number)
%
%
% Name_CharList - Name of applied rule of the form: file:rulename.
%
% RuleMatchDetails - How the rule was applied of the form:
%            inference(ProvedConditions)
%            rewrite(Formula, ProvedConditions)
%
% Where: ProvedConditions takes the form:
%   [proved(Condition, HypNumberList, FactNumberList), ...]
%
% RuleSort - the rule classification. May be:
%   user_inference_rule
%   inference_rule
%
%===============================================================================

%===============================================================================
% add_fact_from_rule(+Goal, +Ref, +From, +Name, +RuleMatchDetails, +RuleSort)
%
% Record derivation of Goal from user defined rules.
%===============================================================================

add_fact_from_rule(Goal, Ref, From, Name, RuleMatchDetails, RuleSort):-
    assertz(get_fact_from_rule(Goal, Ref, From, Name,
                               RuleMatchDetails, RuleSort)).

%===============================================================================
% prune_fact_from_rule(?Goal, ?Ref, ?From, ?Name, ?RuleMatchDetails, ?RuleSort)
%
% Retract matching get_fact_from_rule.
%===============================================================================

prune_fact_from_rule(Goal, Ref, From, Name, RuleMatchDetails, RuleSort):-
    retract(get_fact_from_rule(Goal, Ref, From, Name,
                               RuleMatchDetails, RuleSort)).

%===============================================================================
% prune_all_fact_from_rule
%
% Retract all get_fact_from_rule.
%===============================================================================

prune_all_fact_from_rule :-
    retractall(get_fact_from_rule(_,_,_,_,_,_)).

%===============================================================================

%===============================================================================
% Temporary state: get_candidate_fact
%===============================================================================

:- dynamic get_candidate_fact/2.

%===============================================================================
% add_candidate_fact(+N, +Formula)
%===============================================================================

add_candidate_fact(N, Formula):-
    assertz(get_candidate_fact(N, Formula)).

%===============================================================================
% prune_all_candidate_fact
%===============================================================================

prune_all_candidate_fact:-
    retractall(get_candidate_fact(_, _)).

%===============================================================================

%###############################################################################
% END-OF-FILE
