% -----------------------------------------------------------------------------
%  (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 predicates for working with quantifiers.
%###############################################################################

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

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

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

%###############################################################################
% PREDICATES
%###############################################################################

%===============================================================================
% form_instantiable_hyp_facts.
%-------------------------------------------------------------------------------
% This predicate sets up a collection of predicates which can be
% pattern-matched against to automate use of the universally-quantified
% hypothesis formulae in discharging conclusions.
%
% Each for_all will typically be of the form (or worse!):
% for_all(i:ordtype, lwb <= i and i <= upb -> (element(a, [i]) >= x and
%         element(a, [i]) <= y))
%
% This predicate creates a set of facts of the form:
% get_forall_hyp(STROB >= LWB, CONDS, N)
%
% Where N is the hypothesis number from which the fact was derived, STROB
% is typically the structured object access, and CONDS is a list of
% conditions that must be true to allow the hypothesis to be used.  For
% instance, the above, if it were H3, would give us:
% get_forall_hyp(element(a, [I]) >= x, [lwb <= I, I <= upb], 3).
% get_forall_hyp(3, element(a, [I]) <= y, [lwb <= I, I <= upb], 3).
%
% In these facts, bound variables are replaced by Prolog variables, so
% standard Prolog pattern-matching may be used to find matches directly.
% Furthermore, these facts are set up to eliminate all nested for_alls as
% far as possible, so a formula:
% for_all(i:ordt1, l1 <= i and i <= u1 -> for_all(j:ordt2, l2 <= j and
%         j <= u2 -> (element(element(a, [i]), [j]) >= lwb and
%         element(element( a, [i]), [j]) <= upb)))
% Should yield a collection of facts such as:
% get_forall_hyp(element(element(a, [I]), [J]) >= lwb, [l1 <= I, I <= u1,
%              l2 <= J, J <= u2], 4).
% etc.
%===============================================================================

form_instantiable_hyp_facts :-
        prune_all_forall_hyp,
        fail.

form_instantiable_hyp_facts :-
        get_hyp(for_all(X:T, P), x, N),
        save_skolemisation_of(N, for_all(X:T, P)),
        fail.   /* force backtracking, to do all relevant hypotheses */

form_instantiable_hyp_facts.

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

save_skolemisation_of(N, Formula) :-
        skolemise(Formula, Skolemisation, Conditions),
        !,
        save_the_skolemisations(N, Skolemisation, Conditions),
        !.

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

save_the_skolemisations(N, VAR, C) :-
        var(VAR),
        !,
        add_forall_hyp(VAR, C, N).
save_the_skolemisations(N, X and Y, C) :-
        add_forall_hyp(X and Y, C, N),
        !,
        save_the_skolemisations(N, X, C),
        !,
        save_the_skolemisations(N, Y, C),
        !.
save_the_skolemisations(N, X, C) :-
        add_forall_hyp(X, C, N),
        !.

%===============================================================================
% skolemise(+Formula, -Goal_Part, -Conditions_List).
%-------------------------------------------------------------------------------
% Unwraps for_alls, splits implications up into Conditions (LHS) and Goal
% parts (RHS).  Unwrapping involves replacing the bound variable
% consistently throughout the formula with a new Prolog variable (the "X"
% in the relevant clauses below), but ensuring this variable does not
% become instantiated or "entangled" (to use the quantum terminology!)
% before the relevant expressions-with-Prolog-variables get returned ready
% to be asserted by save_the_skolemisations in the calling environment.
%===============================================================================

skolemise(VAR, VAR, []) :-      /* Need? */
        var(VAR),
        !.

skolemise(for_all(_V:_T, VAR), VAR, []) :-
        var(VAR),
        !.

skolemise(for_all(V:_T, LHS -> RHS), Formula, Conditions) :-
        introduce_prolog_variable(_X, V, LHS -> RHS, L -> R), /* X: new uncaptured variable */
        !,
        skolemise(R, Formula, C2),
        form_conditions(L, C1),
        !,
        append(C1, C2, Conditions).

skolemise(for_all(V:_T, RHS), Formula, Conditions) :-   /* No implication */
        introduce_prolog_variable(_X, V, RHS, R),
        !,
        skolemise(R, Formula, Conditions).

skolemise(LHS -> RHS, Formula, Conditions) :-
        !,
        skolemise(RHS, Formula, C2),
        form_conditions(LHS, C1),
        !,
        append(C1, C2, Conditions).

skolemise(Formula, Formula, []).

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

/* introduce_prolog_variable(NewVar, {for} Atom, OldFormula, NewFormula) */
introduce_prolog_variable(_X, _V, Old, Old) :-
        var(Old),
        !.

introduce_prolog_variable(X, V, V, X) :-
        !.

introduce_prolog_variable(_X, _V, Old, Old) :-
        atomic(Old),    /* and not equal to V */
        !.

introduce_prolog_variable(X, V, [H|T], New) :-
        !,
        introduce_prolog_variable_in_list(X, V, [H|T], New).

introduce_prolog_variable(_X, _V, [], []) :-
        !.

introduce_prolog_variable(X, V, Old, New) :-    /* Old must be composite */
        Old =.. [F|OldArgs],
        introduce_prolog_variable_in_list(X, V, OldArgs, NewArgs),
        !,
        New =.. [F|NewArgs].

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

introduce_prolog_variable_in_list(X, V, [OldH|OldT], [NewH|NewT]) :-
        introduce_prolog_variable(X, V, OldH, NewH),
        !,
        introduce_prolog_variable_in_list(X, V, OldT, NewT).

introduce_prolog_variable_in_list(_X, _V, [], []) :-
        !.

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

form_conditions(VAR, [VAR]) :-
        var(VAR),
        !.

form_conditions(L and R, List) :-
        form_conditions(L, L1),
        !,
        form_conditions(R, L2),
        !,
        append(L1, L2, List).

form_conditions(L, [L]) :-
        !.

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