% -----------------------------------------------------------------------------
%  (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
%-------------------------------------------------------------------------------
% Facilities to prove that a relational expression is true, given the
% current hypotheses of the VC.
%###############################################################################


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


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


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


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


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


%===============================================================================
% int(+Exp).
%-------------------------------------------------------------------------------
% Integer test.
%===============================================================================

int(X) :- (integer(X) ; (X=(-Y), integer(Y))), !.

%===============================================================================
% testused(+Exp).
%-------------------------------------------------------------------------------
% Simple inference.
%===============================================================================

testused(X=Y) :-
    (
        used(X=Y)
    ;
        used(Y=X)
    ),
    !,
    fail.

testused(X=Y) :-
    advance_used_facts(X=Y).
testused(X=Y) :-
    retreat_used_facts(X=Y).

testused(X>=Y) :-
    used(X>=Y),
    !,
    fail.

testused(X>=Y) :-
    advance_used_facts(X>=Y).
testused(X>=Y) :-
    retreat_used_facts(X>=Y).

testused(X>Y) :-
    used(X>Y),
    !,
    fail.

testused(X>Y) :-
    advance_used_facts(X>Y).
testused(X>Y) :-
    retreat_used_facts(X>Y).

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

advance_used_facts(X=Y) :-
    (\+ X=Y),
    (\+ used(X=Y)),
    (\+ used(Y=X)),
    assertz(used(X=Y)),
    !.
advance_used_facts(X>=Y) :-
    (\+ used(X>=Y)),
    assertz(used(X>=Y)),
    !.
advance_used_facts(X>Y) :-
    (\+ used(X>Y)),
    assertz(used(X>Y)),
    !.

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

retreat_used_facts(X=Y) :-
    retract(used(X=Y)),
    !,
    fail.
retreat_used_facts(X=Y) :-
    retract(used(Y=X)),
    !,
    fail.
retreat_used_facts(X>=Y) :-
    retract(used(X>=Y)),
    !,
    fail.
retreat_used_facts(X>Y) :-
    retract(used(X>Y)),
    !,
    fail.

%===============================================================================
% fact(?Hyp_Term, ?HypId_List)
%-------------------------------------------------------------------------------
% Hyp_Term is a hypothesis or is equivalent to a hypothesis.
%===============================================================================

fact(Hyp_Term, [HypId_Int]) :-
    get_hyp(Hyp_Term, x, HypId_Int).

fact(X=Y, [HypId_Int])  :-
        (
            get_hyp(not X<>Y, x, HypId_Int)
        ;
            get_hyp(Y=X, x, HypId_Int)
        ;
            get_hyp(not Y<>X, x, HypId_Int)
        ).

fact(X<>Y, [HypId_Int]) :-
        (
            get_hyp(not X=Y, x, HypId_Int)
        ;
            get_hyp(Y<>X, x, HypId_Int)
        ;
            get_hyp(not Y=X, x, HypId_Int)
        ).

fact(X>Y, [HypId_Int])  :-
        (
            get_hyp(not X<=Y, x, HypId_Int)
        ;
            get_hyp(Y<X, x, HypId_Int)
        ;
            get_hyp(not Y>=X, x, HypId_Int)
        ).

fact(X<Y, [HypId_Int])  :-
        (
            get_hyp(not X>=Y, x, HypId_Int)
        ;
            get_hyp(Y>X, x, HypId_Int)
        ;
            get_hyp(not Y<=X, x, HypId_Int)
        ).

fact(X>=Y, [HypId_Int]) :-
        (
            get_hyp(not X<Y, x, HypId_Int)
        ;
            get_hyp(Y<=X, x, HypId_Int)
        ;
            get_hyp(not Y>X, x, HypId_Int)
        ).

fact(X<=Y, [HypId_Int]) :-
        (
            get_hyp(not X>Y, x, HypId_Int)
        ;
            get_hyp(Y>=X, x, HypId_Int)
        ;
            get_hyp(not Y<X, x, HypId_Int)
        ).

%===============================================================================
% infrule(+Exp, -Hs).
%-------------------------------------------------------------------------------
% Infer Exp from hypothesis, reporting those hypotheses used as Hs.
%===============================================================================

infrule(X, Hs) :- standard_infrule(X, Hs).
infrule(X, Hs) :- new_strategies_are_allowed, extended_infrule(X, Hs).

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

standard_infrule(X,Hs) :- fact(X,Hs).

standard_infrule(X=Y,Hs) :- fact(X>=Y,H1), fact(X<=Y,H2), append(H1, H2, Hs).

standard_infrule(X<>Y,Hs) :- fact(X>Y,Hs) ; fact(X<Y,Hs).

standard_infrule(X>Y,Hs) :- fact(X>=Y,H1), fact(X<>Y,H2), append(H1, H2, Hs).

standard_infrule(X<Y,Hs) :- fact(X<=Y,H1), fact(X<>Y,H2), append(H1, H2, Hs).

standard_infrule(X>=Y,Hs) :- (fact(X=Y, Hs), testused(X=Y)) ; fact(X>Y,Hs).

standard_infrule(X<=Y,Hs) :- (fact(X=Y,Hs), testused(X=Y)) ; fact(X<Y,Hs).

standard_infrule(X <= N, Hs) :-
    \+ int(X),
    int(N),
    known_upper_numeric_limit(X, U, _, Hs),
    simplify(U <= N, true).

standard_infrule(N <= X, Hs) :-
    \+ int(X),
    int(N),
    known_lower_numeric_limit(X, L, _, Hs),
    simplify(N <= L, true).

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




% These clauses only operate during the later stages of simplification,
% when allow_new_strategies has been asserted.  They make use of the
% forall_hyp clauses created for proof-framing, and allow the Simplifier
% to spot, for instance, that element(a,[n])>=v holds given a hypothesis of
% the form for_all(i:t, element(a,[i])>=v and element(a,[i])<=u) (plus
% other variants involving range constraints on the left of implications
% within the quantified formulae, use of record functions, etc.).  In each
% case, one side of the inequality (<= or >= only) must be either an array
% access or a record access.
extended_infrule(X<=Y, Hs) :-
    novars(X),
    (
        X = element(_,_)
    ;
        record_function(_, X, access, _, _, _)
    ),
    (
        get_forall_hyp(X <= Y, Conditions, N)
    ;
        get_forall_hyp(Y >= X, Conditions, N)
    ),
    novars(Y),
    testused(Y>=X),
    var_free(Conditions),
    decrement_inference_depth_remaining(infrule),
    (
        do_infer_side_conditions(Conditions, HCL),
        Success = true
    ;
        Success = fail
    ),
    !,
    increment_inference_depth_remaining(infrule),
    call(Success),
    !,
    merge_sort([N], HCL, Hs).

extended_infrule(X<=Y, Hs) :-
    novars(Y),
    (
        Y = element(_,_)
    ;
        record_function(_, Y, access, _, _, _)
    ),
    (
        get_forall_hyp(X <= Y, Conditions, N)
    ;
        get_forall_hyp(Y >= X, Conditions, N)
    ),
    novars(X),
    testused(Y>=X),
    var_free(Conditions),
    decrement_inference_depth_remaining(infrule),
    (
        do_infer_side_conditions(Conditions, HCL),
        Success = true
    ;
        Success = fail
    ),
    !,
    increment_inference_depth_remaining(infrule),
    call(Success),
    !,
    merge_sort([N], HCL, Hs).

extended_infrule(X>=Y, Hs) :-
    novars(X),
    (
        X = element(_,_)
    ;
        record_function(_, X, access, _, _, _)
    ),
    (
        get_forall_hyp(X >= Y, Conditions, N)
    ;
        get_forall_hyp(Y <= X, Conditions, N)
    ),
    novars(Y),
    testused(X>=Y),
    var_free(Conditions),
    decrement_inference_depth_remaining(infrule),
    (
        do_infer_side_conditions(Conditions, HCL),
        Success = true
    ;
        Success = fail
    ),
    !,
    increment_inference_depth_remaining(infrule),
    call(Success),
    !,
    merge_sort([N], HCL, Hs).

extended_infrule(X>=Y, Hs) :-
    novars(Y),
    (
        Y = element(_,_)
    ;
        record_function(_, Y, access, _, _, _)
    ),
    (
        get_forall_hyp(X >= Y, Conditions, N)
    ;
        get_forall_hyp(Y <= X, Conditions, N)
    ),
    novars(X),
    testused(X>=Y),
    var_free(Conditions),
    decrement_inference_depth_remaining(infrule),
    (
        do_infer_side_conditions(Conditions, HCL),
        Success = true
    ;
        Success = fail
    ),
    !,
    increment_inference_depth_remaining(infrule),
    call(Success),
    !,
    merge_sort([N], HCL, Hs).

%===============================================================================
% deduce(+R,+T,-Hs).
%-------------------------------------------------------------------------------
% Deduce relational expression R (whose lhs and rhs are of type T) from VC,
% giving Hs instantiated to list of hypotheses used.
%===============================================================================

deduce(true,_,[]).
deduce(X,_,Hs) :- infrule(X,Hs).

% Numeric upper bounds
%==================================================





% These rules cater for an integer bound N in the goal formula, and search
% for corresponding facts (via infrule) which give an integer bound
% sufficient to prove that N is a bound also -- e.g. if X<=K and K<=N, then
% X<=N.

% Less than or equal to
%----------------------

deduce(X<=N, T, Hs) :-
    i_am_using_rule(le_trans_1),
    int(N),
    (
        infrule(X < K, Hs),
        int(K),
        T=integer,
        % For integers: X<=M, where M=K-1
        M=K-1
    ;
        infrule(X <= K, Hs),
        testused(K >= X),
        int(K),
        M=K
    ),
    simplify(M<=N, true),
    !.

deduce(N<=X, T, Hs) :-
    i_am_using_rule(le_trans_2),
    int(N),
    (
        infrule(K < X, Hs),
        int(K),
        T=integer,
        % For integers: M<=X where M=K+1
        M=K+1
    ;
        infrule(K <= X, Hs),
        testused(X >= K),
        int(K),
        M=K
    ),
    simplify(N<=M, true),
    !.


% Support reasoning of the form:
% (X < K and K <= (N+1)) -> (X <= N)
% Where N is an integer literal. This pattern can crop up when traversing
% an array in SPARK using a "for" loop.
deduce(X<=N, T, Hs) :-
    i_am_using_rule(le_trans_3),
    int(N),
    T=integer,
    M iss N+1,
    infrule(X<K, H1),
    infrule(K<=M, H2),
    !,
    merge_sort(H1, H2, Hs).



% Support reasoning of the form:
% (X + 1 < K and K <= (N+2)) -> (X <= N)
% where N is an integer literal.
%
% This is really a generalisation of le_trans_3 where terms like (N + 1) +
% 1 get evaluated earlier to N + 2, and so no longer match le_trans_3
% above.  A full generalisation of this rule to handle:
%    X + C <  K and
%        K <= N + C + 1
%         ->
%        X <= N
% where C = 0           (le_trans_3 above)
%       C = 1           (this rule)
%       C = 2, 3, 4 ... (could be considered in future)
deduce(X<=N, T, Hs) :-
    i_am_using_rule(le_trans_3b),
    int(N),
    T=integer,
    M iss N+2,
    infrule(X+1<K, H1),
    infrule(K<=M, H2),
    !,
    merge_sort(H1, H2, Hs).



% There is a general need for reasoning about VCs of the form:
%
% H: X <= EXP
% ->
% C: X <= S(EXP)
%
% Where EXP is an expression and S is an operation (or series of operations)
% on that expression. This situation occurs where reasoning about loop
% iteration. For example, where reasoning about a for-loop, a variable I
% will be incremented (or decremented) on each iteration, leading to loop
% iteration VCs of the form: I<=EXP -> I <= EXP+1.
%
% (Such goals conform to the Rippling heuristic. However, this would require
% a significant re-engineering of the Simplifier to implement. Instead the
% specific case identified here is directly targeted with a hard coded
% transitivity rule.)
%
% Essentially, the game is to exploit (fertilise with) hypothesis H,
% enabling the conclusion to be simplified, in the hope that the simplified
% conclusion is now more tractable.  Transitivity enables 'C' to be broken
% into two conjuncts. It can be arranged so that one of the conjuncts match
% with 'H'. Hopefully, the second conjunct (the proof residue) should be
% simpler.

% Note that a single transitivity rule is hard coded into this strategy.
% Other rules have the potential to be useful. However, as the examiner
% pushes variables to the left and their constraints to the right (Var <=
% Constraint), other transitive rules will cost the same, but have less
% impact.
deduce(I<=K, _T, Hs) :-
    i_am_using_rule(le_trans_4),

    % The checker gives us:
    % transitivity(10): K>=I may_be_deduced_from [ I<=J, J<=K ].
    % Which can be expressed as:
    % ((I<=J) and (J<=K)) -> I<=K
    % So:
    % Find a hypothesis of the form: I<=J (making this conjunct trivially true)
    % Which will instantiate: J<=K (if this is true, can do the deduction)
    infrule((I<=J), Hs),

    %Choose to restrict the J to non-integers and non-atomic items. (As
    %these cases are handled quite well elsewhere.)
    \+ int(J),
    \+ atomic(J),

    % Must be able to prove the residue! Ideally, this should be undertaken
    % as a subgoal, enabling the reuse of other strategies and exploiting of
    % hypotheses. However, this is quite difficult to implement. Instead look
    % for the term being internally true (no hypotheses needed) through a
    % combination of normalisation and simplification. This has the capacity
    % to be effective on more simple problems.
    norm_typed_expr((J<=K), boolean, Normalised),
    simplify(Normalised, true),
    !.

% Greater than or equal to
%-------------------------

deduce(N>=X, T, Hs) :-
    i_am_using_rule(ge_trans_1),
    int(N),
    (
        infrule(X < K, Hs),
        int(K),
        T=integer,
        % For integers: X<=M, where M=K-1
        M=K-1
    ;
        infrule(X <= K, Hs),
        testused(K >= X),
        int(K),
        M=K
    ),
    simplify(M<=N, true),
    !.

deduce(X>=N, T, Hs) :-
    i_am_using_rule(ge_trans_2),
    int(N),
    (
        infrule(K < X, Hs),
        int(K),
        T=integer,
        % For integers: M<=X where M=K+1
        M=K+1
    ;
        infrule(K <= X, Hs),
        testused(X >= K),
        int(K),
        M=K
    ),
    simplify(N<=M, true),
    !.


% Allow reasoning of the form:
%   (X > K and K >= (N-1)) -> (X >= N)
% where N is an integer literal. This pattern can crop up when traversing
% an array in SPARK using a "reverse for" loop.
deduce(X>=N, T, Hs) :-
    i_am_using_rule(ge_trans_3),
    int(N),
    T=integer,
    M iss N-1,
    infrule(X>K, H1),
    infrule(K>=M, H2),
    !,
    merge_sort(H1, H2, Hs).

% Less than
%----------

deduce(X<N, _, Hs) :-
    i_am_using_rule(lt_trans_1),
    int(N),
    (
        infrule(X < K, Hs),
        int(K),
        simplify(K<=N, true)
    ;
        infrule(X <= K, Hs),
        testused(K >= X),
        int(K),
        simplify(K<N, true)
    ),
    !.

deduce(N<X, _, Hs) :-
    i_am_using_rule(lt_trans_2),
    int(N),
    (
        infrule(K < X, Hs),
        int(K),
        simplify(N<=K, true)
    ;
        infrule(K <= X, Hs),
        testused(X >= K),
        int(K),
        simplify(N<K, true)
    ),
    !.

% Greater than
%-------------

deduce(N>X, _, Hs) :-
    i_am_using_rule(gt_trans_1),
    int(N),
    (
        infrule(X < K, Hs),
        int(K),
        simplify(K<=N, true)
    ;
        infrule(X <= K, Hs),
        testused(K >= X),
        int(K),
        simplify(K<N, true)
    ),
    !.

deduce(X>N, _, Hs) :-
    i_am_using_rule(gt_trans_2),
    int(N),
    (
        infrule(K < X, Hs),
        int(K),
        simplify(N<=K, true)
    ;
        infrule(K <= X, Hs),
        testused(X >= K),
        int(K),
        simplify(N<K, true)
    ),
    !.


% Simple addition and subtraction
%==================================================

% Equality
%---------

deduce(X+Y=Z,T,Hs) :-
    i_am_using_rule(eq_1),
    (
        infrule(Y+X=Z,Hs)
    ;
        infrule(Y=Z-X,Hs)
    ;
        evaluate(-X,W),
        infrule(Y=W+Z,Hs)
    ;
        % X is an integer.
        int(X),
        (
            % If X=0 then prove Y=Z or fail.
            simplify(X=0,true),
            (
                deduce(Y=Z,T,Hs)
            ;
                !, fail
            )
       ;
            % If Y is an integer, then prove that Y+X=Z, or fail.
            int(Y),
            (
                evaluate(Y+X,W),
                deduce(Z=W,T,Hs)
            ;
                !, fail
            )
       ;
            % If Z is an integer, prove that Z-X=Y, or fail.
            int(Z),
            (
                evaluate(Z-X,W),
                deduce(Y=W,T,Hs)
            ;
                !, fail
            )
       )
    ;
        % If neither X nor Y is an integer, try finding a substitution for Y
        % and proving the resulting equality, or fail.
        (\+ int(Y)),
        (\+ int(Z)),
        (
            nonvar(Y),
            infrule(Y=W,H1),
            testused(Y=W),
            deduce(X+W=Z,T,H2),
            append(H1,H2,Hs)
        ;
            !, fail
        )
    ).

% Alternative representations of N+X=Y, where N is an integer. Each is
% converted into N+X=Y form and the code for X+Y=Z above is used to attempt
% the proof. The conditions used prevent looping, e.g. by using 2+a=3+b if
% 3+b=2+a if ...

deduce(X+N=Y,T,Hs) :- int(N), (\+ int(X)), deduce(N+X=Y,T,Hs).

deduce(X-N=Y,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X=Y,T,Hs).

deduce(Y=X-N,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X=Y,T,Hs).

deduce(N-X=Y,T,Hs) :- int(N), evaluate(-X,Z), deduce(N+Z=Y,T,Hs).

deduce(Y=N+X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X=Y,T,Hs).

deduce(Y=X+N,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X=Y,T,Hs).

deduce(Y=N-X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, evaluate(-X,Z),
                      deduce(N+Z=Y,T,Hs).

deduce(X+Y=N,T,Hs) :- int(N), \+ int(X), \+ int(Y), evaluate(-X,Z),
                      deduce(N+Z=Y,T,Hs).

deduce(X-Y=N,T,Hs) :- int(N), \+ int(X), \+ int(Y), deduce(N+Y=X,T,Hs).

% Inequality
%-----------

deduce(X-A<>X-B,T,Hs) :- deduce(A<>B,T,Hs).
deduce(X+A<>X+B,T,Hs) :- deduce(A<>B,T,Hs).

% Alternative representations of N+X<>Y, where N is an integer. Each is
% converted into N+X<>Y form and the code for X+Y=\Z on the next page used
% to attempt the proof. The conditions used prevent infinite looping.

deduce(X+N<>Y,T,Hs) :- int(N), (\+ int(X)), deduce(N+X<>Y,T,Hs).

deduce(X-N<>Y,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X<>Y,T,Hs).

deduce(Y<>X-N,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X<>Y,T,Hs).

deduce(N-X<>Y,T,Hs) :- int(N), evaluate(-X,Z), deduce(N+Z<>Y,T,Hs).

deduce(Y<>N+X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X<>Y,T,Hs).

deduce(Y<>X+N,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X<>Y,T,Hs).

deduce(Y<>N-X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, evaluate(-X,Z),
                       deduce(N+Z<>Y,T,Hs).

deduce(X+Y<>N,T,Hs) :- int(N), \+ int(X), \+ int(Y), evaluate(-X,Z),
                       deduce(N+Z<>Y,T,Hs).

deduce(X-Y<>N,T,Hs) :- int(N), \+ int(X), \+ int(Y), deduce(N+Y<>X,T,Hs).

deduce(X+Y<>Z,T,Hs) :-
    i_am_using_rule(ineq_1),
    (
        infrule(Y+X<>Z,Hs)
    ;
        infrule(Y<>Z-X,Hs)
    ;
        evaluate(-X,W),
        infrule(Y<>W+Z,Hs)
    ;
        int(X),                            /* If X is an integer -           */
        (
            simplify(X=0,true),             /* - and =0, prove Y<>Z or fail   */
            (
                deduce(Y<>Z,T,Hs)
            ;
                !, fail
            )
        ;
            deduce(Y=Z,T,Hs)                /* - and <>0, prove Y=Z           */
        ;
            int(Y),                         /* - and Y is an integer, sum     */
            (                               /*   them & prove sum=Z or fail   */
                evaluate(Y+X,W),
                deduce(Z<>W,T,Hs)
            ;
                !, fail
            )
        ;
          int(Z),                         /* - and Z is an integer subtract */
          (                               /*   & prove diff<>Y or fail      */
                evaluate(Z-X,W),
                deduce(Y<>W,T,Hs)
          ;
                !, fail
          )
       )
    ;
        (\+ int(Y)),                      /* If neither Y nor Z is an       */
        (\+ int(Z)),                      /* integer, try finding a         */
        (                                  /* substitution for Y and         */
            nonvar(Y),
            infrule(Y=W,H1),                /* proving resulting inequality.  */
            testused(Y=W),
            deduce(X+W<>Z,T,H2),
            append(H1, H2, Hs)
        ;
            !, fail
        )
    ).

% Greater than
%-------------

deduce(X+Y>Z,T,Hs) :-
    i_am_using_rule(gt_1),
    (
        infrule(Y+X>Z,Hs)
    ;
        infrule(Y>Z-X,Hs)
    ;
        evaluate(-X,W),
        infrule(Y>W+Z,Hs)
    ;
        int(X),                              /* If X is an integer -           */
        (
            simplify(X=0,true),               /* - and X=0, prove Y>Z or fail   */
            ( deduce(Y>Z,T,Hs) ; !, fail )
        ;
            int(Y),                           /* - and Y is an integer, sum and */
            (                                 /*   prove sum>Z or fail          */
                evaluate(Y+X,W),
                deduce(Z<W,T,Hs)
            ;
                !, fail
            )
        ;
            int(Z),                           /* - and Z is an integer, take X  */
            (                                 /*   away & prove diff<Y or fail  */
                evaluate(Z-X,W),
                deduce(Y>W,T,Hs)
            ;
                !, fail
            )
        ;
            nonvar(Y),                        /* - and Y & Z are the same, show */
            nonvar(Z),                        /*   X>0 or fail                  */
            Y=Z,
            ( simplify(X>0,true), Hs=[] ; !, fail )
        ;
            simplify(X>0,true),               /* - and X>0, prove Y>=Z          */
            deduce(Y>=Z,T,Hs)
        ;
            T=integer,
            evaluate(X-1,W),                  /* - subtract 1 to give more      */
            (                                 /*   equivalent forms to search   */
                infrule(W+Y>=Z,Hs)             /*   for which use the operator   */
            ;                                 /*   >= instead of >.             */
                infrule(Y+W>=Z,Hs)
            ;
                infrule(Y>=Z-W,Hs)
            ;
                evaluate(-W,V),
                infrule(Y>=V+Z,Hs)
            )
        )
    ;
        (\+ int(Y)),                        /* If neither Y nor Z are         */
        (\+ int(Z)),                        /* integers, try finding a        */
        (                                    /* substitution for Y and prove   */
            (                                 /* the resulting expression.  Two */
                nonvar(Y),
                infrule(Y>W,H1),               /*     >= followed by >.          */
                deduce(X+W>=Z,T,H2),
                append(H1, H2, Hs)
            ;
                !, fail
            )
        ;
            (
                nonvar(Y),
                infrule(Y>=W,H1),
                testused(Y>=W),
                deduce(X+W>Z,T,H2),
                append(H1, H2, Hs)
            ;
                !, fail
            )
        )
    ).

% Alternate representations of N+X>Y, where N is an integer.  All are
% converted to a N+X>Y form and the above code used to attempt the proof.
% The conditions before altering expressions are included to prevent
% infinite loops.

deduce(X+N>Y,T,Hs) :- int(N), (\+ int(X)), deduce(N+X>Y,T,Hs).

deduce(X-N>Y,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X>Y,T,Hs).

deduce(Y<X-N,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X>Y,T,Hs).

deduce(N-X>Y,T,Hs) :- int(N), evaluate(-X,Z), deduce(N+Z>Y,T,Hs).

deduce(X+N>Y,T,Hs) :- i_am_using_rule(new_gt_1), int(N), simplify(N>0, true), deduce(Y<=X, T, Hs).

deduce(N+X>Y,T,Hs) :- i_am_using_rule(new_gt_2), int(N), simplify(N>0, true), deduce(Y<=X, T, Hs).

deduce(X>Y-N,T,Hs) :- i_am_using_rule(new_gt_3), int(N), simplify(N>0, true), deduce(Y<=X, T, Hs).

deduce(X-N>Y,T,Hs) :- i_am_using_rule(new_gt_4), int(N), simplify(N<0, true), deduce(Y<=X, T, Hs).

deduce(X>Y+N,T,Hs) :- i_am_using_rule(new_gt_5), int(N), simplify(N<0, true), deduce(Y<=X, T, Hs).

deduce(X>N+Y,T,Hs) :- i_am_using_rule(new_gt_6), int(N), simplify(N<0, true), deduce(Y<=X, T, Hs).

deduce(Y<N+X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X>Y,T,Hs).

deduce(Y<X+N,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X>Y,T,Hs).

deduce(Y<N-X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, evaluate(-X,Z),
                      deduce(N+Z>Y,T,Hs).

deduce(X+Y<N,T,Hs) :- int(N), (\+ int(X)), (\+ int(Y)),
                      evaluate(-X,Z), deduce(N+Z>Y,T,Hs).

deduce(X-Y<N,T,Hs) :- int(N), (\+ int(X)), (\+ int(Y)), deduce(N+Y>X,T,Hs).

% Less than
%----------

deduce(X+Y<Z,T,Hs) :-
    i_am_using_rule(lt_1),
    (
        infrule(Y+X<Z,Hs)
    ;
        infrule(Y<Z-X,Hs)
    ;
        evaluate(-X,W),
        infrule(Y<W+Z,Hs)
    ;
        int(X),                             /* If X is an integer -           */
        (
            simplify(X=0,true),              /* - and X=0, prove Y<Z or fail   */
            ( deduce(Y<Z,T,Hs) ; !, fail )
        ;
            int(Y),                          /* - and Y is an integer, sum and */
            (                                /*   prove sum<Z or fail          */
                evaluate(Y+X,W),
                deduce(Z>W,T,Hs)
            ;
                !, fail
            )
        ;
            int(Z),                          /* - and Z is an integer, take X  */
            (                                /*   away & prove diff>Y or fail  */
                evaluate(Z-X,W),
                deduce(Y<W,T,Hs)
            ;
                !, fail
            )
        ;
            nonvar(Y),                       /* - and Y & Z are the same, show */
            nonvar(Z),                       /*   X<0 or fail                  */
            Y=Z,
            ( simplify(0>X,true), Hs=[] ; !, fail )
        ;
            simplify(0>X,true),              /* - and X<0, prove Y<=Z          */
            deduce(Y<=Z,T,Hs)
        ;
            T=integer,
            evaluate(X+1,W),                 /* - add 1 to X to give more      */
            (                                /*   equivalent forms to search   */
                infrule(W+Y<=Z,Hs)            /*   for which use the operator   */
            ;                                /*   <= instead of <.             */
                infrule(Y+W<=Z,Hs)
            ;
                infrule(Y<=Z-W,Hs)
            ;
                evaluate(-W,V),
                infrule(Y<=V+Z,Hs)
            )
        )
    ;
        (\+ int(Y)),                       /* If neither Y nor Z are         */
        (\+ int(Z)),                       /* integers, try finding a        */
        (                                   /* substitution for Y and prove   */
            (                                /* the resulting expression.  Two */
                nonvar(Y),
                infrule(Y<W,H1),              /*     <= followed by <.          */
                deduce(X+W<=Z,T,H2),
                append(H1, H2, Hs)
            ;
                !, fail
            )
        ;
            (
                nonvar(Y),
                infrule(Y<=W,H1),
                testused(W>=Y),
                deduce(X+W>Z,T,H2),
                append(H1, H2, Hs)
            ;
                !, fail
            )
        )
    ).

% Alternate representations of N+X<Y, where N is an integer.  All are
% converted to a N+X<Y form and the above code used to attempt the proof.
% The conditions before altering expressions are included to prevent
% infinite loops.

deduce(X+N<Y,T,Hs) :- int(N), (\+ int(X)), deduce(N+X<Y,T,Hs).

deduce(X-N<Y,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X<Y,T,Hs).

deduce(Y>X-N,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X<Y,T,Hs).

deduce(N-X<Y,T,Hs) :- int(N), evaluate(-X,Z), deduce(N+Z<Y,T,Hs).

deduce(Y<X+N,T,Hs) :- i_am_using_rule(new_lt_1), int(N), simplify(N>0, true), deduce(Y<=X, T, Hs).

deduce(Y<N+X,T,Hs) :- i_am_using_rule(new_lt_2), int(N), simplify(N>0, true), deduce(Y<=X, T, Hs).

deduce(Y-N<X,T,Hs) :- i_am_using_rule(new_lt_3), int(N), simplify(N>0, true), deduce(Y<=X, T, Hs).

deduce(Y<X-N,T,Hs) :- i_am_using_rule(new_lt_4), int(N), simplify(N<0, true), deduce(Y<=X, T, Hs).

deduce(Y+N<X,T,Hs) :- i_am_using_rule(new_lt_5), int(N), simplify(N<0, true), deduce(Y<=X, T, Hs).

deduce(N+Y<X,T,Hs) :- i_am_using_rule(new_lt_6), int(N), simplify(N<0, true), deduce(Y<=X, T, Hs).

deduce(Y>N+X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X<Y,T,Hs).

deduce(Y>X+N,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X<Y,T,Hs).

deduce(Y>N-X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, evaluate(-X,Z),
                      deduce(N+Z<Y,T,Hs).

deduce(X+Y>N,T,Hs) :- int(N), (\+ int(X)), (\+ int(Y)),
                      evaluate(-X,Z), deduce(N+Z<Y,T,Hs).

deduce(X-Y>N,T,Hs) :- int(N), (\+ int(X)), (\+ int(Y)), deduce(N+Y<X,T,Hs).

% Greater than or equals
%-----------------------



% Special case for X+Y>=Z where Z is an integer literal - a common case for
% Range_Check in SPARK.

deduce(X+Y>=Z,_,Hs) :-
    i_am_using_rule(ge_1),
    checktype(X,integer), /* If X and Y are both integer typed... */
    checktype(Y,integer),
    int(Z),               /* ...and Z is an integer literal */
    (                     /* ... then find lower bounds on X and Y */
        infrule(X > XLminus1, H1), int(XLminus1), XL=XLminus1+1
    ;
        infrule(X >= XL, H1), int(XL)
    ),
    (
        infrule(Y > YLminus1, H2), int(YLminus1), YL=YLminus1+1
    ;
        infrule(Y >= YL, H2), int(YL)
    ),                    /* unnecessary cuts removed, ION, 25/03/04 */
    evaluate(XL + YL, LB),
    simplify(LB >= Z, true),          /* ...and compare those with Z */
    !,
    append(H1, H2, Hs).

% Special case for X+Y<=Z where Z is an integer literal - a common case for
% Range_Check in SPARK.
deduce(X+Y<=Z,_,Hs) :-
    i_am_using_rule(le_1),
    checktype(X,integer), /* If X and Y are both integer typed... */
    checktype(Y,integer),
    int(Z),               /* ...and Z is an integer literal */
    (                     /* ... then find upper bounds on X and Y */
        infrule(X < XUplus1, H1), int(XUplus1), XU=XUplus1-1
    ;
        infrule(X <= XU, H1), int(XU)
    ),
    (
        infrule(Y < YUplus1, H2), int(YUplus1), YU=YUplus1-1
    ;
        infrule(Y <= YU, H2), int(YU)
    ),                    /* unnecessary cuts removed, ION, 25/03/04 */
    evaluate(XU + YU, UB),
    simplify(UB <= Z, true),          /* ...and compare those with Z */
    !,
    append(H1, H2, Hs).

deduce(X+Y>=Z,T,Hs) :-
    i_am_using_rule(ge_2),
    (
        infrule(Y+X>=Z,Hs)
    ;
        infrule(Y>=Z-X,Hs)
    ;
        infrule(X>=Z-Y,Hs)
    ;
        evaluate(-X,W), infrule(Y>=W+Z,Hs)
    ;
        int(X),                             /* If X is an integer literal   */
        (
            simplify(X=0,true),              /* and X=0, prove Y>=Z or fail  */
            ( deduce(Y>=Z,T,Hs) ; !, fail )
        ;
            int(Y),                          /* - and Y is an integer literal , sum and */
            (                                /*   prove sum>=Z or fail                  */
                evaluate(Y+X,W), deduce(Z<=W,T,Hs)
            ;
                !, fail
            )
        ;
            int(Z),                          /* - and Z is an integer, take    */
            (                                /*   away X & show diff<=Y or fail*/
                evaluate(Z-X,W), deduce(Y>=W,T,Hs)
            ;
                !, fail
            )
        ;
            nonvar(Y), nonvar(Z),            /* - and Y & Z are the same, show */
            Y=Z,                             /*   X>=0 or fail                 */
            ( simplify(X>=0,true), Hs=[] ; !, fail )
        ;
            simplify(X>=0,true),deduce(Y>=Z,T,Hs) /* - and X>=0, prove Y>=Z         */
        ;
            T=integer,
            evaluate(X+1,W),                 /* - add 1 to X giving more       */
            (                                /*   equivalent forms using the   */
                infrule(W+Y>Z,Hs)             /*   operator > instead of >=     */
            ;
                infrule(Y+W>Z,Hs)
            ;
                infrule(Y>Z-W,Hs)
            ;
                evaluate(-W,V), infrule(Y>V+Z,Hs)
            )
        ;
            (
                infrule(W+Y>=Z,Hs)            /* - and W+Y>=Z,                  */
            ;
                infrule(Y+W>=Z,Hs)
            ;
                infrule(Y>=Z-W,Hs)
            ),
            int(W),                          /*   and W is an integer literal, */
            simplify(X>=W,true)              /*   and X>=W, then X+Y>=W+Y>=Z   */
        )
    ;
        int(Z),
        (
            infrule(X >= A, H1),              /* If X>=A and                    */
            int(A),
            simplify(Z-A, B),
            deduce(Y >= B, T, H2)             /* Y>=Z-A, then X+Y>=Z            */
        ;
            infrule(Y >= B, H1),              /* Otherwise, if Y>=B and         */
            int(B),
            simplify(Z-B, A),
            deduce(X >= A, T, H2)             /* X>=Z-B, then X+Y>=Z            */
        ),
        append(H1, H2, Hs)
    ;
        (\+ int(Y)), (\+ int(Z)),         /* If neither Y nor Z are         */
        (                                   /* integers, try finding a        */
            nonvar(Y),
            infrule(Y>=W,H1), testused(Y>=W),
            deduce(X+W>=Z,T,H2),
            append(H1, H2, Hs)
        ;
            !, fail
        )
    ).

% Alternative representations of N+X>=Y where N is an integer.  All are
% converted into N+X>=Y form and code above is used to attempt the proof.
% Conditions before altering expressions are included to prevent infinite
% looping.

deduce(X+N>=Y,T,Hs) :- int(N), (\+ int(X)), deduce(N+X>=Y,T,Hs).

deduce(X-N>=Y,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X>=Y,T,Hs).

deduce(Y<=X-N,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X>=Y,T,Hs).

deduce(N-X>=Y,T,Hs) :- int(N), evaluate(-X,Z), deduce(N+Z>=Y,T,Hs).

deduce(N-X>=M,T,Hs) :- int(N), int(M), evaluate(N-M,L), deduce(X<=L,T,Hs).

deduce(Y<=N+X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X>=Y,T,Hs).

deduce(Y<=X+N,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X>=Y,T,Hs).

deduce(X+N>=Y,T,Hs) :- i_am_using_rule(new_ge_1), int(N), simplify(N>=0, true), deduce(Y<=X, T, Hs).

deduce(N+X>=Y,T,Hs) :- i_am_using_rule(new_ge_2), int(N), simplify(N>=0, true), deduce(Y<=X, T, Hs).

deduce(X>=Y-N,T,Hs) :- i_am_using_rule(new_ge_3), int(N), simplify(N>=0, true), deduce(Y<=X, T, Hs).

deduce(X-N>=Y,T,Hs) :- i_am_using_rule(new_ge_4), int(N), simplify(N<=0, true), deduce(Y<=X, T, Hs).

deduce(X>=Y+N,T,Hs) :- i_am_using_rule(new_ge_5), int(N), simplify(N<=0, true), deduce(Y<=X, T, Hs).

deduce(X>=N+Y,T,Hs) :- i_am_using_rule(new_ge_6), int(N), simplify(N<=0, true), deduce(Y<=X, T, Hs).

deduce(Y<=N-X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, evaluate(-X,Z),
                       deduce(N+Z>=Y,T,Hs).

deduce(X+Y<=N,T,Hs) :- int(N), (\+ int(X)), (\+ int(Y)),
                       evaluate(-X,Z), deduce(N+Z>=Y,T,Hs).

deduce(X-Y<=N,T,Hs) :- int(N), (\+ int(X)), (\+ int(Y)), deduce(N+Y>=X,T,Hs).

% Less-than-or-equals
%--------------------

% Alternative representations of N+X>=Y where N is an integer.  All are
% converted into N+X>=Y form and code above is used to attempt the proof.
% Conditions before altering expressions are included to prevent infinite
% looping.

deduce(X+N<=Y,T,Hs) :- int(N), (\+ int(X)), deduce(N+X<=Y,T,Hs).

deduce(X-N<=Y,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X<=Y,T,Hs).

deduce(Y>=X-N,T,Hs) :- int(N), evaluate(-N,M), deduce(M+X<=Y,T,Hs).

deduce(N-X<=Y,T,Hs) :- int(N), evaluate(-X,Z), deduce(N+Z<=Y,T,Hs).

deduce(Y<=X+N,T,Hs) :- i_am_using_rule(new_le_1), int(N), simplify(N>=0, true), deduce(Y<=X, T, Hs).

deduce(Y<=N+X,T,Hs) :- i_am_using_rule(new_le_2), int(N), simplify(N>=0, true), deduce(Y<=X, T, Hs).

deduce(Y-N<=X,T,Hs) :- i_am_using_rule(new_le_3), int(N), simplify(N>=0, true), deduce(Y<=X, T, Hs).

deduce(Y<=X-N,T,Hs) :- i_am_using_rule(new_le_4), int(N), simplify(N<=0, true), deduce(Y<=X, T, Hs).

deduce(Y+N<=X,T,Hs) :- i_am_using_rule(new_le_5), int(N), simplify(N<=0, true), deduce(Y<=X, T, Hs).

deduce(N+Y<=X,T,Hs) :- i_am_using_rule(new_le_6), int(N), simplify(N<=0, true), deduce(Y<=X, T, Hs).

deduce(Y>=N+X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X<=Y,T,Hs).

deduce(Y>=X+N,T,Hs) :- int(N), Y\=_+_, Y\=_-_, deduce(N+X<=Y,T,Hs).

deduce(Y>=N-X,T,Hs) :- int(N), Y\=_+_, Y\=_-_, evaluate(-X,Z),
                       deduce(N+Z<=Y,T,Hs).

deduce(X+Y>=N,T,Hs) :- int(N), (\+ int(X)), (\+ int(Y)),
                       evaluate(-X,Z), deduce(N+Z<=Y,T,Hs).

deduce(X-Y>=N,T,Hs) :- int(N), (\+ int(X)), (\+ int(Y)), deduce(N+Y<=X,T,Hs).

deduce(X+Y<=Z,T,Hs) :-
    i_am_using_rule(le_2),
    (
        infrule(Y+X<=Z,Hs)
    ;
        infrule(Y<=Z-X,Hs)
    ;
        infrule(X<=Z-Y,Hs)
    ;
        evaluate(-X,W), infrule(Y<=W+Z,Hs)
    ;
        int(X),                              /* If X is an integer -           */
        (                                    /* - and X=0, prove Y>=Z or fail  */
            simplify(X=0,true),
            ( deduce(Y<=Z,T,Hs) ; !, fail )
        ;
            int(Y),                           /* - and Y is an integer, sum and */
            (                                 /*   prove sum>=Z or fail         */
                evaluate(Y+X,W), deduce(Z>=W,T,Hs)
            ;
                !, fail
            )
        ;
            int(Z),                           /* - and Z is an integer, take    */
            (                                 /*   away X & show diff<=Y or fail*/
                evaluate(Z-X,W), deduce(Y<=W,T,Hs)
            ;
                !, fail
            )
        ;
            nonvar(Y), nonvar(Z),             /* - and Y & Z are the same, show */
            Y=Z,                              /*   X<=0 or fail                 */
            ( simplify(0>=X,true), Hs=[] ; !, fail )
        ;
            simplify(0>=X,true),              /* - and X<=0, prove Y>=Z         */
            deduce(Y<=Z,T,Hs)
        ;
            T=integer,
            evaluate(X-1,W),                  /* - add 1 to X giving more       */
            (                                 /*   equivalent forms using the   */
                infrule(W+Y<Z,Hs)              /*   operator > instead of >=     */
            ;
                infrule(Y+W<Z,Hs)
            ;
                infrule(Y<Z-W,Hs)
            ;
                evaluate(-W,V), infrule(Y<V+Z,Hs)
            )
        ;
            (
                infrule(W+Y<=Z,Hs)             /* - and W+Y<=Z,                  */
            ;
                infrule(Y+W<=Z,Hs)
            ;
                infrule(Y<=Z-W,Hs)
            ),
            int(W),                           /*   and W is an integer literal, */
            simplify(X<=W,true)               /*   and X<=W, then X+Y<=W+Y<=Z   */
        )
    ;
        int(Z),
        (
            infrule(X <= A, H1),              /* If X<=A and                    */
            int(A),
            simplify(Z-A, B),
            deduce(Y <= B, T, H2)             /* Y<=Z-A, then X+Y<=Z            */
        ;
            infrule(Y <= B, H1),              /* Otherwise, if Y<=B and         */
            int(B),
            simplify(Z-B, A),
            deduce(X <= A, T, H2)             /* X<=Z-B, then X+Y<=Z            */
        ),
        append(H1, H2, Hs)
    ;
        (\+ int(Y)), (\+ int(Z)),          /* If neither Y nor Z are         */
        (                                    /* integers, try finding a        */
            nonvar(Y),
            infrule(Y<=W,H1), testused(W>=Y),
            deduce(X+W<=Z,T,H2),
            append(H1, H2, Hs)
        ;
            !, fail
        )
    ).


% Multiplication
%==================================================

% Equality
%---------

deduce(X*Y=Z,T,Hs) :-
    i_am_using_rule(eq_2),
    (
        infrule(Y*X=Z,Hs)
    ;
        int(X),                              /* If X is an integer -           */
        (
            simplify(X=0,true),               /* - and X=0, prove Z=0 or fail   */
            ( deduce(Z=0,T,Hs) ; !, fail )
        ;
            simplify(X=1,true),               /* - and X=1, prove Y=Z or fail   */
            ( deduce(Y=Z,T,Hs) ; !, fail )
        ;
            simplify(X=(-1),true),            /* - and X=-1, prove -Y=Z or fail */
            (
                evaluate(-Y,W),
                deduce(W=Z,T,Hs)
            ;
                !, fail
            )
        ;
            int(Y),                           /* - and Y is an integer, multiply*/
            (                                 /*   by X & prove prod=Z or fail  */
                evaluate(X*Y,W),
                deduce(W=Z,T,Hs)
            ;
                !, fail
            )
        ;
            infrule(Y=W,H1),                  /* - try substituting for Y       */
            testused(Y=W),
            deduce(X*W=Z,T,H2),
            append(H1, H2, Hs)
        )
    ;
        (\+ int(X)),
        (\+ int(Y)),
        int(Z),
        simplify(Z=0,true),                  /* If only Z is an integer & Z=0, */
        (                                    /* try to prove either X=0 or     */
            nonvar(X),
            deduce(X=0,T,Hs)                  /* Y=0.                           */
        ;
            nonvar(Y),
            deduce(Y=0,T,Hs)
        )
    ).

% Alternative forms of N*X=Y - converted to this form to use above code,
% avoiding looping.

deduce(X*N=Y,T,Hs) :- int(N), deduce(N*X=Y,T,Hs).

deduce(Y=N*X,T,Hs) :- int(N), Y\=_*_, deduce(N*X=Y,T,Hs).

deduce(Y=X*N,T,Hs) :- int(N), Y\=_*_, deduce(N*X=Y,T,Hs).

% Inequality
%-----------

deduce(X*Y<>Z,T,Hs) :-
    i_am_using_rule(ineq_2),
    (
        infrule(Y*X<>Z,Hs)
    ;
        int(X),                             /* If X is an integer -           */
        (
            simplify(X=0,true),              /* - and X=0, prove Z<>0 or fail  */
            ( deduce(Z<>0,T,Hs) ; !, fail )
        ;
            simplify(X=1,true),              /* - and X=1, prove Y<>Z or fail  */
            ( deduce(Y<>Z,T,Hs) ; !, fail )
        ;
            deduce(Y=Z,T,H1),                /* - and X<>1, prove Y=Z<>0       */
            (
                infrule(Y<>0,H2)
            ;
                infrule(Z<>0,H2)
            ),
            append(H1, H2, Hs)
        ;
            simplify(X=(-1),true),           /* - and X=-1, prove -Y<>Z or fail */
            (
                evaluate(-Y,W), deduce(W<>Z,T,Hs)
            ;
                !, fail
            )
        ;
            int(Y),                          /* - and Y is an integer, multiply*/
            (                                /*   by X & prove prod<>Z or fail */
                evaluate(X*Y,W), deduce(W<>Z,T,Hs)
            ;
                !, fail
            )
        ;
            infrule(Y=W,H1), testused(Y=W),  /* - try substituting for Y       */
            deduce(X*W<>Z,T,H2),
            append(H1, H2, Hs)
        )
    ;
        (\+ int(X)), (\+ int(Y)),
        int(Z),                             /* If only Z is an integer -      */
        (
            simplify(Z=0,true),              /* - and Z=0, prove neither X nor */
            deduce(X<>0,T,H1), deduce(Y<>0,T,H2),   /*   Y is zero                    */
            append(H1, H2, Hs)
        ;
            simplify(Z>0,true),              /* - and Z>0, prove X*Y<0, i.e.   */
            nonvar(X), nonvar(Y),
            (                                /*   one is greater than zero and */
                deduce(X>0,T,H1), deduce(Y<0,T,H2)  /*   the other is less than zero  */
            ;
                deduce(X<0,T,H1), deduce(Y>0,T,H2)
            ),
            append(H1, H2, Hs)
        ;
            simplify(0>Z,true),              /* - and Z<0, prove X*Y>0, i.e.   */
            nonvar(X), nonvar(Y),
            (                                /*   both X and Y have the same   */
                deduce(X>0,T,H1), deduce(Y>0,T,H2)  /*   sign.                        */
            ;
                deduce(X<0,T,H1), deduce(Y<0,T,H2)
            ),
            append(H1, H2, Hs)
        )
    ).

% Alternative forms of N*X<>Y - converted to this form to use above.

deduce(X*N<>Y,T,Hs) :- int(N), deduce(N*X<>Y,T,Hs).

deduce(Y<>N*X,T,Hs) :- int(N), Y\=_*_, deduce(N*X<>Y,T,Hs).

deduce(Y<>X*N,T,Hs) :- int(N), Y\=_*_, deduce(N*X<>Y,T,Hs).

% Greater than
%-------------

deduce(X*Y>Z,T,Hs) :-
    i_am_using_rule(gt_2),
    (
        infrule(Y*X>Z,Hs)
    ;
        int(X),                             /* If X is an integer -           */
        (
            simplify(X=0,true),              /* - and X=0, prove Z<0 or fail   */
            ( deduce(Z<0,T,Hs) ; !, fail )
        ;
            simplify(X=1,true),              /* - and X=1, prove Y>Z or fail   */
            ( deduce(Y>Z,T,Hs) ; !, fail )
        ;
            simplify(X=(-1),true),           /* - and X=-1, prove -Y>Z or fail */
            (
                evaluate(-Y,W), deduce(W>Z,T,Hs)
            ;
                !, fail
            )
        ;
            int(Y),                          /* - and Y is an integer, multiply*/
            (                                /*   & prove prod>Z or fail       */
                evaluate(X*Y,W), deduce(W>Z,T,Hs)
            ;
                !, fail
            )
        ;
            simplify(X>0,true),              /* - try substituting for Y,      */
            (                                /*   depending on sign of X.  For */
                infrule(Y>=W,H1), testused(Y>=W),/*   each sign of X, there are two*/
                deduce(X*W>Z,T,H2)            /*   cases: >= then > and > then  */
            ;                                /*   >= for X>0, or <= then > and */
                infrule(Y>W,H1), deduce(X*W>=Z,T,H2)  /*   < then >= for X<0.           */
            ),
            append(H1, H2, Hs)
        ;
            simplify(0>X,true),
            (
                infrule(Y<=W,H1), testused(W>=Y), deduce(X*W>Z,T,H2)
            ;
                infrule(Y<W,H1), deduce(X*W>=Z,T,H2)
            ),
            append(H1, H2, Hs)
        )
    ;
        (\+ int(X)), (\+ int(Y)),
        int(Z), simplify(0>=Z,true),        /* If only Z is an integer, and Z */
        nonvar(X), nonvar(Y),
        (                                   /* is less than or equal to zero, */
            deduce(X>0,T,H1), deduce(Y>0,T,H2) /* try proving X*Y product>0, by  */
        ;                                   /* showing X & Y have same sign.  */
            deduce(X<0,T,H1), deduce(Y<0,T,H2)
        ),
        append(H1, H2, Hs)
    ).


% Alternative forms of N*X>Y, converted to this form to use above.

deduce(X*N>Y,T,Hs) :- int(N), deduce(N*X>Y,T,Hs).
deduce(Y<N*X,T,Hs) :- int(N), Y\=_*_, deduce(N*X>Y,T,Hs).
deduce(Y<X*N,T,Hs) :- int(N), Y\=_*_, deduce(N*X>Y,T,Hs).

% Less-than
%----------

deduce(X*Y<Z,T,Hs) :-
    i_am_using_rule(lt_2),
    (
        infrule(Y*X<Z,Hs)
    ;
        int(X),                              /* If X is an integer -           */
        (
            simplify(X=0,true),               /* - and X=0, prove Z>0 or fail   */
            ( deduce(Z>0,T,Hs) ; !, fail )
        ;
            simplify(X=1,true),               /* - and X=1, prove Y<Z or fail   */
            ( deduce(Y<Z,T,Hs) ; !, fail )
        ;
            simplify(X=(-1),true),            /* - and X=-1, prove -Y<Z or fail */
            (
                evaluate(-Y,W), deduce(W<Z,T,Hs)
            ;
                !, fail
            )
        ;
            int(Y),                           /* - and Y is an integer, multiply*/
            (                                 /*   & prove prod<Z or fail       */
                evaluate(X*Y,W), deduce(W<Z,T,Hs)
            ;
                !, fail
            )
        ;
            simplify(X>0,true),               /* - try substituting for Y,      */
            (                                 /*   depending on sign of X.  For */
                infrule(Y<=W,H1), testused(W>=Y), /*   each sign of X, there are two*/
                deduce(X*W<Z,T,H2)             /*   cases: <= then < and < then  */
            ;                                 /*   <= for X>0, or >= then < and */
                infrule(Y<W,H1), deduce(X*W<=Z,T,H2) /*   > then <= for X<0.           */
            ),
            append(H1, H2, Hs)
        ;
            simplify(0>X,true),
            (
                infrule(Y>=W,H1), testused(Y>=W),
                deduce(X*W<Z,T,H2)
            ;
                infrule(Y>W,H1), deduce(X*W<=Z,T,H2)
            ),
            append(H1, H2, Hs)
        )
    ;
        (\+ int(X)), (\+ int(Y)),          /* If only Z is an integer, and Z */
        int(Z), simplify(Z>=0,true),         /* is greater than or equal to 0, */
        nonvar(X), nonvar(Y),
        (                                    /* try proving X*Y product<0, by  */
            deduce(X>0,T,H1), deduce(Y<0,T,H2)/* showing X & Y have diff signs. */
        ;
            deduce(X<0,T,H1), deduce(Y>0,T,H2)
        ),
        append(H1, H2, Hs)
    ).


% Alternative forms of N*X<Y, converted to this form to use above code.

deduce(X*N<Y,T,Hs) :- int(N), deduce(N*X<Y,T,Hs).
deduce(Y>N*X,T,Hs) :- int(N), Y\=_*_, deduce(N*X<Y,T,Hs).
deduce(Y>X*N,T,Hs) :- int(N), Y\=_*_, deduce(N*X<Y,T,Hs).

% Greater than or equals
%-----------------------

deduce(X*Y>=Z,T,Hs) :-
    i_am_using_rule(ge_3),
    (
        infrule(Y*X>=Z,Hs)
    ;
        int(X),                               /* If X is an integer -           */
        (
            simplify(X=0,true),                /* - and X=0, prove Z<=0 or fail  */
            ( deduce(Z<=0,T,Hs) ; !, fail )
        ;
            simplify(X=1,true),                /* - and X=1, prove Y>=Z or fail  */
            ( deduce(Y>=Z,T,Hs) ; !, fail )
        ;
            simplify(X=(-1),true),             /* - and X=-1, prove -Y>=Z or fail*/
            (
                evaluate(-Y,W), deduce(W>=Z,T,Hs)
            ;
                !, fail
            )
        ;
            int(Y),                            /* - and Y is an integer, multiply*/
            (                                  /*   and prove prod>=Z or fail    */
                evaluate(X*Y,W), deduce(W>=Z,T,Hs)
            ;
                !, fail
            )
        ;
            simplify(X>0,true),               /* - try finding substitution for */
            (                                 /*   Y depending on sign of X and */
                infrule(Y>=W,H1),              /*   prove resulting expression.  */
                testused(Y>=W),
                deduce(X*W>=Z,T,H2)
            ;
                T = integer,                   /* - find W such that Y>W, i.e.   */
                infrule(Y > W, H1),            /*   Y >= W-1 for integer T.  If  */
                simplify(W-1, Wminus1),        /*   X*(W-1)>=Z, then X*Y>=Z too. */
                deduce(X*Wminus1 >= Z, T, H2)
            ),
            append(H1, H2, Hs)
        ;
            simplify(0>X,true),                /*   prove resulting expression.  */
            infrule(Y<=W,H1), testused(W>=Y),
            deduce(X*W>=Z,T,H2),
            append(H1, H2, Hs)
        )
    ;
        (\+ int(X)), (\+ int(Y)),           /* If only Z is an integer, less  */
        int(Z), simplify(0>=Z,true),          /* than or equal to 0, prove X*Y  */
        nonvar(X), nonvar(Y),
        (                                     /* product greater than or equal  */
            deduce(X>=0,T,H1), deduce(Y>=0,T,H2) /* to 0.                          */
        ;
            deduce(X<=0,T,H1), deduce(Y<=0,T,H2)
        ),
        append(H1, H2, Hs)
    ).

% Alternative forms of N*X>=Y - converted to this form to use above code,
% avoiding looping.

deduce(X*N>=Y,T,Hs) :- int(N), deduce(N*X>=Y,T,Hs).
deduce(Y<=N*X,T,Hs) :- int(N), Y\=_*_, deduce(N*X>=Y,T,Hs).
deduce(Y<=X*N,T,Hs) :- int(N), Y\=_*_, deduce(N*X>=Y,T,Hs).

% Special case for squaring X - see TN [K601-017]
% Note this is OK for integers or reals.
deduce(X*X>=0,_T,[]).

% Less-than-or-equals
%--------------------

deduce(X*Y<=Z,T,Hs) :-
    i_am_using_rule(le_3),
    (
        infrule(Y*X<=Z,Hs)
    ;
        int(X),                              /* If X is an integer -           */
        (
            simplify(X=0,true),               /* - and X=0, prove Z>=0 or fail  */
            ( deduce(Z>=0,T,Hs) ; !, fail )
        ;
            simplify(X=1,true),               /* - and X=1, prove Y<=Z or fail  */
            ( deduce(Y<=Z,T,Hs) ; !, fail )
        ;
            simplify(X=(-1),true),            /* - and X=-1, prove -Y<=Z or fail*/
            (
                evaluate(-Y,W),
                deduce(W<=Z,T,Hs)
            ;
                !, fail
            )
        ;
            int(Y),                           /* - and Y is an integer, multiply*/
            (                                 /*   and prove prod<=Z or fail    */
                evaluate(X*Y,W),
                deduce(W<=Z,T,Hs)
            ;
                !, fail
            )
        ;
            simplify(X>0,true),               /* - try finding substitution for */
            (                                 /*   Y depending on sign of X and */
                infrule(Y<=W,H1),              /*   prove resulting expression.  */
                testused(W>=Y),
                deduce(X*W<=Z,T,H2)
            ;
                T = integer,                   /* - find W such that Y<W, i.e.   */
                infrule(Y < W, H1),            /*   Y <= W-1 for integer T.  If  */
                simplify(W-1, Wminus1),        /*   X*(W-1)<=Z, then X*Y<=Z too. */
                deduce(X*Wminus1 <= Z, T, H2)
            ),
            append(H1, H2, Hs)
        ;
            simplify(0>X,true),
            infrule(Y>=W,H1),
            testused(Y>=W),
            deduce(X*W<=Z,T,H2),
            append(H1, H2, Hs)
        )
    ;
        (\+ int(X)), (\+ int(Y)),          /* If only Z is an integer greater*/
        int(Z), simplify(Z>=0,true),         /* than or equal to 0, prove X*Y  */
        nonvar(X), nonvar(Y),
        (                                    /* product less than or equal to  */
            deduce(X>=0,T,H1),                /* 0.                             */
            deduce(Y<=0,T,H2)
        ;
            deduce(X<=0,T,H1),
            deduce(Y>=0,T,H2)
        ),
        append(H1, H2, Hs)
    ).

% Alternative forms of N*X<=Y - converted to this form to use above code,
% avoiding looping.

deduce(X*N<=Y,T,Hs) :- int(N), deduce(N*X<=Y,T,Hs).
deduce(Y>=N*X,T,Hs) :- int(N), Y\=_*_, deduce(N*X<=Y,T,Hs).
deduce(Y>=X*N,T,Hs) :- int(N), Y\=_*_, deduce(N*X<=Y,T,Hs).

% General rules
%==================================================

% Equality
%---------

deduce(X=Y,T,Hs) :-
    i_am_using_rule(eq_gen),
    (
        X=Y, Hs=[]                           /* Proved if same expression.     */
    ;
        int(X),                              /* If both ints, equal or fail.   */
        int(Y),
        (
            simplify(X=Y,true), Hs=[]
        ;
            !, fail
        )
    ;
        infrule(X=Z,H1),                     /* Try transitive chain.          */
        testused(X=Z),
        deduce(Z=Y,T,H2),
        append(H1, H2, Hs)
    ).

% Inequality
%-----------


% Add this special case to deal with:
% X > I -> X <> I
% Where I is an integer literal. Covers the common case of:
% X > 0 -> X <> 0
% Which crops up in SPARK.
deduce(X<>I,T,Hs) :-
    i_am_using_rule(ineq_gen_special),
    checktype(X, integer),
    int(I),
    deduce(X > I, T, Hs).


% Add this special case to deal with:
% X < I -> X <> I
% Where I is an integer literal.  Covers the common case of:
% X < 0 -> X <> 0
% Which crops up in SPARK.
deduce(X<>I,T,Hs) :-
    i_am_using_rule(ineq_gen_special2),
    checktype(X, integer),
    int(I),
    deduce(X < I, T, Hs).


% Add this special case to deal with:
% abs(X) > 0 -> X <> 0
% This is OK for integers or reals.
deduce(X<>0,T,Hs) :-
    i_am_using_rule(ineq_gen_special3),
    % use safe_deduce/3 here to guard against unbounded recursion
    safe_deduce(abs(X) > 0, T, Hs).


% General case.
deduce(X<>Y,T,Hs) :-
    i_am_using_rule(ineq_gen),
    (
        int(X),                              /* If X is an integer -           */
        (
            int(Y),                           /* - and Y is an integer, prove   */
            (                                 /*   not equal or fail            */
                (\+ simplify(X=Y,true)), Hs=[]
            ;
                !, fail
            )
        ;
            deduce(Y<>X,T,Hs)                 /* - swap sides and prove         */
        )
    ;
        (                                    /* Equivalent forms of the goal   */
            infrule(Z+X=Y,H1)                 /* giving subgoals                */
        ;
            infrule(X+Z=Y,H1)
        ;
            infrule(X-Z=Y,H1)
        ),
        (
            int(Z),
            (\+ simplify(Z=0,true)), H2=[]
        ;
            infrule(Z<>0,H2)
        ),
        append(H1, H2, Hs)
    ;
        infrule(X<>Z,H1),
        deduce(Z=Y,T,H2),
        append(H1, H2, Hs)
    ;
        infrule(X=Z,H1),
        testused(X=Z),
        deduce(Z<>Y,T,H2),
        append(H1, H2, Hs)
    ).

% Greater than
%-------------

deduce(X>Y,T,Hs) :-
    i_am_using_rule(gt_gen),
    (
        int(X),                              /* If X is an integer -           */
        (
            int(Y),                           /* - and Y is an integer, prove   */
            ( simplify(X>Y,true) ; !, fail ), /*   X greater than Y or fail     */
            Hs=[]
        ;
            deduce(Y<X,T,Hs)                  /* - swap sides then prove.       */
        )
    ;
        (
            infrule(Z+X>=Y,H1)                /* Equivalent forms of the        */
        ;                                    /* expression using different     */
            infrule(X+Z>=Y,H1)                /* operator and integer addition  */
        ),                                   /* or subtraction.                */
        (
            int(Z), simplify(0>Z,true), H2=[]
        ;
            infrule(Z<0,H2)
        ),
        append(H1, H2, Hs)
    ;
        infrule(X-Z>=Y,H1),
        (
            int(Z), simplify(Z>0,true), H2=[]
        ;
            infrule(Z>0,H2)
        ),
        append(H1, H2, Hs)
    ;
        infrule(X>=Z,H1), testused(X>=Z),    /* Two possible intermediate      */
        deduce(Z>Y,T,H2),                    /* steps of deduction.            */
        append(H1, H2, Hs)
    ;
        infrule(X>Z,H1), testused(X>Z),
        deduce(Z>=Y,T,H2),
        append(H1, H2, Hs)
    ).

% Less-than
%----------

deduce(X<Y,T,Hs) :-
    i_am_using_rule(lt_gen),
    (
        int(X),                              /* If X is an integer -           */
        (
            int(Y),                           /* - and Y is an integer, prove   */
            ( simplify(Y>X,true) ; !, fail ), /*   X is less than Y or fail     */
            Hs=[]
        ;
            deduce(Y>X,T,Hs)                  /* - swap sides then prove.       */
        )
    ;
        (
            infrule(Z+X<=Y,H1)                /* Equivalent forms of the        */
        ;                                    /* expression using different     */
            infrule(X+Z<=Y,H1)                /* operator and integer addition  */
        ),                                   /* or subtraction.                */
        (
            int(Z), simplify(Z>0,true), H2=[]
        ;
            infrule(Z>0,H2)
        ),
        append(H1, H2, Hs)
    ;
        infrule(X-Z<=Y,H1),
        (
            int(Z), simplify(0>Z,true), H2=[]
        ;
            infrule(Z<0,H2)
        ),
        append(H1, H2, Hs)
    ;
        infrule(X<=Z,H1), testused(Z>=X),    /* Two possible intermediate      */
        deduce(Z<Y,T,H2),                    /* steps of deduction.            */
        append(H1, H2, Hs)
    ;
        infrule(X<Z,H1), testused(Z>X),
        deduce(Z<=Y,T,H2),
        append(H1, H2, Hs)
    ).

% Greater than or equals
%-----------------------

deduce(X>=Y,T,Hs) :-
    i_am_using_rule(ge_gen),
    (
        int(X),                              /* If X is an integer -           */
        (
            int(Y),                           /* - and Y is an integer, prove   */
            (                                 /*   X>=Y or fail                 */
                simplify(X>=Y,true), Hs=[]
            ;
                !, fail
            )
        ;
            deduce(Y<=X,T,Hs)                 /* - swap sides then prove.       */
        )
    ;
        nonvar(X), nonvar(Y), X=Y, Hs=[]     /* Proved if X & Y are the same.  */
    ;
        (
            infrule(Z+X>=Y,H1)                /* Equivalent forms of the        */
        ;
            infrule(X+Z>=Y,H1)
        ),
        (                                    /* expression using different     */
            int(Z),                           /* operator and integer addition  */
            simplify(0>=Z,true), H2=[]        /* or subtraction.                */
        ;
            infrule(Z<=0,H2)
        ),
        append(H1, H2, Hs)
    ;
        infrule(X-Z>=Y,H1),
        (
            int(Z),
            simplify(Z>=0,true), H2=[]
        ;
            infrule(Z>=0,H2)
        ),
        append(H1, H2, Hs)
    ;
        infrule(X>=Z,H1),                    /* Try an intermediate step.      */
        testused(X>=Z),
        deduce(Z>=Y,T,H2),
        append(H1, H2, Hs)
    ).

% Less-than-or-equals
%--------------------

deduce(X<=Y,T,Hs) :-
    i_am_using_rule(le_gen),
    (
        int(X),                              /* If X is an integer -           */
        (
            int(Y),                           /* - and Y is an integer, prove   */
            (                                 /*   Y>=X or fail                 */
                simplify(Y>=X,true), Hs=[]
            ;
                !, fail
            )
        ;
            deduce(Y>=X,T,Hs)                 /* - swap sides then prove.       */
        )
    ;
        nonvar(X),
        nonvar(Y),                           /* Proved if X & Y are the same.  */
        X=Y, Hs=[]
    ;
        (                                    /* Equivalent forms of the        */
            infrule(Z+X<=Y,H1)                /* expression using different     */
        ;                                    /* operator and integer addition  */
            infrule(X+Z<=Y,H1)                /* or subtraction.                */
        ),
        (
            int(Z),
            simplify(Z>=0,true), H2=[]
        ;
            infrule(Z>=0,H2)
        ),
        append(H1, H2, Hs)
    ;
        infrule(X-Z<=Y,H1),
        (
            int(Z),
            simplify(0>=Z,true), H2=[]
        ;
            infrule(Z<=0,H2)
        ),
        append(H1, H2, Hs)
    ;
        infrule(X<=Z,H1),                    /* Try an intermediate step.      */
        testused(Z>=X),
        deduce(Z<=Y,T,H2),
        append(H1, H2, Hs)
    ).

% Bitwise logical operator rules
%==================================================

deduce(X, T, Hs) :- bitwise_deduce(X, T, Hs).  /* try normal way round */

% Other bitwise bound permutations (but without introducing
% non-termination).

deduce(bit__and(X,Y) >= Z, T, Hs) :-
    Z \= bit__and(_,_),
    Z \= bit__or(_,_),
    Z \= bit__xor(_,_),
    !,
    bitwise_deduce(Z <= bit__and(X,Y), T, Hs).
deduce(Z >= bit__and(X,Y), T, Hs) :-
    Z \= bit__and(_,_),
    Z \= bit__or(_,_),
    Z \= bit__xor(_,_),
    !,
    bitwise_deduce(bit__and(X,Y) <= Z, T, Hs).

deduce(bit__or(X,Y) >= Z, T, Hs) :-
    Z \= bit__and(_,_),
    Z \= bit__or(_,_),
    Z \= bit__xor(_,_),
    !,
    bitwise_deduce(Z <= bit__or(X,Y), T, Hs).
deduce(Z >= bit__or(X,Y), T, Hs) :-
    Z \= bit__and(_,_),
    Z \= bit__or(_,_),
    Z \= bit__xor(_,_),
    !,
    bitwise_deduce(bit__or(X,Y) <= Z, T, Hs).

deduce(bit__xor(X,Y) >= Z, T, Hs) :-
    Z \= bit__and(_,_),
    Z \= bit__or(_,_),
    Z \= bit__xor(_,_),
    !,
    bitwise_deduce(Z <= bit__xor(X,Y), T, Hs).
deduce(Z >= bit__xor(X,Y), T, Hs) :-
    Z \= bit__and(_,_),
    Z \= bit__or(_,_),
    Z \= bit__xor(_,_),
    !,
    bitwise_deduce(bit__xor(X,Y) <= Z, T, Hs).

% Modulus bounds rules
%==================================================

% Rules for SPARK modulus (+, - include 0 for X, exclude for Y):
%
% --------------------------------------------------------------------------
% X|Y| X MOD Y        | Example
% -|-|----------------|-----------------------------------------------------
% +|+| >= 0,   <= Y-1 | 0 <= X mod  5 <= 4, for X>=0  e.g.   7 mod  5 =  2
% -|+| >= 0,   <= Y-1 | 0 <= X mod  3 <= 2, for X<=0  e.g.  -8 mod  3 =  1
% +|-| >= Y+1, <= 0   | -5 <= X mod -6 <= 0, for X>=0  e.g.  13 mod -6 = -5
% -|-| >= Y+1, <= 0   | -3 <= X mod -4 <= 0, for X<=0, e.g.  -7 mod -4 = -3
% --------------------------------------------------------------------------

% Basic rules
%------------

deduce(N <= _X mod Y, T, Hs) :-             /* X+ Y+ lwb */
    get_provenance_framework(spark),                 /* X- Y+ lwb */
    (
        int(N),
        !,
        simplify(N <= 0, true),
        H1 = []
    ;
        infrule(N <= 0, H1)
    ),
    (
        deduce(Y >= 1, T, H2)   /* must be integer */
    ;
        deduce(Y > 0, T, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

deduce(_ mod Y >= N, T, Hs) :-              /* X+ Y+ lwb */
    get_provenance_framework(spark),                 /* X- Y+ lwb */
    (
        int(N),
        !,
        simplify(N <= 0, true),
        H1 = []
    ;
        infrule(N <= 0, H1)
    ),
    (
        deduce(Y >= 1, T, H2)   /* must be integer */
    ;
        deduce(Y > 0, T, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

deduce(Z <= _X mod Y, T, Hs) :-             /* X- Y- lwb */
    get_provenance_framework(spark),                 /* X+ Y- lwb */
    (
        deduce(Y <= - 1, T, H1)      /* must be integer */
    ;
        deduce(Y < 0, T, H1)
    ),
    (
        deduce(Z <= Y + 1, T, H2)
    ;
        deduce(Z < Y + 2, T, H2)
    ),
    !,
    append(H1, H2, Hs).

deduce(_ mod Y >= Z, T, Hs) :-              /* X- Y- lwb */
    get_provenance_framework(spark),                 /* X+ Y- lwb */
    (
        deduce(Y <= - 1, T, H1)      /* must be integer */
    ;
        deduce(Y < 0, T, H1)
    ),
    (
        deduce(Z <= Y + 1, T, H2)
    ;
        deduce(Z < Y + 2, T, H2)
    ),
    !,
    append(H1, H2, Hs).

deduce(_ mod Y <= 0, T, Hs) :-              /* X- Y- upb */
    get_provenance_framework(spark),                 /* X+ Y- upb */
    (
        deduce(Y <= - 1, T, Hs)      /* must be integer */
    ;
        deduce(Y < 0, T, Hs)
    ),
    !.

deduce(0 >= _X mod Y, T, Hs) :-             /* X- Y- upb */
    get_provenance_framework(spark),                 /* X+ Y- upb */
    (
        deduce(Y <= - 1, T, Hs)      /* must be integer */
    ;
        deduce(Y < 0, T, Hs)
    ),
    !.

deduce(_ mod Y <= Z, T, Hs) :-              /* X+ Y+ upb */
    get_provenance_framework(spark),                 /* X- Y+ upb */
    (
        deduce(Y >= 1, T, H1)   /* must be integer */
    ;
        deduce(Y > 0, T, H1)
    ),
    (
        deduce(Y <= Z + 1, T, H2)
    ;
        deduce(Y - 1 <= Z, T, H2)
    ;
        deduce(Y < Z + 2, T, H2)
    ;
        deduce(Y - 2 < Z, T, H2)
    ),
    !,
    append(H1, H2, Hs).

deduce(Z >= _X mod Y, T, Hs) :-             /* X+ Y+ upb */
    get_provenance_framework(spark),                 /* X- Y+ upb */
    (
        deduce(Y >= 1, T, H1)   /* must be integer */
    ;
        deduce(Y > 0, T, H1)
    ),
    (
        deduce(Y <= Z + 1, T, H2)
    ;
        deduce(Y - 1 <= Z, T, H2)
    ;
        deduce(Y < Z + 2, T, H2)
    ;
        deduce(Y - 2 < Z, T, H2)
    ),
    !,
    append(H1, H2, Hs).

% Extra rules
%------------

deduce(X mod Y < Z, T, Hs) :-
    get_provenance_framework(spark),
    (
        int(Z),
        Z1 iss Z - 1
    ;
        \+ int(Z),
        Z1 = Z - 1
    ),
    deduce(X mod Y <= Z1, T, Hs).

deduce(Z > X mod Y, T, Hs) :-
    get_provenance_framework(spark),
    (
        int(Z),
        Z1 iss Z - 1
    ;
        \+ int(Z),
        Z1 = Z - 1
    ),
    deduce(X mod Y <= Z1, T, Hs).

deduce(Z < X mod Y, T, Hs) :-
    get_provenance_framework(spark),
    (
        int(Z),
        Z1 iss Z + 1
    ;
        \+ int(Z),
        Z1 = Z + 1
    ),
    deduce(Z1 <= X mod Y, T, Hs).

deduce(X mod Y > Z, T, Hs) :-
    get_provenance_framework(spark),
    (
        int(Z),
        Z1 iss Z + 1
    ;
        \+ int(Z),
        Z1 = Z + 1
    ),
    deduce(Z1 <= X mod Y, T, Hs).

%===============================================================================
% bitwise_deduce(+R,+T,-Hs).
%-------------------------------------------------------------------------------
% Deduce expression R, related to bitwise operations, whose lhs and rhs are
% of type T, giving Hs instantiated to list of hypotheses used.
%===============================================================================

% bit__and: lower bound
%----------------------

bitwise_deduce(Z <= bit__and(X, X), T, Hs) :-   /* bit__and(X, X) = X: proceed from there */
    !,
    deduce(Z <= X, T, Hs).
bitwise_deduce(0 <= bit__and(X, Y), T, Hs) :-   /* 0 <= bit__and(X, Y) if 0<=X    and 0<=Y    */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    !,
    append(H1, H2, Hs).
bitwise_deduce(Z <= bit__and(X, Y), T, Hs) :-   /* Z <= bit__and(X, Y) if 0<=X and 0<=Y and Z<=0 */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    deduce_once(Z <= 0, T, H3),
    !,
    append(H2,  H3, H23),
    append(H1, H23, Hs),
    !.

% bit__and: upper bound
%----------------------

bitwise_deduce(bit__and(X, X) <= Z, T, Hs) :-   /* bit__and(X, X) = X: proceed from there */
    !,
    deduce(X <= Z, T, Hs).
bitwise_deduce(bit__and(X, Y) <= X, T, Hs) :-   /* bit__and(X, Y) <= X if 0<=X    and 0<=Y    */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    !,
    append(H1, H2, Hs).
bitwise_deduce(bit__and(X, Y) <= Y, T, Hs) :-   /* bit__and(X, Y) <= Y if 0<=X    and 0<=Y    */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    !,
    append(H1, H2, Hs).
bitwise_deduce(bit__and(X, Y) <= Z, T, Hs) :-   /* bit__and(X, Y) <= Z if 0<=X<=Z or 0<=Y<=Z */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    (
        ( Z = X+Y ; Z = Y+X ),          /* bit__and(X, Y) <= X+Y, because both X<=X+Y and Y<=X+Y */
        H3 = []
    ;
        deduce_once(X <= Z, T, H3)
    ;
        deduce_once(Y <= Z, T, H3)
    ;
        deduce(X + Y <= Z, T, H3)
    ),
    !,
    append(H2,  H3, H23),
    append(H1, H23, Hs),
    !.

% bit__or: lower bound
%---------------------

bitwise_deduce(Z <= bit__or(X, X), T, Hs) :-    /* bit__or(X, X) = X: proceed from there */
    !,
    deduce(Z <= X, T, Hs).
bitwise_deduce(X <= bit__or(X, Y), T, Hs) :-    /* X <= bit__or(X, Y) if 0<=X    and 0<=Y    */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    !,
    append(H1, H2, Hs).
bitwise_deduce(Y <= bit__or(X, Y), T, Hs) :-    /* Y <= bit__or(X, Y) if 0<=X    and 0<=Y    */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    !,
    append(H1, H2, Hs).
bitwise_deduce(Z <= bit__or(X, Y), T, Hs) :-    /* Z <= bit__or(X, Y) if Z<=X and Z<=Y and 0<=X and 0<=Y */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    (
        deduce_once(Z <= 0, T, H3),
        H4 = []
    ;
        deduce_once(Z <= X, T, H3),
        deduce_once(Z <= Y, T, H4)
    ),
    !,
    append(H3,   H4, H34),
    append(H2,  H34, H234),
    append(H1, H234, Hs),
    !.

% bit__or: upper bound
%---------------------

bitwise_deduce(bit__or(X, X) <= Z, T, Hs) :-    /* bit__or(X, X) = X: proceed from there */
    !,
    deduce(X <= Z, T, Hs).
bitwise_deduce(bit__or(X, Y) <= X+Y, T, Hs) :-  /* bit__or(X, Y) <= X+Y if 0<=X and 0<=Y */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    !,
    append(H1, H2, Hs).
bitwise_deduce(bit__or(X, Y) <= Z, T, Hs) :-    /* bit__or(X, Y) <= Z if 0<=X and 0<=Y and one of... */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    (
        int(Z),
        Z >= 0,
        next_two_to_N_minus_1_above(Z, Z),   /* ... Z = 2**N-1 for some N, and */
        deduce(X <= Z, T, H3),          /* ... both X and Y are <= Z */
        deduce(Y <= Z, T, H4),
        H5 = []
    ;
        deduce(X+Y <= Z, T, H3),        /* ... X+Y <= Z, or ... */
        H4 = [], H5 = []
    ;
        infrule_int_rhs(X <= Xint, H3),
        next_two_to_N_minus_1_above(Xint, XUpper2toNm1),     /* ... X <= 2**M-1 and ... */
        infrule_int_rhs(Y <= Yint, H4),
        next_two_to_N_minus_1_above(Yint, YUpper2toNm1),     /* ... Y <= 2**N-1 and ... */
        (
            XUpper2toNm1 >= YUpper2toNm1,
            ZL = XUpper2toNm1
        ;
            XUpper2toNm1 < YUpper2toNm1,
            ZL = YUpper2toNm1
        ),
        deduce(ZL <= Z, T, H5)                /* ... max(2**M-1, 2**N-1) <= Z */
    ),
    !,
    append(H4,    H5, H45),
    append(H3,   H45, H345),
    append(H2,  H345, H2345),
    append(H1, H2345, Hs),
    !.

% bit__xor: lower bound
%----------------------

bitwise_deduce(Z <= bit__xor(X, X), T, Hs) :-   /* bit__xor(X, X) = 0: proceed from there */
    !,
    deduce(Z <= 0, T, Hs).
bitwise_deduce(0 <= bit__xor(X, Y), T, Hs) :-   /* 0 <= bit__xor(X, Y) if 0<=X    and 0<=Y    */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    !,
    append(H1, H2, Hs).
bitwise_deduce(Z <= bit__xor(X, Y), T, Hs) :-   /* Z <= bit__xor(X, Y) if 0<=X and 0<=Y and Z<=0 */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    (
        deduce_once(Z <= 0, T, H3)
    ;
        deduce(Z <= X - Y, T, H3)       /* abs(X-Y) <= bit__xor(X, Y), since bit__and(X, Y) >= X and >= Y */
    ;                   /*              and bit__xor = X+Y - 2 * bit__and  */
        deduce(Z <= Y - X, T, H3)
    ),
    !,
    append(H2,  H3, H23),
    append(H1, H23, Hs),
    !.

% bit__xor: upper bound
%----------------------

bitwise_deduce(bit__xor(X, X) <= Z, T, Hs) :-   /* bit__xor(X, X) = 0: proceed from there */
    !,
    deduce(0 <= Z, T, Hs).
bitwise_deduce(bit__xor(X, Y) <= X+Y, T, Hs) :- /* bit__xor(X, Y) <= X+Y if 0<=X and 0<=Y */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    !,
    append(H1, H2, Hs).
bitwise_deduce(bit__xor(X, Y) <= Z, T, Hs) :-   /* bit__xor(X, Y) <= Z if 0<=X and 0<=Y and one of... */
    deduce_once(X >= 0, T, H1),
    deduce_once(Y >= 0, T, H2),
    (
        int(Z),
        Z >= 0,
        next_two_to_N_minus_1_above(Z, Z),   /* ... Z = 2**N-1 for some N and     */
        safe_deduce(X <= Z, T, H3),     /* ... both X and Y are <= Z, or ... */
        safe_deduce(Y <= Z, T, H4),
        H5 = []
    ;
        safe_deduce(X+Y <= Z, T, H3),   /* ... X+Y <= Z, or ... */
        H4 = [], H5 = []
    ;
        infrule_int_rhs(X <= Xint, H3),
        next_two_to_N_minus_1_above(Xint, XUpper2toNm1),     /* ... X <= 2**M-1 and ... */
        infrule_int_rhs(Y <= Yint, H4),
        next_two_to_N_minus_1_above(Yint, YUpper2toNm1),     /* ... Y <= 2**N-1 and ... */
        (
            XUpper2toNm1 >= YUpper2toNm1,
            ZL = XUpper2toNm1
        ;
            XUpper2toNm1 < YUpper2toNm1,
            ZL = YUpper2toNm1
        ),
        deduce(ZL <= Z, T, H5)                /* ... max(2**M-1, 2**N-1) <= Z */
    ),
    !,
    append(H4,    H5, H45),
    append(H3,   H45, H345),
    append(H2,  H345, H2345),
    append(H1, H2345, Hs),
    !.

%===============================================================================
% deduce_once(+GOAL, +Type, -HypList).
%-------------------------------------------------------------------------------
% Succeed once, or fail.
%===============================================================================

deduce_once(X >= N, T, H) :-
    (
        int(X),
        simplify(X >= N, true),
        H = []
    ;
        deduce(X >= N, T, H)
    ;
        infrule(X >= XL, Ha),
        testused(X >= XL),
        deduce(XL >= N, T, Hb),
        append(Ha, Hb, H)
    ),
    !.
deduce_once(Z <= N, T, H) :-
    (
        int(Z),
        simplify(Z <= N, true),
        H = []
    ;
        deduce(Z <= N, T, H)
    ;
        infrule(Z <= ZL, Ha),
        testused(ZL >= Z),
        deduce(ZL <= N, T, Hb),
        append(Ha, Hb, H)
    ),
    !.

%===============================================================================
% infrule_int_rhs(+GOAL, -HypList).
%-------------------------------------------------------------------------------
% Solve with integer r.h.s.
%===============================================================================

infrule_int_rhs(X <= Xint, H) :-
        (
            infrule(X <= Xint, H)
        ;
            infrule(X <= XU, Ha),
            testused(XU >= X),
            infrule(XU <= Xint, Hb),
            append(Ha, Hb, H)
        ),
        int(Xint).

%===============================================================================
% try_new_deduction_strategies(+GOAL, +Type, -HypList).
%-------------------------------------------------------------------------------
% Deduce expression GOAL, of type T, giving Hs instantiated to list of
% hypotheses used. These new deduction strategies cater for for abs, div,
% mod and exponentiation (to the power of) rules.
%===============================================================================

% Inference rules
%----------------

try_new_deduction_strategies(Goal, _T, Hs) :-
    inference_rule(_Name, Goal, Conditions),
    ground(Conditions),
    safe_infer_side_conditions(Conditions, Hs),
    !.

try_new_deduction_strategies(X <= Atomic, _T, Hs) :-
    int_enum_lit_or_const(Atomic, Which),
    (
        inference_rule(_Name, X <= OtherAtomic, Conditions)
    ;
        inference_rule(_Name, OtherAtomic >= X, Conditions)
    ),
    int_enum_lit_or_const(OtherAtomic, Which),
    simplify(OtherAtomic <= Atomic, true),
    ground(Conditions),
    safe_infer_side_conditions(Conditions, Hs),
    !.

try_new_deduction_strategies(Atomic >= X, _T, Hs) :-
    int_enum_lit_or_const(Atomic, Which),
    (
        inference_rule(_Name, X <= OtherAtomic, Conditions)
    ;
        inference_rule(_Name, OtherAtomic >= X, Conditions)
    ),
    int_enum_lit_or_const(OtherAtomic, Which),
    simplify(OtherAtomic <= Atomic, true),
    ground(Conditions),
    safe_infer_side_conditions(Conditions, Hs),
    !.

try_new_deduction_strategies(X >= Atomic, _T, Hs) :-
    int_enum_lit_or_const(Atomic, Which),
    (
        inference_rule(_Name, X >= OtherAtomic, Conditions)
    ;
        inference_rule(_Name, OtherAtomic <= X, Conditions)
    ),
    int_enum_lit_or_const(OtherAtomic, Which),
    simplify(OtherAtomic >= Atomic, true),
    ground(Conditions),
    safe_infer_side_conditions(Conditions, Hs),
    !.

try_new_deduction_strategies(Atomic <= X, _T, Hs) :-
    int_enum_lit_or_const(Atomic, Which),
    (
        inference_rule(_Name, X >= OtherAtomic, Conditions)
    ;
        inference_rule(_Name, OtherAtomic <= X, Conditions)
    ),
    int_enum_lit_or_const(OtherAtomic, Which),
    simplify(OtherAtomic >= Atomic, true),
    ground(Conditions),
    safe_infer_side_conditions(Conditions, Hs),
    !.

% Additional rules for inequalities using transitivity.

try_new_deduction_strategies(X <= Atomic, _T, Hs) :-
    int_or_enum_lit(Atomic, Which),
    (
        inference_rule(_Name, Ident <= OtherAtomic, [])
    ;
        inference_rule(_Name, OtherAtomic >= Ident, [])
    ),
    int_or_enum_lit(OtherAtomic, Which),
    simplify(OtherAtomic <= Atomic, true),
    safe_infer_side_conditions([X <= Ident], Hs),
    !.

try_new_deduction_strategies(Atomic >= X, _T, Hs) :-
    int_or_enum_lit(Atomic, Which),
    (
        inference_rule(_Name, Ident <= OtherAtomic, [])
    ;
        inference_rule(_Name, OtherAtomic >= Ident, [])
    ),
    int_or_enum_lit(OtherAtomic, Which),
    simplify(OtherAtomic <= Atomic, true),
    safe_infer_side_conditions([X <= Ident], Hs),
    !.

try_new_deduction_strategies(X >= Atomic, _T, Hs) :-
    int_or_enum_lit(Atomic, Which),
    (
        inference_rule(_Name, Ident >= OtherAtomic, [])
    ;
        inference_rule(_Name, OtherAtomic <= Ident, [])
    ),
    int_or_enum_lit(OtherAtomic, Which),
    simplify(OtherAtomic >= Atomic, true),
    safe_infer_side_conditions([X >= Ident], Hs),
    !.

try_new_deduction_strategies(Atomic <= X, _T, Hs) :-
    int_or_enum_lit(Atomic, Which),
    (
        inference_rule(_Name, Ident >= OtherAtomic, [])
    ;
        inference_rule(_Name, OtherAtomic <= Ident, [])
    ),
    int_or_enum_lit(OtherAtomic, Which),
    simplify(OtherAtomic >= Atomic, true),
    safe_infer_side_conditions([X >= Ident], Hs),
    !.

try_new_deduction_strategies(X <= Atomic, T, Hs) :-
    T \= real,
    int_or_enum_lit(Atomic, Which),
    (
        Which = integer,
        simplify(Atomic + 1, Next)
    ;
        Which = enum,
        simplify(succ(Atomic), Next)
    ),
    int_or_enum_lit(Next, Which),
    (
        inference_rule(_Name, Ident <= Next, [])
    ;
        inference_rule(_Name, Next >= Ident, [])
    ),
    safe_infer_side_conditions([X < Ident], Hs),
    !.

try_new_deduction_strategies(Atomic >= X, T, Hs) :-
    T \= real,
    int_or_enum_lit(Atomic, Which),
    (
        Which = integer,
        simplify(Atomic + 1, Next)
    ;
        Which = enum,
        simplify(succ(Atomic), Next)
    ),
    int_or_enum_lit(Next, Which),
    (
        inference_rule(_Name, Ident <= Next, [])
    ;
        inference_rule(_Name, Next >= Ident, [])
    ),
    safe_infer_side_conditions([X < Ident], Hs),
    !.

try_new_deduction_strategies(X >= Atomic, T, Hs) :-
    T \= real,
    int_or_enum_lit(Atomic, Which),
    (
        Which = integer,
        simplify(Atomic - 1, Prev)
    ;
        Which = enum,
        simplify(pred(Atomic), Prev)
    ),
    int_or_enum_lit(Prev, Which),
    (
        inference_rule(_Name, Ident >= Prev, [])
    ;
        inference_rule(_Name, Prev <= Ident, [])
    ),
    safe_infer_side_conditions([X > Ident], Hs),
    !.

try_new_deduction_strategies(Atomic <= X, T, Hs) :-
    T \= real,
    int_or_enum_lit(Atomic, Which),
    (
        Which = integer,
        simplify(Atomic - 1, Prev)
    ;
        Which = enum,
        simplify(pred(Atomic), Prev)
    ),
    int_or_enum_lit(Prev, Which),
    (
        inference_rule(_Name, Ident >= Prev, [])
    ;
        inference_rule(_Name, Prev <= Ident, [])
    ),
    safe_infer_side_conditions([X > Ident], Hs),
    !.

% Rules for common enumeration-type bounds reasoning in loops.

try_new_deduction_strategies(LWB <= succ(X), _T, Hs) :-
    int_or_enum_lit(LWB, enum),
    standard_infrule(LWB <= X, H1),
    standard_infrule(X < C, H2),
    atom(C),
    (
        inference_rule(_Name, C <= UPB, [])
    ;
        inference_rule(_Name, UPB >= C, [])
    ),
    int_or_enum_lit(UPB, enum),
    safe_infer_side_conditions([LWB <= X], H3),
    !,
    append(H1, H2, H12),
    merge_sort(H12, H3, Hs).

try_new_deduction_strategies(succ(X) >= LWB, _T, Hs) :-
    int_or_enum_lit(LWB, enum),
    standard_infrule(LWB <= X, H1),
    standard_infrule(X < C, H2),
    atom(C),
    (
        inference_rule(_Name, C <= UPB, [])
    ;
        inference_rule(_Name, UPB >= C, [])
    ),
    int_or_enum_lit(UPB, enum),
    safe_infer_side_conditions([LWB <= X], H3),
    !,
    append(H1, H2, H12),
    merge_sort(H12, H3, Hs).

try_new_deduction_strategies(C <= succ(X), _T, Hs) :-
    \+ int_or_enum_lit(C, _),
    var_const(C, _T, c),
    standard_infrule(C <= X, H1),
    standard_infrule(X < D, H2),
    atom(D),
    (
        inference_rule(_Name, D <= UPB, [])
    ;
        inference_rule(_Name, UPB >= D, [])
    ),
    int_or_enum_lit(UPB, enum),
    !,
    merge_sort(H1, H2, Hs).

try_new_deduction_strategies(succ(X) >= C, _T, Hs) :-
    \+ int_or_enum_lit(C, _),
    var_const(C, _T, c),
    standard_infrule(C <= X, H1),
    standard_infrule(X < D, H2),
    atom(D),
    (
        inference_rule(_Name, D <= UPB, [])
    ;
        inference_rule(_Name, UPB >= D, [])
    ),
    int_or_enum_lit(UPB, enum),
    !,
    merge_sort(H1, H2, Hs).

try_new_deduction_strategies(succ(X) <= UPB, _T, Hs) :-
    int_or_enum_lit(UPB, enum),
    standard_infrule(X < C, Hs),
    atom(C),
    (
        inference_rule(_Name, C <= E, [])
    ;
        inference_rule(_Name, E >= C, [])
    ),
    int_or_enum_lit(E, enum),
    simplify(E <= UPB, true),
    !.

try_new_deduction_strategies(UPB >= succ(X), _T, Hs) :-
    int_or_enum_lit(UPB, enum),
    standard_infrule(X < C, Hs),
    atom(C),
    (
        inference_rule(_Name, C <= E, [])
    ;
        inference_rule(_Name, E >= C, [])
    ),
    int_or_enum_lit(E, enum),
    simplify(E <= UPB, true),
    !.

try_new_deduction_strategies(succ(X) <= C, _T, Hs) :-
    \+ int_or_enum_lit(C, _),
    var_const(C, _T, c),
    standard_infrule(X < C, Hs),
    atom(C),
    (
        inference_rule(_Name, C <= E, [])
    ;
        inference_rule(_Name, E >= C, [])
    ),
    int_or_enum_lit(E, enum),
    !.

try_new_deduction_strategies(C >= succ(X), _T, Hs) :-
    \+ int_or_enum_lit(C, _),
    var_const(C, _T, c),
    standard_infrule(X < C, Hs),
    atom(C),
    (
        inference_rule(_Name, C <= E, [])
    ;
        inference_rule(_Name, E >= C, [])
    ),
    int_or_enum_lit(E, enum),
    !.

try_new_deduction_strategies(pred(X) <= UPB, _T, Hs) :-
    int_or_enum_lit(UPB, enum),
    standard_infrule(X <= UPB, H1),
    standard_infrule(X > C, H2),
    atom(C),
    (
        inference_rule(_Name, C >= LWB, [])
    ;
        inference_rule(_Name, LWB <= C, [])
    ),
    int_or_enum_lit(LWB, enum),
    safe_infer_side_conditions([UPB >= X], H3),
    !,
    append(H1, H2, H12),
    merge_sort(H12, H3, Hs).

try_new_deduction_strategies(UPB >= pred(X), _T, Hs) :-
    int_or_enum_lit(UPB, enum),
    standard_infrule(X <= UPB, H1),
    standard_infrule(X > C, H2),
    atom(C),
    (
        inference_rule(_Name, C >= LWB, [])
    ;
        inference_rule(_Name, LWB <= C, [])
    ),
    int_or_enum_lit(LWB, enum),
    safe_infer_side_conditions([UPB >= X], H3),
    !,
    append(H1, H2, H12),
    merge_sort(H12, H3, Hs).

try_new_deduction_strategies(pred(X) <= C, _T, Hs) :-
    \+ int_or_enum_lit(C, _),
    var_const(C, _T, c),
    standard_infrule(X <= C, H1),
    standard_infrule(X > D, H2),
    atom(D),
    (
        inference_rule(_Name, D >= LWB, [])
    ;
        inference_rule(_Name, LWB <= D, [])
    ),
    int_or_enum_lit(LWB, enum),
    !,
    merge_sort(H1, H2, Hs).

try_new_deduction_strategies(C >= pred(X), _T, Hs) :-
    \+ int_or_enum_lit(C, _),
    var_const(C, _T, c),
    standard_infrule(X <= C, H1),
    standard_infrule(X > D, H2),
    atom(D),
    (
        inference_rule(_Name, D >= LWB, [])
    ;
        inference_rule(_Name, LWB <= D, [])
    ),
    int_or_enum_lit(LWB, enum),
    !,
    merge_sort(H1, H2, Hs).

try_new_deduction_strategies(pred(X) >= LWB, _T, Hs) :-
    int_or_enum_lit(LWB, enum),
    standard_infrule(X > C, Hs),
    atom(C),
    (
        inference_rule(_Name, C >= E, [])
    ;
        inference_rule(_Name, E <= C, [])
    ),
    int_or_enum_lit(E, enum),
    simplify(E >= LWB, true),
    !.

try_new_deduction_strategies(LWB <= pred(X), _T, Hs) :-
    int_or_enum_lit(LWB, enum),
    standard_infrule(X > C, Hs),
    atom(C),
    (
        inference_rule(_Name, C >= E, [])
    ;
        inference_rule(_Name, E <= C, [])
    ),
    int_or_enum_lit(E, enum),
    simplify(E >= LWB, true),
    !.

try_new_deduction_strategies(pred(X) >= C, _T, Hs) :-
    \+ int_or_enum_lit(C, _),
    var_const(C, _T, c),
    standard_infrule(X > C, Hs),
    atom(C),
    (
        inference_rule(_Name, C >= E, [])
    ;
        inference_rule(_Name, E <= C, [])
    ),
    int_or_enum_lit(E, enum),
    !.

try_new_deduction_strategies(C <= pred(X), _T, Hs) :-
    \+ int_or_enum_lit(C, _),
    var_const(C, _T, c),
    standard_infrule(X > C, Hs),
    atom(C),
    (
        inference_rule(_Name, C >= E, [])
    ;
        inference_rule(_Name, E <= C, [])
    ),
    int_or_enum_lit(E, enum),
    !.

% Absolute value rules.

% Abs(1): abs(X) >= 0 may_be_deduced.
try_new_deduction_strategies(abs(_) >= 0, _, []).

try_new_deduction_strategies(0 <= abs(_), _, []).

% Abs(2): abs(X) >= N may_be_deduced_from [N < 0].
try_new_deduction_strategies(abs(_) >= N, T, Hs) :-
    safe_deduce(N < 0, T, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N <= abs(_), T, Hs) :-
    safe_deduce(N < 0, T, HL),
    sort(HL, Hs).

% Abs(3): abs(X) <= N may_be_deduced_from [(1) 0 <= X, (2) X <= N.
try_new_deduction_strategies(abs(X) <= N, T, Hs) :-
    i_am_using_rule(abs_3a),
    safe_deduce(0 <= X, T, H1),           /* (1) */
    safe_deduce(X <= N, T, H2),           /* (2) */
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= abs(X), T, Hs) :-
    i_am_using_rule(abs_3b),
    safe_deduce(0 <= X, T, H1),           /* (1) */
    safe_deduce(X <= N, T, H2),           /* (2) */
    append(H1, H2, HL),
    sort(HL, Hs).

%Abs(4): abs(X) <= N may_be_deduced_from [(1) A <= X,
%                                         (2) X <= B,
%                                         (3) A >= 0,
%                                         (4) B <= N].
try_new_deduction_strategies(abs(X) <= N, T, Hs) :-
    i_am_using_rule(abs_4a),
    infrule(A <= X, H1),          /* (1): generates candidate A */
    infrule(X <= B, H2),          /* (2): generates candidate B */
    safe_deduce(A >= 0, T, H3),           /* (3) */
    safe_deduce(B <= N, T, H4),           /* (4) */
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= abs(X), T, Hs) :-
    i_am_using_rule(abs_4b),
    infrule(A <= X, H1),          /* (1): generates candidate A */
    infrule(X <= B, H2),          /* (2): generates candidate B */
    safe_deduce(A >= 0, T, H3),           /* (3) */
    safe_deduce(B <= N, T, H4),           /* (4) */
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Abs(5): abs(X) <= N may_be_deduced_from [(1) A <= X,
%                                          (2) X <= B,
%                                          (3) B <= 0,
%                                          (4) -A <= N].
try_new_deduction_strategies(abs(X) <= N, T, Hs) :-
    i_am_using_rule(abs_5a),
    infrule(A <= X, H1),          /* (1): generates candidate A */
    infrule(X <= B, H2),          /* (2): generates candidate B */
    safe_deduce(B <= 0, T, H3),           /* (3) */
    simplify(-A, MinusA),
    safe_deduce(MinusA <= N, T, H4),      /* (4) */
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= abs(X), T, Hs) :-
    i_am_using_rule(abs_5b),
    infrule(A <= X, H1),          /* (1): generates candidate A */
    infrule(X <= B, H2),          /* (2): generates candidate B */
    safe_deduce(B <= 0, T, H3),           /* (3) */
    simplify(-A, MinusA),
    safe_deduce(MinusA <= N, T, H4),      /* (4) */
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

/* Abs(6): abs(X) <= N may_be_deduced_from [(1) A <= X,
                                            (2) X <= B,
                                            (3) A <= 0,
                                            (4) 0 <= B,
                                            (5) N >= -A,
                                            (6) N >= B]. */
try_new_deduction_strategies(abs(X) <= N, T, Hs) :-
    i_am_using_rule(abs_6a),
    infrule(A <= X, H1),          /* (1): generates candidate A */
    infrule(X <= B, H2),          /* (2): generates candidate B */
    safe_deduce(A <= 0, T, H3),           /* (3) */
    simplify(-A, MinusA),
    safe_deduce(MinusA <= N, T, H5),      /* (5) */
    safe_deduce(0 <= B, T, H4),           /* (4) */
    safe_deduce(B <= N, T, H6),           /* (6) */
    append(H5, H6, H5to6),
    append(H4, H5to6, H4to6),
    append(H3, H4to6, H3to6),
    append(H2, H3to6, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= abs(X), T, Hs) :-
    i_am_using_rule(abs_6b),
    infrule(A <= X, H1),          /* (1): generates candidate A */
    infrule(X <= B, H2),          /* (2): generates candidate B */
    safe_deduce(A <= 0, T, H3),           /* (3) */
    simplify(-A, MinusA),
    safe_deduce(MinusA <= N, T, H5),      /* (5) */
    safe_deduce(0 <= B, T, H4),           /* (4) */
    safe_deduce(B <= N, T, H6),           /* (6) */
    append(H5, H6, H5to6),
    append(H4, H5to6, H4to6),
    append(H3, H4to6, H3to6),
    append(H2, H3to6, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Abs(7): abs(X) <= N may_be_deduced_from [(1) N > 1,
%                                          (2) -N <= X,
%                                          (3) X < N].
try_new_deduction_strategies(abs(X) <= N, T, Hs) :-
    i_am_using_rule(abs_7a),
    (
        safe_deduce(N > 1, T, H1)          /* (1) */
    ;
        safe_deduce(N >= 2, T, H1)
    ),
    simplify(-N, MinusN),
    safe_deduce(MinusN <= X, T, H2),      /* (2) */
    (
        safe_deduce(X < N, T, H3)          /* (3) */
    ;
        T = integer,  /* in which case X<N <-> X<=N-1 */
        simplify(N-1, Nminus1),
        safe_deduce(X <= Nminus1, T, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= abs(X), T, Hs) :-
    i_am_using_rule(abs_7b),
    (
        safe_deduce(N > 1, T, H1)          /* (1) */
    ;
        safe_deduce(N >= 2, T, H1)
    ),
    simplify(-N, MinusN),
    safe_deduce(MinusN <= X, T, H2),      /* (2) */
    (
        safe_deduce(X < N, T, H3)          /* (3) */
    ;
        T = integer,  /* in which case X<N <-> X<=N-1 */
        simplify(N-1, Nminus1),
        safe_deduce(X <= Nminus1, T, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Abs(8): abs(X - Y) <= N may_be_deduced_from [(1) 0 <= X,
%                                              (2) X <= N,
%                                              (3) 0 <= Y,
%                                              (4) Y <= N]. */
try_new_deduction_strategies(abs(X-Y) <= N, T, Hs) :-
    i_am_using_rule(abs_8a),
    safe_deduce(0 <= X, T, H1),           /* (1) */
    safe_deduce(X <= N, T, H2),           /* (2) */
    safe_deduce(0 <= Y, T, H3),           /* (3) */
    safe_deduce(Y <= N, T, H4),           /* (4) */
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= abs(X-Y), T, Hs) :-
    i_am_using_rule(abs_8b),
    safe_deduce(0 <= X, T, H1),           /* (1) */
    safe_deduce(X <= N, T, H2),           /* (2) */
    safe_deduce(0 <= Y, T, H3),           /* (3) */
    safe_deduce(Y <= N, T, H4),           /* (4) */
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Abs(9): abs(X - Y) >= 1 may_be_deduced_from... {one of} [X > Y]
%                                                         [X < Y]
%                                                         [X <> Y].  {all for integers} */
try_new_deduction_strategies(abs(X-Y) >= 1, integer, Hs) :- i_am_using_rule(abs_9a), safe_deduce(X > Y, integer, Hs).
try_new_deduction_strategies(1 <= abs(X-Y), integer, Hs) :- i_am_using_rule(abs_9b), safe_deduce(X > Y, integer, Hs).
try_new_deduction_strategies(abs(X-Y) >= 1, integer, Hs) :- i_am_using_rule(abs_9c), safe_deduce(Y > X, integer, Hs).
try_new_deduction_strategies(1 <= abs(X-Y), integer, Hs) :- i_am_using_rule(abs_9d), safe_deduce(Y > X, integer, Hs).
try_new_deduction_strategies(abs(X-Y) >= 1, integer, Hs) :- i_am_using_rule(abs_9e), safe_deduce(X <> Y, integer, Hs).
try_new_deduction_strategies(1 <= abs(X-Y), integer, Hs) :- i_am_using_rule(abs_9f), safe_deduce(X <> Y, integer, Hs).


% Integer division rules
%-----------------------

% Div(1): (X+Y) div 2 < Y may_be_deduced_from [(1) 0 <= X,
%                                              (2) X < Y]. {for integers}
try_new_deduction_strategies(XplusY div 2 < Y, integer, Hs) :-
    i_am_using_rule(div_1a),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    (
        safe_deduce(X < Y, integer, H2)         /* (2) */
    ;
        simplify(Y-1, Yminus1),
        safe_deduce(X <= Yminus1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(Y > XplusY div 2, integer, Hs) :-
    i_am_using_rule(div_1b),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    (
        safe_deduce(X < Y, integer, H2)         /* (2) */
    ;
        simplify(Y-1, Yminus1),
        safe_deduce(X <= Yminus1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(XplusY div 2 <= Y - 1, integer, Hs) :-
    i_am_using_rule(div_1c),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    (
        safe_deduce(X < Y, integer, H2)         /* (2) */
    ;
        simplify(Y-1, Yminus1),
        safe_deduce(X <= Yminus1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(Y - 1 >= XplusY div 2, integer, Hs) :-
    i_am_using_rule(div_1d),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    (
        safe_deduce(X < Y, integer, H2)         /* (2) */
    ;
        simplify(Y-1, Yminus1),
        safe_deduce(X <= Yminus1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

% Div(2): (X+Y) div 2 >= X may_be_deduced_from [(1) X <= Y]. {for integers}
try_new_deduction_strategies(XplusY div 2 >= X, integer, Hs) :-
    i_am_using_rule(div_2a),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(X <= Y,  integer, Hs).         /* (1) */

try_new_deduction_strategies(X <= XplusY div 2, integer, Hs) :-
    i_am_using_rule(div_2b),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(X <= Y,  integer, Hs).         /* (1) */

% Div(3): (X+Y) div 2 <= Y may_be_deduced_from [(1) X <= Y]. {for integers}
try_new_deduction_strategies(XplusY div 2 <= Y, integer, Hs) :-
    i_am_using_rule(div_3a),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(X <= Y,  integer, Hs).         /* (1) */

try_new_deduction_strategies(Y >= XplusY div 2, integer, Hs) :-
    i_am_using_rule(div_3b),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(X <= Y,  integer, Hs).         /* (1) */

% Div(4): (X+Y) div 2 >= N may_be_deduced_from [(1) X >= N,
%                                               (2) Y >= N].
try_new_deduction_strategies(XplusY div 2 >= N, integer, Hs) :-
    i_am_using_rule(div_4a),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(X >= N, integer, H1),          /* (1) */
    safe_deduce(Y >= N, integer, H2),          /* (2) */
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N <= XplusY div 2, integer, Hs) :-
    i_am_using_rule(div_4b),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(X >= N, integer, H1),          /* (1) */
    safe_deduce(Y >= N, integer, H2),          /* (2) */
    append(H1, H2, HL),
    sort(HL, Hs).

% Div(5): (X+Y) div 2 <= N may_be_deduced_from [(1) X <= N,
%                                               (2) Y <= N].
try_new_deduction_strategies(XplusY div 2 <= N, integer, Hs) :-
    i_am_using_rule(div_5a),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(X <= N, integer, H1),          /* (1) */
    safe_deduce(Y <= N, integer, H2),          /* (2) */
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= XplusY div 2, integer, Hs) :-
    i_am_using_rule(div_5b),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(X <= N, integer, H1),          /* (1) */
    safe_deduce(Y <= N, integer, H2),          /* (2) */
    append(H1, H2, HL),
    sort(HL, Hs).

% Div(6): X < (X+Y) div 2 may_be_deduced_from [(1) 0 <= X,
%                                              (2) X < Y,
%                                              (3) one of {X+1 <> Y | X+1 < Y | X+2 <= Y}]. {for integers}
try_new_deduction_strategies(X < XplusY div 2, integer, Hs) :-
    i_am_using_rule(div_6a),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    (
        safe_deduce(X < Y, integer, H2)         /* (2) */
    ;
        simplify(Y-1, Yminus1),
        safe_deduce(X <= Yminus1, integer, H2)
    ),
    (
        simplify(X+1, Xplus1),
        (
            safe_deduce(Xplus1 <> Y, integer, H3)     /* (3) */
        ;
            safe_deduce(Xplus1 < Y, integer, H3)
        )
    ;
        simplify(X+2, Xplus2),
        safe_deduce(Xplus2 <= Y, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(XplusY div 2 > X, integer, Hs) :-
    i_am_using_rule(div_6b),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    (
        safe_deduce(X < Y, integer, H2)         /* (2) */
    ;
        simplify(Y-1, Yminus1),
        safe_deduce(X <= Yminus1, integer, H2)
    ),
    (
        simplify(X+1, Xplus1),
        (
            safe_deduce(Xplus1 <> Y, integer, H3)     /* (3) */
        ;
            safe_deduce(Xplus1 < Y, integer, H3)
        )
    ;
        simplify(X+2, Xplus2),
        safe_deduce(Xplus2 <= Y, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(X + 1 <= XplusY div 2, integer, Hs) :-
    i_am_using_rule(div_6c),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    (
        safe_deduce(X < Y, integer, H2)         /* (2) */
    ;
        simplify(Y-1, Yminus1),
        safe_deduce(X <= Yminus1, integer, H2)
    ),
    (
        simplify(X+1, Xplus1),
        (
            safe_deduce(Xplus1 <> Y, integer, H3)     /* (3) */
        ;
            safe_deduce(Xplus1 < Y, integer, H3)
        )
    ;
        simplify(X+2, Xplus2),
        safe_deduce(Xplus2 <= Y, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(XplusY div 2 >= X + 1, integer, Hs) :-
    i_am_using_rule(div_6d),
    (
        XplusY = X + Y
    ;
        XplusY = Y + X
    ),
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    (
        safe_deduce(X < Y, integer, H2)         /* (2) */
    ;
        simplify(Y-1, Yminus1),
        safe_deduce(X <= Yminus1, integer, H2)
    ),
    (
        simplify(X+1, Xplus1),
        (
            safe_deduce(Xplus1 <> Y, integer, H3)     /* (3) */
        ;
            safe_deduce(Xplus1 < Y, integer, H3)
        )
    ;
        simplify(X+2, Xplus2),
        safe_deduce(Xplus2 <= Y, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(7): X div Y >= M may_be_deduced_from [(1) M <= 0,
%                                           (2) X >= 0,
%                                           (3) Y > 0].
try_new_deduction_strategies(X div Y >= M, integer, Hs) :-
    i_am_using_rule(div_7a),
    safe_deduce(M <= 0, integer, H1),          /* (1) */
    safe_deduce(X >= 0, integer, H2),          /* (2) */
    (
        safe_deduce(Y > 0, integer, H3)         /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(M <= X div Y, integer, Hs) :-
    i_am_using_rule(div_7b),
    safe_deduce(M <= 0, integer, H1),          /* (1) */
    safe_deduce(X >= 0, integer, H2),          /* (2) */
    (
        safe_deduce(Y > 0, integer, H3)         /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(8): X div Y >= M may_be_deduced_from [(1) M <= 0,
%                                           (2) M <= X,
%                                           (3) Y > 0].
try_new_deduction_strategies(X div Y >= M, integer, Hs) :-
    i_am_using_rule(div_8a),
    safe_deduce(M <= 0, integer, H1),          /* (1) */
    safe_deduce(M <= X, integer, H2),          /* (2) */
    (
        safe_deduce(Y > 0, integer, H3)         /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(M <= X div Y, integer, Hs) :-
    i_am_using_rule(div_8b),
    safe_deduce(M <= 0, integer, H1),          /* (1) */
    safe_deduce(M <= X, integer, H2),          /* (2) */
    (
        safe_deduce(Y > 0, integer, H3)         /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(9): X div Y <= N may_be_deduced_from [(1) X <= N,
%                                           (2) 0 <= N,
%                                           (3) Y > 0].
try_new_deduction_strategies(X div Y <= N, integer, Hs) :-
    i_am_using_rule(div_9a),
    safe_deduce(0 <= N, integer, H2),          /* (2) */
    safe_deduce(X <= N, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H3)         /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= X div Y, integer, Hs) :-
    i_am_using_rule(div_9b),
    safe_deduce(0 <= N, integer, H2),          /* (2) */
    safe_deduce(X <= N, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H3)         /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(10): X div Y <= N may_be_deduced_from [(1) X <= Y * N,
%                                            (2) Y > 0].
try_new_deduction_strategies(X div Y <= N, integer, Hs) :-
    i_am_using_rule(div_10a),
    simplify(Y*N, YtimesN),
    safe_deduce(X <= YtimesN, integer, H1),    /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= X div Y, integer, Hs) :-
    i_am_using_rule(div_10b),
    simplify(Y*N, YtimesN),
    safe_deduce(X <= YtimesN, integer, H1),    /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

% Div(11): X div Y <= N may_be_deduced_from [(1) X <= Y * N + Y - 1,
%                                            (2) N >= 0,
%                                            (3) Y > 0].
try_new_deduction_strategies(X div Y <= N, integer, Hs) :-
    i_am_using_rule(div_11a),
    simplify(Y*(N+1)-1, Product),
    safe_deduce(X <= Product, integer, H1),    /* (1) */
    safe_deduce(N >= 0, integer, H2),          /* (2) */
    (
        safe_deduce(Y > 0, integer, H3)         /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= X div Y, integer, Hs) :-
    i_am_using_rule(div_11b),
    simplify(Y*(N+1)-1, Product),
    safe_deduce(X <= Product, integer, H1),    /* (1) */
    safe_deduce(N >= 0, integer, H2),          /* (2) */
    (
        safe_deduce(Y > 0, integer, H3)         /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(12): N <= X div Y may_be_deduced_from [(1) Y * N <= X,
%                                            (2) Y > 0].
try_new_deduction_strategies(N <= X div Y, integer, Hs) :-
    i_am_using_rule(div_12a),
    simplify(Y*N, YtimesN),
    safe_deduce(YtimesN <= X, integer, H1),    /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(X div Y >= N, integer, Hs) :-
    i_am_using_rule(div_12b),
    simplify(Y*N, YtimesN),
    safe_deduce(YtimesN <= X, integer, H1),    /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

% Div(13): X div Y * Y >= N may_be_deduced_from [(1) X >= 0,
%                                                (2) Y > 0,
%                                                (3) {N <= 0 | N <= X-Y+1} one of].
try_new_deduction_strategies(XdivYtimesY >= N, integer, Hs) :-
    i_am_using_rule(div_13a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    (
        safe_deduce(N <= 0, integer, H3)        /* (3) */
    ;
        safe_deduce(N <= X-Y+1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).
try_new_deduction_strategies(N <= XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_13b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    (
        safe_deduce(N <= 0, integer, H3)        /* (3) */
    ;
        safe_deduce(N <= X-Y+1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(14): X div Y * Y <= N may_be_deduced_from [(1) X >= 0,
%                                                (2) Y > 0,
%                                                (3) X <= N].
try_new_deduction_strategies(XdivYtimesY <= N, integer, Hs) :-
    i_am_using_rule(div_14a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    safe_deduce(X <= N, integer, H3),          /* (3) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_14b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    safe_deduce(X <= N, integer, H3),          /* (3) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(15): X div Y * Y = 0 may_be_deduced_from [(1) X >= 0,
%                                               (2) Y > X].
try_new_deduction_strategies(XdivYtimesY = 0, integer, Hs) :-
    i_am_using_rule(div_15a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > X, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= X + 1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(0 = XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_15b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > X, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= X + 1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(X - XdivYtimesY = X, integer, Hs) :-
    i_am_using_rule(div_15c),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > X, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= X + 1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(X = X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_15d),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > X, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= X + 1, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

% Div(16): X div Y * Y >= N may_be_deduced_from [(1) X <= 0,
%                                                (2) Y > 0,
%                                                (3) X >= N].
try_new_deduction_strategies(XdivYtimesY >= N, integer, Hs) :-
    i_am_using_rule(div_16a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    safe_deduce(X >= N, integer, H3),          /* (3) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N <= XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_16b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    safe_deduce(X >= N, integer, H3),          /* (3) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(17): X div Y * Y <= N may_be_deduced_from [(1) X <= 0,
%                                                (2) Y > 0,
%                                                (3) X + Y - 1 <= N].
try_new_deduction_strategies(X div Y * Y <= N, integer, Hs) :-
    i_am_using_rule(div_17a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    safe_deduce(X + Y - 1 <= N, integer, H3),       /* (3) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_17b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    safe_deduce(X + Y - 1 <= N, integer, H3),       /* (3) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(18): X div Y * Y = 0 may_be_deduced_from [(1) X <= 0,
%                                               (2) Y > -X].
try_new_deduction_strategies(XdivYtimesY = 0, integer, Hs) :-
    i_am_using_rule(div_18a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > -X, integer, H2)        /* (2) */
    ;
        safe_deduce(Y - 1 >= -X, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(0 = XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_18b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > -X, integer, H2)        /* (2) */
    ;
        safe_deduce(Y - 1 >= -X, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(X - XdivYtimesY = X, integer, Hs) :-
    i_am_using_rule(div_18c),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > -X, integer, H2)        /* (2) */
    ;
        safe_deduce(Y - 1 >= -X, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(X = X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_18d),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > -X, integer, H2)        /* (2) */
    ;
        safe_deduce(Y - 1 >= -X, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

% Div(19): X div Y * Y >= -N may_be_deduced_from [(1) N > 1,
%                                                 (2) -N <= X,
%                                                 (3) X <= N-1,
%                                                 (4) Y >= 1].
try_new_deduction_strategies(XdivYtimesY >= MinusN, integer, Hs) :-
    i_am_using_rule(div_19a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(MinusN <= X, integer, H2),     /* (2) */
    simplify(-MinusN, N),
    (
        safe_deduce(N > 1, integer, H1)         /* (1) */
    ;
        safe_deduce(N >= 2, integer, H1)
    ),
    simplify(N-1, Nminus1),
    safe_deduce(X <= Nminus1, integer, H3),    /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(MinusN <= XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_19b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(MinusN <= X, integer, H2),     /* (2) */
    simplify(-MinusN, N),
    (
        safe_deduce(N > 1, integer, H1)         /* (1) */
    ;
        safe_deduce(N >= 2, integer, H1)
    ),
    simplify(N-1, Nminus1),
    safe_deduce(X <= Nminus1, integer, H3),    /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(20): X div Y * Y <= M may_be_deduced_from [(1) M > 0,
%                                                (2) -(M+1) <= X,
%                                                (3) X <= M,
%                                                (4) Y >= 1].
try_new_deduction_strategies(XdivYtimesY <= M, integer, Hs) :-
    i_am_using_rule(div_20a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    (
        safe_deduce(M > 0, integer, H1)         /* (1) */
    ;
        safe_deduce(M >= 1, integer, H1)
    ),
    simplify(-(M+1), MinusMplus1),
    safe_deduce(MinusMplus1 <= X, integer, H2),     /* (2) */
    safe_deduce(X <= M, integer, H3),          /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(M >= XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_20b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    (
        safe_deduce(M > 0, integer, H1)         /* (1) */
    ;
        safe_deduce(M >= 1, integer, H1)
    ),
    simplify(-(M+1), MinusMplus1),
    safe_deduce(MinusMplus1 <= X, integer, H2),     /* (2) */
    safe_deduce(X <= M, integer, H3),          /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(21): X div Y * Y = 0 may_be_deduced_from [(1) M > 0,
%                                               (2) -(M+1) <= X,
%                                               (3) X <= M,
%                                               (4) Y >= M+2 ].
try_new_deduction_strategies(XdivYtimesY = 0, integer, Hs) :-
    i_am_using_rule(div_21a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    infrule(X <= M, H3),             /* (3): generates candidate M */
    (
        int(M),
        H1 = [],
        M > 0               /* (1) */
    ;
        safe_deduce(M > 0, integer, H1)
    ;
        safe_deduce(M >= 1, integer, H1)
    ),
    simplify(-(M+1), MinusMPlus1),
    safe_deduce(MinusMPlus1 <= X, integer, H2),     /* (2) */
    (
        simplify(M+2, MPlusI),
        safe_deduce(Y >= MPlusI, integer, H4)   /* (4) */
    ;
        simplify(M+1, MPlusI),
        safe_deduce(Y > MPlusI, integer, H4)
    ),
    append(H3, H4, H3to4),
    append(H2, H3to4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(0 = XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_21b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    infrule(X <= M, H3),             /* (3): generates candidate M */
    (
        int(M),
        H1 = [],
        M > 0               /* (1) */
    ;
        safe_deduce(M > 0, integer, H1)
    ;
        safe_deduce(M >= 1, integer, H1)
    ),
    simplify(-(M+1), MinusMPlus1),
    safe_deduce(MinusMPlus1 <= X, integer, H2),     /* (2) */
    (
        simplify(M+2, MPlusI),
        safe_deduce(Y >= MPlusI, integer, H4)   /* (4) */
    ;
        simplify(M+1, MPlusI),
        safe_deduce(Y > MPlusI, integer, H4)
    ),
    append(H3, H4, H3to4),
    append(H2, H3to4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(X - XdivYtimesY = X, integer, Hs) :-
    i_am_using_rule(div_21c),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    infrule(X <= M, H3),             /* (3): generates candidate M */
    (
        int(M),
        H1 = [],
        M > 0               /* (1) */
    ;
        safe_deduce(M > 0, integer, H1)
    ;
        safe_deduce(M >= 1, integer, H1)
    ),
    simplify(-(M+1), MinusMPlus1),
    safe_deduce(MinusMPlus1 <= X, integer, H2),     /* (2) */
    (
        simplify(M+2, MPlusI),
        safe_deduce(Y >= MPlusI, integer, H4)   /* (4) */
    ;
        simplify(M+1, MPlusI),
        safe_deduce(Y > MPlusI, integer, H4)
    ),
    append(H3, H4, H3to4),
    append(H2, H3to4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(X = X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_21d),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    infrule(X <= M, H3),             /* (3): generates candidate M */
    (
        int(M),
        H1 = [],
        M > 0               /* (1) */
    ;
        safe_deduce(M > 0, integer, H1)
    ;
        safe_deduce(M >= 1, integer, H1)
    ),
    simplify(-(M+1), MinusMPlus1),
    safe_deduce(MinusMPlus1 <= X, integer, H2),     /* (2) */
    (
        simplify(M+2, MPlusI),
        safe_deduce(Y >= MPlusI, integer, H4)   /* (4) */
    ;
        simplify(M+1, MPlusI),
        safe_deduce(Y > MPlusI, integer, H4)
    ),
    append(H3, H4, H3to4),
    append(H2, H3to4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(22): X - X div Y * Y <= N may_be_deduced_from [(1) X >= 0,
%                                                    (2) Y > 0,
%                                                    (3) {X <= N | Y - 1 <= N} one of].
try_new_deduction_strategies(X - XdivYtimesY <= N, integer, Hs) :-
    i_am_using_rule(div_22a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    (
        safe_deduce(X <= N, integer, H3)        /* (3) */
    ;
        safe_deduce(Y - 1 <= N, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_22b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    (
        safe_deduce(X <= N, integer, H3)        /* (3) */
    ;
        safe_deduce(Y - 1 <= N, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(23): X - X div Y * Y >= N may_be_deduced_from [(1) N <= 0,
%                                                    (2) 0 <= X,
%                                                    (3) Y > 0].
try_new_deduction_strategies(X - XdivYtimesY >= N, integer, Hs) :-
    i_am_using_rule(div_23a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(0 <= X, integer, H2),          /* (2) */
    (
        safe_deduce(Y > 0, integer, H3)         /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ),
    safe_deduce(N <= 0, integer, H1),          /* (1) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N <= X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_23b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(0 <= X, integer, H2),          /* (2) */
    (
        safe_deduce(Y > 0, integer, H3)         /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ),
    safe_deduce(N <= 0, integer, H1),          /* (1) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(24): X - X div Y * Y <= N may_be_deduced_from [(1) X <= 0,
%                                                    (2) Y > 0,
%                                                    (3) 0 <= N].
try_new_deduction_strategies(X - XdivYtimesY <= N, integer, Hs) :-
    i_am_using_rule(div_24a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    safe_deduce(0 <= N, integer, H3),          /* (3) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_24b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    safe_deduce(0 <= N, integer, H3),          /* (3) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(25): X - X div Y * Y >= N may_be_deduced_from [(1) X <= 0,
%                                                    (2) Y > 0,
%                                                    (3) N <= 1 - Y].
try_new_deduction_strategies(X - XdivYtimesY >= N, integer, Hs) :-
    i_am_using_rule(div_25a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    safe_deduce(N <= 1 - Y, integer, H3),      /* (3) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N <= X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_25b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X <= 0, integer, H1),          /* (1) */
    (
        safe_deduce(Y > 0, integer, H2)         /* (2) */
    ;
        safe_deduce(Y >= 1, integer, H2)
    ),
    safe_deduce(N <= 1 - Y, integer, H3),      /* (3) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(26): X - X div Y * Y >= -N may_be_deduced_from [(1) N > 1,
%                                                     (2) X >= 0,
%                                                     (3) X <= N-1,
%                                                     (4) Y >= 1 ].
try_new_deduction_strategies(X - XdivYtimesY >= MinusN, integer, Hs) :-
    i_am_using_rule(div_26a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H2),          /* (2) */
    simplify(-MinusN, N),
    (
        safe_deduce(N > 1, integer, H1)         /* (1) */
    ;
        safe_deduce(N >= 2, integer, H1)
    ),
    simplify(N-1, Nminus1),
    safe_deduce(X <= Nminus1, integer, H3),    /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(MinusN <= X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_26b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H2),          /* (2) */
    simplify(-MinusN, N),
    (
        safe_deduce(N > 1, integer, H1)         /* (1) */
    ;
        safe_deduce(N >= 2, integer, H1)
    ),
    simplify(N-1, Nminus1),
    safe_deduce(X <= Nminus1, integer, H3),    /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(27): X - X div Y * Y >= -N may_be_deduced_from [(1) N > 1,
%                                                     (2) -N <= X,
%                                                     (3) X <= N-1,
%                                                     (4) Y >= 1,
%                                                     (5) Y <= N+1 ].
try_new_deduction_strategies(X - XdivYtimesY >= MinusN, integer, Hs) :-
    i_am_using_rule(div_27a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(MinusN <= X, integer, H2),     /* (2) */
    simplify(-MinusN, N),
    (
        safe_deduce(N > 1, integer, H1)         /* (1) */
    ;
        safe_deduce(N >= 2, integer, H1)
    ),
    simplify(N-1, Nminus1),
    safe_deduce(X <= Nminus1, integer, H3),    /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    (
        simplify(N+1, Nplus1),
        safe_deduce(Y <= Nplus1, integer, H5)   /* (5) */
    ;
        simplify(N+2, Nplus2),
        safe_deduce(Y < Nplus2, integer, H5)
    ),
    append(H4, H5, H4to5),
    append(H3, H4to5, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(MinusN =< X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_27b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(MinusN <= X, integer, H2),     /* (2) */
    simplify(-MinusN, N),
    (
        safe_deduce(N > 1, integer, H1)         /* (1) */
    ;
        safe_deduce(N >= 2, integer, H1)
    ),
    simplify(N-1, Nminus1),
    safe_deduce(X <= Nminus1, integer, H3),    /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    (
        simplify(N+1, Nplus1),
        safe_deduce(Y <= Nplus1, integer, H5)   /* (5) */
    ;
        simplify(N+2, Nplus2),
        safe_deduce(Y < Nplus2, integer, H5)
    ),
    append(H4, H5, H4to5),
    append(H3, H4to5, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(28): X - X div Y * Y <= M may_be_deduced_from [(1) M > 0,
%                                                    (2) X >= 0,
%                                                    (3) X <= M,
%                                                    (4) Y >= 1].
try_new_deduction_strategies(X - XdivYtimesY <= M, integer, Hs) :-
    i_am_using_rule(div_28a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H2),          /* (2) */
    (
        safe_deduce(M > 0, integer, H1)         /* (1) */
    ;
        safe_deduce(M >= 1, integer, H1)
    ),
    safe_deduce(X <= M, integer, H3),          /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(M >= X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_28b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    safe_deduce(X >= 0, integer, H2),          /* (2) */
    (
        safe_deduce(M > 0, integer, H1)         /* (1) */
    ;
        safe_deduce(M >= 1, integer, H1)
    ),
    safe_deduce(X <= M, integer, H3),          /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    append(H3, H4, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(29): X - X div Y * Y <= M may_be_deduced_from [(1) M > 0,
%                                                    (2) -(M+1) <= X,
%                                                    (3) X <= M,
%                                                    (4) Y >= 1,
%                                                    (5) Y <= M+2].
try_new_deduction_strategies(X - XdivYtimesY <= M, integer, Hs) :-
    i_am_using_rule(div_29a),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    simplify(-(M+1), MinusMplus1),
    safe_deduce(MinusMplus1 <= X, integer, H2),     /* (2) */
    (
        safe_deduce(M > 0, integer, H1)         /* (1) */
    ;
        safe_deduce(M >= 1, integer, H1)
    ),
    safe_deduce(X <= M, integer, H3),          /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    (
        simplify(M+2, Mplus2),
        safe_deduce(Y <= Mplus2, integer, H5)   /* (5) */
    ;
        simplify(M+3, Mplus3),
        safe_deduce(Y < Mplus3, integer, H5)
    ),
    append(H4, H5, H4to5),
    append(H3, H4to5, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(M >= X - XdivYtimesY, integer, Hs) :-
    i_am_using_rule(div_29b),
    (
        XdivYtimesY = X div Y * Y
    ;
        XdivYtimesY = Y * (X div Y)
    ),
    simplify(-(M+1), MinusMplus1),
    safe_deduce(MinusMplus1 <= X, integer, H2),     /* (2) */
    (
        safe_deduce(M > 0, integer, H1)         /* (1) */
    ;
        safe_deduce(M >= 1, integer, H1)
    ),
    safe_deduce(X <= M, integer, H3),          /* (3) */
    (
        safe_deduce(Y > 0, integer, H4)         /* (4) */
    ;
        safe_deduce(Y >= 1, integer, H4)
    ),
    (
        simplify(M+2, Mplus2),
        safe_deduce(Y <= Mplus2, integer, H5)   /* (5) */
    ;
        simplify(M+3, Mplus3),
        safe_deduce(Y < Mplus3, integer, H5)
    ),
    append(H4, H5, H4to5),
    append(H3, H4to5, Htail),
    append(H2, Htail, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(30): X <= Y may_be_deduced_from [(1) N > 0,
%                                      (2) M div N <= Y,
%                                      (3) X * N <= M].
try_new_deduction_strategies(X <= Y, integer, Hs) :-
    i_am_using_rule(div_30a),
    infrule(X * N <= M, H3),         /* (3): generates candidate M, N */
    (
        safe_deduce(N > 0, integer, H1)         /* (1) */
    ;
        safe_deduce(N >= 1, integer, H1)
    ),
    safe_deduce(M div N <= Y, integer, H2),    /* (2) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(Y >= X, integer, Hs) :-
    i_am_using_rule(div_30b),
    infrule(X * N <= M, H3),         /* (3): generates candidate M, N */
    (
        safe_deduce(N > 0, integer, H1)         /* (1) */
    ;
        safe_deduce(N >= 1, integer, H1)
    ),
    safe_deduce(M div N <= Y, integer, H2),    /* (2) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(31): X >= N may_be_deduced_from [(1) N <> 0,
%                                      (2) X >= 0,
%                                      (3) X div N <> 0].
try_new_deduction_strategies(X >= N, integer, Hs) :-
    i_am_using_rule(div_31a),
    safe_deduce(N <> 0, integer, H1),          /* (1) */
    safe_deduce(X div N <> 0, integer, H3),    /* (3) */
    safe_deduce(X >= 0, integer, H2),          /* (2) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N <= X, integer, Hs) :-
    i_am_using_rule(div_31b),
    safe_deduce(N <> 0, integer, H1),          /* (1) */
    safe_deduce(X div N <> 0, integer, H3),    /* (3) */
    safe_deduce(X >= 0, integer, H2),          /* (2) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Div(32): X div Y >= -(M+1) may_be_deduced_from [(1) M > 0,
%                                                 (2) -(M+1) <= X,
%                                                 (3) X <= M,
%                                                 (4) Y <> 0].
try_new_deduction_strategies(X div Y >= MinusMplus1, integer, Hs) :-
    i_am_using_rule(div_32a),
    int(MinusMplus1),
    simplify(-MinusMplus1-1, M),
    simplify(M > 0, true),           /* (1) */
    safe_deduce(MinusMplus1 <= X, integer, H2),     /* (2) */
    safe_deduce(X <= M, integer, H3),          /* (3) */
    (
        safe_deduce(Y <> 0, integer, H4)        /* (4) */
    ;
        (
            safe_deduce(Y > 0, integer, H4)
        ;
            safe_deduce(Y >= 1, integer, H4)
        ;
            safe_deduce(Y < 0, integer, H4)
        ;
            safe_deduce(Y <= - 1, integer, H4)
        )
    ),
    append(H3, H4, Hrest),
    append(H2, Hrest, HL),     /* N.B.  There is no H1 */
    sort(HL, Hs).

try_new_deduction_strategies(MinusMplus1 <= X div Y, integer, Hs) :-
    i_am_using_rule(div_32b),
    int(MinusMplus1),
    simplify(-MinusMplus1-1, M),
    simplify(M > 0, true),           /* (1) */
    safe_deduce(MinusMplus1 <= X, integer, H2),     /* (2) */
    safe_deduce(X <= M, integer, H3),          /* (3) */
    (
        safe_deduce(Y <> 0, integer, H4)        /* (4) */
    ;
        (
            safe_deduce(Y > 0, integer, H4)
        ;
            safe_deduce(Y >= 1, integer, H4)
        ;
            safe_deduce(Y < 0, integer, H4)
        ;
            safe_deduce(Y <= - 1, integer, H4)
        )
    ),
    append(H3, H4, Hrest),
    append(H2, Hrest, HL),     /* N.B.  There is no H1 */
    sort(HL, Hs).

% Div(33): X div Y >= -(M+1) may_be_deduced_from [(1) M > 0,
%                                                 (2) -M <= X,
%                                                 (3) X <= M,
%                                                 (4) Y <> 0].
try_new_deduction_strategies(X div Y >= MinusMplus1, integer, Hs) :-
    i_am_using_rule(div_33a),
    int(MinusMplus1),
    simplify(-MinusMplus1-1, M),
    simplify(M > 0, true),           /* (1) */
    simplify(-M, MinusM),
    safe_deduce(MinusM <= X, integer, H2),     /* (2) */
    safe_deduce(X <= M, integer, H3),          /* (3) */
    (
        safe_deduce(Y <> 0, integer, H4)        /* (4) */
    ;
        (
            safe_deduce(Y > 0, integer, H4)
        ;
            safe_deduce(Y >= 1, integer, H4)
        ;
            safe_deduce(Y < 0, integer, H4)
        ;
            safe_deduce(Y <= - 1, integer, H4)
        )
    ),
    append(H3, H4, Hrest),
    append(H2, Hrest, HL),     /* N.B.  There is no H1 */
    sort(HL, Hs).

try_new_deduction_strategies(MinusMplus1 <= X div Y, integer, Hs) :-
    i_am_using_rule(div_33b),
    int(MinusMplus1),
    simplify(-MinusMplus1-1, M),
    simplify(M > 0, true),           /* (1) */
    simplify(-M, MinusM),
    safe_deduce(MinusM <= X, integer, H2),     /* (2) */
    safe_deduce(X <= M, integer, H3),          /* (3) */
    (
        safe_deduce(Y <> 0, integer, H4)        /* (4) */
    ;
        (
            safe_deduce(Y > 0, integer, H4)
        ;
            safe_deduce(Y >= 1, integer, H4)
        ;
            safe_deduce(Y < 0, integer, H4)
        ;
            safe_deduce(Y <= - 1, integer, H4)
        )
    ),
    append(H3, H4, Hrest),
    append(H2, Hrest, HL),     /* N.B.  There is no H1 */
    sort(HL, Hs).

% Div(34): X div Y <= M may_be_deduced_from [(1) M > 0,
%                                            (2) -(M+1) <= X,
%                                            (3) X <= M,
%                                            (4) Y <> 0,
%                                            (5) Y <> -1].
try_new_deduction_strategies(X div Y <= M, integer, Hs) :-
    i_am_using_rule(div_34a),
    int(M),
    simplify(M > 0, true),           /* (1) */
    safe_deduce(X <= M, integer, H3),          /* (3) */
    simplify(-(M+1), MinusMplus1),
    safe_deduce(MinusMplus1 <= X, integer, H2),     /* (2) */
    (
        safe_deduce(Y <> 0, integer, H4),       /* (4) */
        safe_deduce(Y <> - 1, integer, H5)      /* (5) */
    ;
        (
            safe_deduce(Y > 0, integer, H4)
        ;
            safe_deduce(Y >= 1, integer, H4)
        ;
            safe_deduce(Y < - 1, integer, H4)
        ;
            safe_deduce(Y <= - 2, integer, H4)
        ),
        H5 = []
    ),
    append(H4, H5, H4to5),
    append(H3, H4to5, Hrest),
    append(H2, Hrest, HL),     /* N.B.  There is no H1 */
    sort(HL, Hs).

try_new_deduction_strategies(M >= X div Y, integer, Hs) :-
    i_am_using_rule(div_34b),
    int(M),
    simplify(M > 0, true),           /* (1) */
    safe_deduce(X <= M, integer, H3),          /* (3) */
    simplify(-(M+1), MinusMplus1),
    safe_deduce(MinusMplus1 <= X, integer, H2),     /* (2) */
    (
        safe_deduce(Y <> 0, integer, H4),       /* (4) */
        safe_deduce(Y <> - 1, integer, H5)      /* (5) */
    ;
        (
            safe_deduce(Y > 0, integer, H4)
        ;
            safe_deduce(Y >= 1, integer, H4)
        ;
            safe_deduce(Y < - 1, integer, H4)
        ;
            safe_deduce(Y <= - 2, integer, H4)
        ),
        H5 = []
    ),
    append(H4, H5, H4to5),
    append(H3, H4to5, Hrest),
    append(H2, Hrest, HL),     /* N.B.  There is no H1 */
    sort(HL, Hs).

% Div(35): X div Y <= M may_be_deduced_from [(1) M > 0,
%                                            (2) -M <= X,
%                                            (3) X <= M,
%                                            (4) Y <> 0].
try_new_deduction_strategies(X div Y <= M, integer, Hs) :-
    i_am_using_rule(div_35a),
    int(M),
    simplify(M > 0, true),           /* (1) */
    safe_deduce(X <= M, integer, H3),          /* (3) */
    simplify(-M, MinusM),
    safe_deduce(MinusM <= X, integer, H2),     /* (2) */
    (
        safe_deduce(Y <> 0, integer, H4)        /* (4) */
    ;
        safe_deduce(Y > 0, integer, H4)
    ;
        safe_deduce(Y >= 1, integer, H4)
    ;
        safe_deduce(Y < 0, integer, H4)
    ;
        safe_deduce(Y <= - 1, integer, H4)
    ),
    append(H3, H4, Hrest),
    append(H2, Hrest, HL),     /* N.B.  There is no H1 */
    sort(HL, Hs).

try_new_deduction_strategies(M >= X div Y, integer, Hs) :-
    i_am_using_rule(div_35b),
    int(M),
    simplify(M > 0, true),           /* (1) */
    safe_deduce(X <= M, integer, H3),          /* (3) */
    simplify(-M, MinusM),
    safe_deduce(MinusM <= X, integer, H2),     /* (2) */
    (
        safe_deduce(Y <> 0, integer, H4)        /* (4) */
    ;
        safe_deduce(Y > 0, integer, H4)
    ;
        safe_deduce(Y >= 1, integer, H4)
    ;
        safe_deduce(Y < 0, integer, H4)
    ;
        safe_deduce(Y <= - 1, integer, H4)
    ),
    append(H3, H4, Hrest),
    append(H2, Hrest, HL),     /* N.B.  There is no H1 */
    sort(HL, Hs).

% Extra modulus rules
%--------------------

% Mod(1): X > 0 may_be_deduced_from [(1) X >= 0,
%                                    (2) N > 0,
%                                    (3) X mod N <> 0].
try_new_deduction_strategies(X > 0, integer, Hs) :-
    i_am_using_rule(mod_1a),
    get_provenance_framework(spark),
    infrule(X mod N <> 0, H3),            /* (3): generates candidate N */
    safe_deduce(N > 0, integer, H2),      /* (2) */
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(0 < X, integer, Hs) :-
    i_am_using_rule(mod_1b),
    get_provenance_framework(spark),
    infrule(X mod N <> 0, H3),            /* (3): generates candidate N */
    safe_deduce(N > 0, integer, H2),      /* (2) */
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Mod(2): X >= X mod N may_be_deduced_from [(1) X >= 0,
%                                           (2) N > 0].
try_new_deduction_strategies(X >= X mod N, integer, Hs) :-
    i_am_using_rule(mod_2a),
    get_provenance_framework(spark),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    safe_deduce(N > 0, integer, H2),      /* (2) */
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(X mod N <= X, integer, Hs) :-
    i_am_using_rule(mod_2b),
    get_provenance_framework(spark),
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    safe_deduce(N > 0, integer, H2),      /* (2) */
    append(H1, H2, HL),
    sort(HL, Hs).

% Mod(3): X div M mod N <= K may_be_deduced_from [(1) 0 <= X,
%                                                 (2) X <= U,
%                                                 (3) 0 < M,
%                                                 (4) 0 < N,
%                                                 (5) U div M = L,
%                                                 (6) L < N  {so L mod N = L},
%                                                 (7) L <= K].
try_new_deduction_strategies(X div M mod N <= K, integer, Hs) :-
    i_am_using_rule(mod_3a),
    get_provenance_framework(spark),
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    find_upper_numeric_limit_for(X, U, integer, H2), /* (2) */
    simplify(U>=0, true),   /* sanity check */
    safe_deduce(0 < M, integer, H3),      /* (3) */
    safe_deduce(0 < N, integer, H4),      /* (4) */
    simplify(U div M, L),            /* (5) */
    safe_deduce(L < N, integer, H6),      /* (6) */
    safe_deduce(L <= K, integer, H7),          /* (7) */
    append(H6, H7, H67),
    append(H4, H67, H467),  /* N.B. no H5 */
    append(H3, H467, H3467),
    append(H2, H3467, H23467),
    append(H1, H23467, HL),
    sort(HL, Hs).

try_new_deduction_strategies(K >= X div M mod N, integer, Hs) :-
    i_am_using_rule(mod_3b),
    get_provenance_framework(spark),
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    find_upper_numeric_limit_for(X, U, integer, H2), /* (2) */
    simplify(U>=0, true),   /* sanity check */
    safe_deduce(0 < M, integer, H3),      /* (3) */
    safe_deduce(0 < N, integer, H4),      /* (4) */
    simplify(U div M, L),            /* (5) */
    safe_deduce(L < N, integer, H6),      /* (6) */
    safe_deduce(L <= K, integer, H7),          /* (7) */
    append(H6, H7, H67),
    append(H4, H67, H467),  /* N.B. no H5 */
    append(H3, H467, H3467),
    append(H2, H3467, H23467),
    append(H1, H23467, HL),
    sort(HL, Hs).

% Mod(4): X mod N <= K may_be_deduced_from [(1) 0 <= X,
%                                           (2) X <= K,
%                                           (3) K <= N,
%                                           (4) 0 < N].
try_new_deduction_strategies(X mod N <= K, integer, Hs) :-
    i_am_using_rule(mod_4a),
    get_provenance_framework(spark),
    safe_deduce(N > 0,  integer, H4),          /* (4) */
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    safe_deduce(X <= K, integer, H2),          /* (2) */
    safe_deduce(K <= N, integer, H3),          /* (3) */
    append(H3, H4, H34),
    append(H2, H34, H234),
    append(H1, H234, HL),
    sort(HL, Hs).

try_new_deduction_strategies(K >= X mod N, integer, Hs) :-
    i_am_using_rule(mod_4b),
    get_provenance_framework(spark),
    safe_deduce(N > 0,  integer, H4),          /* (4) */
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    safe_deduce(X <= K, integer, H2),          /* (2) */
    safe_deduce(K <= N, integer, H3),          /* (3) */
    append(H3, H4, H34),
    append(H2, H34, H234),
    append(H1, H234, HL),
    sort(HL, Hs).

% Mod(5): Y + (X mod N) <= K may_be_deduced_from [(1) 0 <= X,
%                                                 (2) X <= A,
%                                                 (3) A <= N,
%                                                 (4) 0 < N,
%                                                 (5) Y <= B,
%                                                 (6) A+B <= K]. */
try_new_deduction_strategies(Y + (X mod N) <= K, integer, Hs) :-
    i_am_using_rule(mod_5a),
    get_provenance_framework(spark),
    safe_deduce(N > 0,  integer, H4),          /* (4) */
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    infrule(X <= A, H2),             /* (2) */
    safe_deduce(A <= N, integer, H3),          /* (3) */
    infrule(Y <= B, H5),             /* (5) */
    safe_deduce(A + B <= K, integer, H6),      /* (6) */
    append(H5, H6, H56),
    append(H4, H56, H456),
    append(H3, H456, H3456),
    append(H2, H3456, H23456),
    append(H1, H23456, HL),
    sort(HL, Hs).

try_new_deduction_strategies((X mod N) + Y <= K, integer, Hs) :-
    i_am_using_rule(mod_5b),
    get_provenance_framework(spark),
    safe_deduce(N > 0,  integer, H4),          /* (4) */
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    infrule(X <= A, H2),             /* (2) */
    safe_deduce(A <= N, integer, H3),          /* (3) */
    infrule(Y <= B, H5),             /* (5) */
    safe_deduce(A + B <= K, integer, H6),      /* (6) */
    append(H5, H6, H56),
    append(H4, H56, H456),
    append(H3, H456, H3456),
    append(H2, H3456, H23456),
    append(H1, H23456, HL),
    sort(HL, Hs).

try_new_deduction_strategies(K >= Y + (X mod N), integer, Hs) :-
    i_am_using_rule(mod_5c),
    get_provenance_framework(spark),
    safe_deduce(N > 0,  integer, H4),          /* (4) */
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    infrule(X <= A, H2),             /* (2) */
    safe_deduce(A <= N, integer, H3),          /* (3) */
    infrule(Y <= B, H5),             /* (5) */
    safe_deduce(A + B <= K, integer, H6),      /* (6) */
    append(H5, H6, H56),
    append(H4, H56, H456),
    append(H3, H456, H3456),
    append(H2, H3456, H23456),
    append(H1, H23456, HL),
    sort(HL, Hs).

try_new_deduction_strategies(K >= (X mod N) + Y, integer, Hs) :-
    i_am_using_rule(mod_5d),
    get_provenance_framework(spark),
    safe_deduce(N > 0,  integer, H4),          /* (4) */
    safe_deduce(0 <= X, integer, H1),          /* (1) */
    infrule(X <= A, H2),             /* (2) */
    infrule(Y <= B, H5),             /* (5) */
    safe_deduce(A <= N, integer, H3),          /* (3) */
    safe_deduce(A + B <= K, integer, H6),      /* (6) */
    append(H5, H6, H56),
    append(H4, H56, H456),
    append(H3, H456, H3456),
    append(H2, H3456, H23456),
    append(H1, H23456, HL),
    sort(HL, Hs).

% Mod(6): Y - X mod N <= K may_be_deduced_from [(1) X >= A,
%                                               (2) A >= 0,
%                                               (3) X <= N-1,
%                                               (4) 0 < N,
%                                               (5) Y <= B,
%                                               (6) B-A <= K].
try_new_deduction_strategies(Y - X mod N <= K, integer, Hs) :-
    i_am_using_rule(mod_6a),
    get_provenance_framework(spark),
    safe_deduce(N > 0,  integer, H4),          /* (4) */
    infrule(X >= A, H1),             /* (1) */
    safe_deduce(A >= 0, integer, H2),          /* (2) */
    infrule(Y <= B, H5),             /* (5) */
    safe_deduce(B - A <= K, integer, H6),      /* (6) */
    (
        safe_deduce(X <= N-1, integer, H3)      /* (3) */
    ;
        safe_deduce(X < N, integer, H3)
    ),
    append(H5, H6, H56),
    append(H4, H56, H456),
    append(H3, H456, H3456),
    append(H2, H3456, H23456),
    append(H1, H23456, HL),
    sort(HL, Hs).

try_new_deduction_strategies(K >= Y - X mod N, integer, Hs) :-
    i_am_using_rule(mod_6b),
    get_provenance_framework(spark),
    safe_deduce(N > 0,  integer, H4),          /* (4) */
    infrule(X >= A, H1),             /* (1) */
    safe_deduce(A >= 0, integer, H2),          /* (2) */
    infrule(Y <= B, H5),             /* (5) */
    safe_deduce(B - A <= K, integer, H6),      /* (6) */
    (
        safe_deduce(X <= N-1, integer, H3)      /* (3) */
    ;
        safe_deduce(X < N, integer, H3)
    ),
    append(H5, H6, H56),
    append(H4, H56, H456),
    append(H3, H456, H3456),
    append(H2, H3456, H23456),
    append(H1, H23456, HL),
    sort(HL, Hs).


% Exponentiation rules
%---------------------

% Pow(1): X ** Y >= 0 may_be_deduced_from [(1) X >= 0, (2) Y >= 0].
try_new_deduction_strategies(X ** Y >= 0, T, Hs) :-
    i_am_using_rule(pow_1a),
    get_provenance_framework(spark),
    safe_deduce(Y >= 0, integer, H2),          /* (2) */
    safe_deduce(X >= 0, T, H1),           /* (1) */
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(0 <= X ** Y, T, Hs) :-
    i_am_using_rule(pow_1b),
    get_provenance_framework(spark),
    safe_deduce(Y >= 0, integer, H2),          /* (2) */
    safe_deduce(X >= 0, T, H1),           /* (1) */
    append(H1, H2, HL),
    sort(HL, Hs).

% Pow(2): X ** Y <= N may_be_deduced_from [(1) 0 <= X,
%                                          (2) X <= A,
%                                          (3) Y >= 1,
%                                          (4) Y <= B,
%                                          (5) A ** B <= N].
try_new_deduction_strategies(X ** Y <= N, T, Hs) :-
    i_am_using_rule(pow_2a),
    get_provenance_framework(spark),
    safe_deduce(Y >= 1, integer, H3),          /* (3) */
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    infrule(X <= A, H2),             /* (2): generates candidate A */
    infrule(Y <= B, H4),             /* (4): generates candidate B */
    simplify(A ** B, AtotheB),
    safe_deduce(AtotheB <= N, T, H5),          /* (5) */
    append(H4, H5, H4to5),
    append(H3, H4to5, H3to5),
    append(H2, H3to5, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= X ** Y, T, Hs) :-
    i_am_using_rule(pow_2b),
    get_provenance_framework(spark),
    safe_deduce(Y >= 1, integer, H3),          /* (3) */
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    infrule(X <= A, H2),             /* (2): generates candidate A */
    infrule(Y <= B, H4),             /* (4): generates candidate B */
    simplify(A ** B, AtotheB),
    safe_deduce(AtotheB <= N, T, H5),          /* (5) */
    append(H4, H5, H4to5),
    append(H3, H4to5, H3to5),
    append(H2, H3to5, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Pow(3): X ** N = X ** (N-1) * X may_be_deduced_from [(1) N > 0].
try_new_deduction_strategies(X ** N = X ** Nminus1 * X, _, Hs) :-
    i_am_using_rule(pow_3a),
    get_provenance_framework(spark),
    (
        safe_deduce(N > 0, integer, H1)         /* (1) */
    ;
        safe_deduce(N >= 1, integer, H1)
    ),
    simplify(Nminus1 = N-1, PowerEquality),
    safe_deduce(PowerEquality, integer, H2),
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(X ** Nminus1 * X = X ** N, _, Hs) :-
    i_am_using_rule(pow_3b),
    get_provenance_framework(spark),
    (
        safe_deduce(N > 0, integer, H1)         /* (1) */
    ;
        safe_deduce(N >= 1, integer, H1)
    ),
    simplify(Nminus1 = N-1, PowerEquality),
    safe_deduce(PowerEquality, integer, H2),
    append(H1, H2, HL),
    sort(HL, Hs).

% Pow(4): X ** Y * Z <= N may_be_deduced_from [(1) Z >= 0,
%                                              (2) 0 <= X,
%                                              (3) X <= A,
%                                              (4) Y >= 1,
%                                              (5) Y <= B,
%                                              (6) A ** B * Z <= N].
try_new_deduction_strategies(XtotheYtimesZ <= N, T, Hs) :-
    i_am_using_rule(pow_4a),
    get_provenance_framework(spark),
    (
        XtotheYtimesZ = (X ** Y) * Z
    ;
        XtotheYtimesZ = Z * (X ** Y)
    ),
    safe_deduce(Z >= 0, integer, H1),          /* (1) */
    safe_deduce(Y >= 1, integer, H4),          /* (4) */
    safe_deduce(X >= 0, integer, H2),          /* (2) */
    (
        int(X), A=X, H3=[]            /* (3): generates candidate A */
    ;
        infrule(X <= A, H3)
    ),
    (
        int(Y), B=Y, H5=[]            /* (5): generates candidate B */
    ;
        infrule(Y <= B, H5)
    ),
    simplify(A ** B, AtotheB),
    simplify(AtotheB * Z, AtotheBtimesZ),
    safe_deduce(AtotheBtimesZ <= N, T, H6),    /* (6) */
    append(H5, H6, H5to6),
    append(H4, H5to6, H4to6),
    append(H3, H4to6, H3to6),
    append(H2, H3to6, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= XtotheYtimesZ, T, Hs) :-
    i_am_using_rule(pow_4b),
    get_provenance_framework(spark),
    (
        XtotheYtimesZ = (X ** Y) * Z
    ;
        XtotheYtimesZ = Z * (X ** Y)
    ),
    safe_deduce(Z >= 0, integer, H1),          /* (1) */
    safe_deduce(Y >= 1, integer, H4),          /* (4) */
    safe_deduce(X >= 0, integer, H2),          /* (2) */
    (
        int(X), A=X, H3=[]            /* (3): generates candidate A */
    ;
        infrule(X <= A, H3)
    ),
    (
        int(Y), B=Y, H5=[]            /* (5): generates candidate B */
    ;
        infrule(Y <= B, H5)
    ),
    simplify(A ** B, AtotheB),
    simplify(AtotheB * Z, AtotheBtimesZ),
    safe_deduce(AtotheBtimesZ <= N, T, H6),    /* (6) */
    append(H5, H6, H5to6),
    append(H4, H5to6, H4to6),
    append(H3, H4to6, H3to6),
    append(H2, H3to6, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Arith(1): X <= N may_be_deduced_from [(1) X+A<=Y+A,
%                                       (2) Y<=N].
try_new_deduction_strategies(X <= N, T, Hs) :-
    i_am_using_rule(arith_1),
    (
        infrule(X + A <= Y + A, H1)        /* (1) */
    ;
        infrule(X + A <= A + Y, H1)
    ;
        infrule(A + X <= Y + A, H1)
    ;
        infrule(A + X <= A + Y, H1)
    ;
        infrule(X - A <= Y - A, H1)  /* special case */
    ),
    safe_deduce(Y <= N, T, H2),           /* (2) */
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= X, T, Hs) :-
    i_am_using_rule(arith_1),
    (
        infrule(X + A <= Y + A, H1)        /* (1) */
    ;
        infrule(X + A <= A + Y, H1)
    ;
        infrule(A + X <= Y + A, H1)
    ;
        infrule(A + X <= A + Y, H1)
    ;
        infrule(X - A <= Y - A, H1)  /* special case */
    ),
    safe_deduce(Y <= N, T, H2),           /* (2) */
    append(H1, H2, HL),
    sort(HL, Hs).

% Arith(2): X <= N may_be_deduced_from [(1) X*Y + K <= Z,
%                                       (2) Y >= 1,
%                                       (3) Z <= M,
%                                       (4) (M-K) div Y <= N]. {K,M,Y integer literals}
try_new_deduction_strategies(X <= N, integer, Hs) :-
    i_am_using_rule(arith_2a),
    (
        XtimesY = X * Y
    ;
        XtimesY = Y * X
    ),
    (
        Sum = XtimesY + K
    ;
        Sum = K + XtimesY
    ),
    infrule(Sum <= Z, H1),           /* (1) */
    int(Y),
    simplify(Y >= 1, true),          /* (2): H2=[], effectively */
    int(K),
    (
        int(Z),
        M = Z,
        H3 = []
    ;
        infrule(Z <= M, H3),          /* (3) */
        int(M)
    ),
    simplify((M - K) div Y, Bound),
    safe_deduce(Bound <= N, integer, H4),      /* (4) */
    append(H3, H4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N >= X, integer, Hs) :-
    i_am_using_rule(arith_2b),
    (
        XtimesY = X * Y
    ;
        XtimesY = Y * X
    ),
    (
        Sum = XtimesY + K
    ;
        Sum = K + XtimesY
    ),
    infrule(Sum <= Z, H1),           /* (1) */
    int(Y),
    simplify(Y >= 1, true),          /* (2): H2=[], effectively */
    int(K),
    (
        int(Z),
        M = Z,
        H3 = []
    ;
        infrule(Z <= M, H3),          /* (3) */
        int(M)
    ),
    simplify((M - K) div Y, Bound),
    safe_deduce(Bound <= N, integer, H4),      /* (4) */
    append(H3, H4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% Arith(3): X >= N may_be_deduced_from [(1) X >= 0,
%                                       (2) X mod Y >= N,
%                                       (3) Y <> 0]. {restrict to literal N}
try_new_deduction_strategies(X >= N, integer, Hs) :-
    get_provenance_framework(spark),
    i_am_using_rule(arith_3a),
    int(N),
    simplify(N <> 0, true),    /* no need to use this rule if N=0 */
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        infrule(X mod Y >= N, H2)          /* (2) */
    ;
        infrule(X mod Y >= K, H2),
        int(K),
        simplify(K >= N, true)
    ),
    (
        safe_deduce(Y <> 0, integer, H3)        /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ;
        safe_deduce(Y <= - 1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

try_new_deduction_strategies(N <= X, integer, Hs) :-
    get_provenance_framework(spark),
    i_am_using_rule(arith_3b),
    int(N),
    simplify(N <> 0, true),    /* no need to use this rule if N=0 */
    safe_deduce(X >= 0, integer, H1),          /* (1) */
    (
        infrule(X mod Y >= N, H2)          /* (2) */
    ;
        infrule(X mod Y >= K, H2),
        int(K),
        simplify(K >= N, true)
    ),
    (
        safe_deduce(Y <> 0, integer, H3)        /* (3) */
    ;
        safe_deduce(Y >= 1, integer, H3)
    ;
        safe_deduce(Y <= - 1, integer, H3)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).


% Unification strategy: for a functor F, we can prove that F(X1,X2,...,Xn)
% = F(Y1,Y2,...,Yn) if we can show that the lists of arguments are provably
% equal, i.e. X1=Y1, X2=Y2, ..., Xn=Yn. The following clause therefore
% attempts to prove this (by invoking deduce_equal_arguments/3, which tests
% the argument lists for equality).
try_new_deduction_strategies(X = Y, _, Hs) :-
    \+ atomic(X),
    \+ atomic(Y),
    X =.. [F|Xargs],
    Y =.. [F|Yargs],         /* so goal is F(X1...Xn)=F(Y1...Yn)  */
    !,
    deduce_equal_arguments(Xargs, Yargs, Hs). /* show each Xi=Ji */

%===============================================================================
% int_enum_lit_or_const(+Identifier, -Which).
%-------------------------------------------------------------------------------
% Check identifier is an enumeration literal or constant.
%===============================================================================

int_enum_lit_or_const(Identifier, Which) :-
    (
        int_or_enum_lit(Identifier, Which)
    ;
        var_const(Identifier, _Type, c)
    ;
        base_rational(Identifier)       /* allow rational literals, too */
    ),
    !.

%===============================================================================
% deduce_equal_arguments(+List1, +List2, -HypsUsed).
%-------------------------------------------------------------------------------
% Successfull where two lists provably equal, returning hypothisies used as
% HypsUsed.
%===============================================================================

deduce_equal_arguments([X], [X], []) :-
    !.
deduce_equal_arguments([X], [Y], Hs) :-
    infer_subgoal(X=Y, Hs),
    !.
deduce_equal_arguments([X|Xs], [X|Ys], Hs) :-
    !,
    deduce_equal_arguments(Xs, Ys, Hs),
    !.
deduce_equal_arguments([X|Xs], [Y|Ys], Hyps) :-
    infer_subgoal(X=Y, H1),
    !,
    deduce_equal_arguments(Xs, Ys, Hs),
    !,
    append(H1, Hs, HL),
    sort(HL, Hyps),
    !.

%===============================================================================
% try_new_numeric_strategies(+GOAL, +Type, -HypList).
%-------------------------------------------------------------------------------
% Deduce numeric expression GOAL, of type T, giving Hs instantiated to list
% of hypotheses used. These new numeric strategies cater for numeric bounds
% & repeated terms.
%===============================================================================

% Numeric literal on either side of an inequality (ignore =, <>)
%---------------------------------------------------------------

try_new_numeric_strategies(X <= N, TYPE, Hs) :-       /*[[NewNum_1]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, U, TYPE, Hs),
    simplify(U <= N, true),
    !.

try_new_numeric_strategies(N <= X, TYPE, Hs) :-       /*[[NewNum_2]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, L, TYPE, Hs),
    simplify(L >= N, true),
    !.

try_new_numeric_strategies(X >= N, TYPE, Hs) :-       /*[[NewNum_3]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, L, TYPE, Hs),
    simplify(L >= N, true),
    !.

try_new_numeric_strategies(N >= X, TYPE, Hs) :-       /*[[NewNum_4]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, U, TYPE, Hs),
    simplify(U <= N, true),
    !.

try_new_numeric_strategies(X < N, TYPE, Hs) :-        /*[[NewNum_5]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, U, TYPE, Hs),
    simplify(U < N, true),
    !.

try_new_numeric_strategies(N < X, TYPE, Hs) :-        /*[[NewNum_6]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, L, TYPE, Hs),
    simplify(L > N, true),
    !.

try_new_numeric_strategies(X > N, TYPE, Hs) :-        /*[[NewNum_7]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, L, TYPE, Hs),
    simplify(L > N, true),
    !.

try_new_numeric_strategies(N > X, TYPE, Hs) :-        /*[[NewNum_8]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, U, TYPE, Hs),
    simplify(U < N, true),
    !.

% Numeric literal on either side of a non-equality (<>)
%------------------------------------------------------

try_new_numeric_strategies(X <> N, TYPE, Hs) :-       /*[[NewNum_9]]*/
    base_rational(N),
    (
        find_lower_numeric_limit_for(X, L, TYPE, Hs),   /* X >= L, and */
        simplify(L > N, true)            /* L > N, so X > N */
    ;
        find_upper_numeric_limit_for(X, U, TYPE, Hs),   /* X <= U, and */
        simplify(U < N, true)            /* U < N, so X < N */
    ),
    !.

try_new_numeric_strategies(N <> X, TYPE, Hs) :-       /*[[NewNum_10]]*/
    base_rational(N),
    (
        find_lower_numeric_limit_for(X, L, TYPE, Hs),   /* X >= L, and */
        simplify(L > N, true)            /* L > N, so X > N */
    ;
        find_upper_numeric_limit_for(X, U, TYPE, Hs),   /* X <= U, and */
        simplify(U < N, true)            /* U < N, so X < N */
    ),
    !.

% Extra inequalities rules with numeric literal sub-terms
%--------------------------------------------------------

% Integer term in upper-bound.

try_new_numeric_strategies(X + Y <= Z + N, TYPE, Hs) :-    /*[[NewNum_11]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, XU, TYPE, H1),     /* X <= XU */
    evaluate_rational_expression(N - XU, A),
    safe_deduce(Y <= Z + A, TYPE, H2),            /* Y <= Z + (N - XU) */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: X + Y <= Z + N */

try_new_numeric_strategies(X + Y <= N + Z, TYPE, Hs) :-    /*[[NewNum_12]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, XU, TYPE, H1),     /* X <= XU */
    evaluate_rational_expression(N - XU, A),
    safe_deduce(Y <= Z + A, TYPE, H2),            /* Y <= Z + (N - XU) */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: X + Y <= Z + N */

try_new_numeric_strategies(Y + X <= Z + N, TYPE, Hs) :-    /*[[NewNum_13]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, XU, TYPE, H1),     /* X <= XU */
    evaluate_rational_expression(N - XU, A),
    safe_deduce(Y <= Z + A, TYPE, H2),            /* Y <= Z + (N - XU) */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: X + Y <= Z + N */

try_new_numeric_strategies(Y + X <= N + Z, TYPE, Hs) :-    /*[[NewNum_14]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, XU, TYPE, H1),     /* X <= XU */
    evaluate_rational_expression(N - XU, A),
    safe_deduce(Y <= Z + A, TYPE, H2),            /* Y <= Z + (N - XU) */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: X + Y <= Z + N */

try_new_numeric_strategies(Z + N >= X + Y, TYPE, Hs) :-    /*[[NewNum_15]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, XU, TYPE, H1),     /* X <= XU */
    evaluate_rational_expression(N - XU, A),
    safe_deduce(Y <= Z + A, TYPE, H2),            /* Y <= Z + (N - XU) */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: X + Y <= Z + N */

try_new_numeric_strategies(N + Z >= X + Y, TYPE, Hs) :-    /*[[NewNum_16]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, XU, TYPE, H1),     /* X <= XU */
    evaluate_rational_expression(N - XU, A),
    safe_deduce(Y <= Z + A, TYPE, H2),            /* Y <= Z + (N - XU) */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: X + Y <= Z + N */

try_new_numeric_strategies(Z + N >= Y + X, TYPE, Hs) :-    /*[[NewNum_17]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, XU, TYPE, H1),     /* X <= XU */
    evaluate_rational_expression(N - XU, A),
    safe_deduce(Y <= Z + A, TYPE, H2),            /* Y <= Z + (N - XU) */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: X + Y <= Z + N */

try_new_numeric_strategies(N + Z >= Y + X, TYPE, Hs) :-    /*[[NewNum_18]]*/
    base_rational(N),
    find_upper_numeric_limit_for(X, XU, TYPE, H1),     /* X <= XU */
    evaluate_rational_expression(N - XU, A),
    safe_deduce(Y <= Z + A, TYPE, H2),            /* Y <= Z + (N - XU) */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: X + Y <= Z + N */

% Integer term in lower-bound.

try_new_numeric_strategies(Z + N <= X + Y, TYPE, Hs) :-    /*[[NewNum_19]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* XL <= X */
    evaluate_rational_expression(N - XL, A),
    safe_deduce(Z + A <= Y, TYPE, H2),            /* Z + (N - XL) <= Y */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: Z + N <= X + Y */

try_new_numeric_strategies(N + Z <= X + Y, TYPE, Hs) :-    /*[[NewNum_20]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* XL <= X */
    evaluate_rational_expression(N - XL, A),
    safe_deduce(Z + A <= Y, TYPE, H2),            /* Z + (N - XL) <= Y */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: Z + N <= X + Y */

try_new_numeric_strategies(Z + N <= Y + X, TYPE, Hs) :-    /*[[NewNum_21]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* XL <= X */
    evaluate_rational_expression(N - XL, A),
    safe_deduce(Z + A <= Y, TYPE, H2),            /* Z + (N - XL) <= Y */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: Z + N <= X + Y */

try_new_numeric_strategies(N + Z <= Y + X, TYPE, Hs) :-    /*[[NewNum_22]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* XL <= X */
    evaluate_rational_expression(N - XL, A),
    safe_deduce(Z + A <= Y, TYPE, H2),            /* Z + (N - XL) <= Y */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: Z + N <= X + Y */

try_new_numeric_strategies(X + Y >= Z + N, TYPE, Hs) :-    /*[[NewNum_23]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* XL <= X */
    evaluate_rational_expression(N - XL, A),
    safe_deduce(Z + A <= Y, TYPE, H2),            /* Z + (N - XL) <= Y */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: Z + N <= X + Y */

try_new_numeric_strategies(X + Y >= N + Z, TYPE, Hs) :-    /*[[NewNum_24]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* XL <= X */
    evaluate_rational_expression(N - XL, A),
    safe_deduce(Z + A <= Y, TYPE, H2),            /* Z + (N - XL) <= Y */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: Z + N <= X + Y */

try_new_numeric_strategies(Y + X >= Z + N, TYPE, Hs) :-    /*[[NewNum_25]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* XL <= X */
    evaluate_rational_expression(N - XL, A),
    safe_deduce(Z + A <= Y, TYPE, H2),            /* Z + (N - XL) <= Y */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: Z + N <= X + Y */

try_new_numeric_strategies(Y + X >= N + Z, TYPE, Hs) :-    /*[[NewNum_26]]*/
    base_rational(N),
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* XL <= X */
    evaluate_rational_expression(N - XL, A),
    safe_deduce(Z + A <= Y, TYPE, H2),            /* Z + (N - XL) <= Y */
    append(H1, H2, HL),
    sort(HL, Hs).                  /* So: Z + N <= X + Y */

% Eliminate common terms on either side of an inequality
%-------------------------------------------------------

% Addition.

try_new_numeric_strategies(X + A = Y + A, TYPE, Hs) :-     /*[[NewNum_27]]*/
    try_to_infer((=), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A = A + Y, TYPE, Hs) :-     /*[[NewNum_28]]*/
    try_to_infer((=), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X = Y + A, TYPE, Hs) :-     /*[[NewNum_29]]*/
    try_to_infer((=), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X = A + Y, TYPE, Hs) :-     /*[[NewNum_30]]*/
    try_to_infer((=), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A <> Y + A, TYPE, Hs) :-    /*[[NewNum_31]]*/
    try_to_infer((<>), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A <> A + Y, TYPE, Hs) :-    /*[[NewNum_32]]*/
    try_to_infer((<>), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X <> Y + A, TYPE, Hs) :-    /*[[NewNum_33]]*/
    try_to_infer((<>), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X <> A + Y, TYPE, Hs) :-    /*[[NewNum_34]]*/
    try_to_infer((<>), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A <= Y + A, TYPE, Hs) :-    /*[[NewNum_35]]*/
    try_to_infer((<=), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A <= A + Y, TYPE, Hs) :-    /*[[NewNum_36]]*/
    try_to_infer((<=), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X <= Y + A, TYPE, Hs) :-    /*[[NewNum_37]]*/
    try_to_infer((<=), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X <= A + Y, TYPE, Hs) :-    /*[[NewNum_38]]*/
    try_to_infer((<=), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A >= Y + A, TYPE, Hs) :-    /*[[NewNum_39]]*/
    try_to_infer((>=), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A >= A + Y, TYPE, Hs) :-    /*[[NewNum_40]]*/
    try_to_infer((>=), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X >= Y + A, TYPE, Hs) :-    /*[[NewNum_41]]*/
    try_to_infer((>=), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X >= A + Y, TYPE, Hs) :-    /*[[NewNum_42]]*/
    try_to_infer((>=), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A < Y + A, TYPE, Hs) :-     /*[[NewNum_43]]*/
    try_to_infer((<), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A < A + Y, TYPE, Hs) :-     /*[[NewNum_44]]*/
    try_to_infer((<), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X < Y + A, TYPE, Hs) :-     /*[[NewNum_45]]*/
    try_to_infer((<), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X < A + Y, TYPE, Hs) :-     /*[[NewNum_46]]*/
    try_to_infer((<), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A > Y + A, TYPE, Hs) :-     /*[[NewNum_47]]*/
    try_to_infer((>), X, Y, TYPE, Hs).

try_new_numeric_strategies(X + A > A + Y, TYPE, Hs) :-     /*[[NewNum_48]]*/
    try_to_infer((>), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X > Y + A, TYPE, Hs) :-     /*[[NewNum_49]]*/
    try_to_infer((>), X, Y, TYPE, Hs).

try_new_numeric_strategies(A + X > A + Y, TYPE, Hs) :-     /*[[NewNum_50]]*/
    try_to_infer((>), X, Y, TYPE, Hs).

% Subtraction

try_new_numeric_strategies(X - A = Y - A, TYPE, Hs) :-     /*[[NewNum_51]]*/
    try_to_infer((=), X, Y, TYPE, Hs).

try_new_numeric_strategies(A - X = A - Y, TYPE, Hs) :-     /*[[NewNum_52]]*/
    try_to_infer((=), Y, X, TYPE, Hs).      /* order reversal deliberate */

try_new_numeric_strategies(X - A <> Y - A, TYPE, Hs) :-    /*[[NewNum_53]]*/
    try_to_infer((<>), X, Y, TYPE, Hs).

try_new_numeric_strategies(A - X <> A - Y, TYPE, Hs) :-    /*[[NewNum_54]]*/
    try_to_infer((<>), Y, X, TYPE, Hs).     /* order reversal deliberate */

try_new_numeric_strategies(X - A <= Y - A, TYPE, Hs) :-    /*[[NewNum_55]]*/
    try_to_infer((<=), X, Y, TYPE, Hs).

try_new_numeric_strategies(A - X <= A - Y, TYPE, Hs) :-    /*[[NewNum_56]]*/
    try_to_infer((<=), Y, X, TYPE, Hs).     /* order reversal deliberate */

try_new_numeric_strategies(X - A >= Y - A, TYPE, Hs) :-    /*[[NewNum_57]]*/
    try_to_infer((>=), X, Y, TYPE, Hs).

try_new_numeric_strategies(A - X >= A - Y, TYPE, Hs) :-    /*[[NewNum_58]]*/
    try_to_infer((>=), Y, X, TYPE, Hs).     /* order reversal deliberate */

try_new_numeric_strategies(X - A < Y - A, TYPE, Hs) :-     /*[[NewNum_59]]*/
    try_to_infer((<), X, Y, TYPE, Hs).

try_new_numeric_strategies(A - X < A - Y, TYPE, Hs) :-     /*[[NewNum_60]]*/
    try_to_infer((<), Y, X, TYPE, Hs).      /* order reversal deliberate */

try_new_numeric_strategies(X - A > Y - A, TYPE, Hs) :-     /*[[NewNum_61]]*/
    try_to_infer((>), X, Y, TYPE, Hs).

try_new_numeric_strategies(A - X > A - Y, TYPE, Hs) :-     /*[[NewNum_62]]*/
    try_to_infer((>), Y, X, TYPE, Hs).      /* order reversal deliberate */

% Multiplication by a constant

try_new_numeric_strategies(X * N = Y * N, TYPE, Hs) :-     /*[[NewNum_63]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N<>0, true),
        !,
        try_to_infer((=), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(X * N = N * Y, TYPE, Hs) :-     /*[[NewNum_64]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N<>0, true),
        !,
        try_to_infer((=), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(N * X = Y * N, TYPE, Hs) :-     /*[[NewNum_65]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N<>0, true),
        !,
        try_to_infer((=), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(N * X = N * Y, TYPE, Hs) :-     /*[[NewNum_66]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N<>0, true),
        !,
        try_to_infer((=), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(X = N * X, TYPE, Hs) :-         /*[[NewNum_67]]*/
    base_rational(N),
    (
        simplify(N = 1, true),
        Hs = []
    ;
        try_to_infer((=), X, 0, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(N * X = X, TYPE, Hs) :-         /*[[NewNum_68]]*/
    base_rational(N),
    (
        simplify(N = 1, true),
        Hs = []
    ;
        try_to_infer((=), X, 0, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(X = X * N, TYPE, Hs) :-         /*[[NewNum_69]]*/
    base_rational(N),
    (
        simplify(N = 1, true),
        Hs = []
    ;
        try_to_infer((=), X, 0, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(X * N = X, TYPE, Hs) :-         /*[[NewNum_70]]*/
    base_rational(N),
    (
        simplify(N = 1, true),
        Hs = []
    ;
        try_to_infer((=), X, 0, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(X * N <> Y * N, TYPE, Hs) :-    /*[[NewNum_71]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N<>0, true),
        !,
        try_to_infer((<>), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(X * N <> N * Y, TYPE, Hs) :-    /*[[NewNum_72]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N<>0, true),
        !,
        try_to_infer((<>), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(N * X <> Y * N, TYPE, Hs) :-    /*[[NewNum_73]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N<>0, true),
        !,
        try_to_infer((<>), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(N * X <> N * Y, TYPE, Hs) :-    /*[[NewNum_74]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N<>0, true),
        !,
        try_to_infer((<>), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(X * N <= Y * N, TYPE, Hs) :-    /*[[NewNum_75]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((<=), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((<=), Y, X, TYPE, Hs)   /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(X * N <= N * Y, TYPE, Hs) :-    /*[[NewNum_76]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((<=), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((<=), Y, X, TYPE, Hs)   /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(N * X <= Y * N, TYPE, Hs) :-    /*[[NewNum_77]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((<=), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((<=), Y, X, TYPE, Hs)   /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(N * X <= N * Y, TYPE, Hs) :-    /*[[NewNum_78]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((<=), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((<=), Y, X, TYPE, Hs)   /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(X * N >= Y * N, TYPE, Hs) :-    /*[[NewNum_79]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((>=), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((>=), Y, X, TYPE, Hs)   /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(X * N >= N * Y, TYPE, Hs) :-    /*[[NewNum_80]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((>=), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((>=), Y, X, TYPE, Hs)   /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(N * X >= Y * N, TYPE, Hs) :-    /*[[NewNum_81]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((>=), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((>=), Y, X, TYPE, Hs)   /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(N * X >= N * Y, TYPE, Hs) :-    /*[[NewNum_82]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        Hs = []
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((>=), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((>=), Y, X, TYPE, Hs)   /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(X * N < Y * N, TYPE, Hs) :-     /*[[NewNum_83]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((<), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((<), Y, X, TYPE, Hs)    /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(X * N < N * Y, TYPE, Hs) :-     /*[[NewNum_84]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((<), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((<), Y, X, TYPE, Hs)    /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(N * X < Y * N, TYPE, Hs) :-     /*[[NewNum_85]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((<), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((<), Y, X, TYPE, Hs)    /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(N * X < N * Y, TYPE, Hs) :-     /*[[NewNum_86]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((<), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((<), Y, X, TYPE, Hs)    /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(X * N > Y * N, TYPE, Hs) :-     /*[[NewNum_87]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((>), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((>), Y, X, TYPE, Hs)    /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(X * N > N * Y, TYPE, Hs) :-     /*[[NewNum_88]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((>), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((>), Y, X, TYPE, Hs)    /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(N * X > Y * N, TYPE, Hs) :-     /*[[NewNum_89]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((>), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((>), Y, X, TYPE, Hs)    /* order reversal deliberate */
    ),
    !.

try_new_numeric_strategies(N * X > N * Y, TYPE, Hs) :-     /*[[NewNum_90]]*/
    base_rational(N),
    !,
    (
        N = 0,
        !,
        fail
    ;
        simplify(N > 0, true),
        !,
        try_to_infer((>), X, Y, TYPE, Hs)
    ;
        simplify(N < 0, true),
        !,
        try_to_infer((>), Y, X, TYPE, Hs)    /* order reversal deliberate */
    ),
    !.

% Multiplication.

try_new_numeric_strategies(X * A = Y * A, TYPE, Hs) :-     /*[[NewNum_91]]*/
    (
        safe_deduce(A = 0, TYPE, Hs)
    ;
        try_to_infer((=), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(X * A = A * Y, TYPE, Hs) :-     /*[[NewNum_92]]*/
    (
        safe_deduce(A = 0, TYPE, Hs)
    ;
        try_to_infer((=), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(A * X = Y * A, TYPE, Hs) :-     /*[[NewNum_93]]*/
    (
        safe_deduce(A = 0, TYPE, Hs)
    ;
        try_to_infer((=), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(A * X = A * Y, TYPE, Hs) :-     /*[[NewNum_94]]*/
    (
        safe_deduce(A = 0, TYPE, Hs)
    ;
        try_to_infer((=), X, Y, TYPE, Hs)
    ),
    !.

try_new_numeric_strategies(X * A <> Y * A, TYPE, Hs) :-    /*[[NewNum_95]]*/
    (
        safe_deduce(A <> 0, TYPE, H1),
        !,
        try_to_infer((<>), X, Y, TYPE, H2)
    ;
        safe_deduce(A = 0, TYPE, Hs),
        !,
        fail
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(X * A <> A * Y, TYPE, Hs) :-    /*[[NewNum_96]]*/
    (
        safe_deduce(A <> 0, TYPE, H1),
        !,
        try_to_infer((<>), X, Y, TYPE, H2)
    ;
        safe_deduce(A = 0, TYPE, Hs),
        !,
        fail
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(A * X <> Y * A, TYPE, Hs) :-    /*[[NewNum_97]]*/
    (
        safe_deduce(A <> 0, TYPE, H1),
        !,
        try_to_infer((<>), X, Y, TYPE, H2)
    ;
        safe_deduce(A = 0, TYPE, Hs),
        !,
        fail
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(A * X <> A * Y, TYPE, Hs) :-    /*[[NewNum_98]]*/
    (
        safe_deduce(A <> 0, TYPE, H1),
        !,
        try_to_infer((<>), X, Y, TYPE, H2)
    ;
        safe_deduce(A = 0, TYPE, Hs),
        !,
        fail
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(X * A <= Y * A, TYPE, Hs) :-    /*[[NewNum_99]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        H1 = []
    ;
        safe_deduce(A >= 0, TYPE, H1),
        !,
        try_to_infer((<=), X, Y, TYPE, H2)
    ;
        safe_deduce(A <= 0, TYPE, H1),
        !,
        try_to_infer((<=), Y, X, TYPE, H2)   /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(X * A <= A * Y, TYPE, Hs) :-    /*[[NewNum_100]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        H1 = []
    ;
        safe_deduce(A >= 0, TYPE, H1),
        !,
        try_to_infer((<=), X, Y, TYPE, H2)
    ;
        safe_deduce(A <= 0, TYPE, H1),
        !,
        try_to_infer((<=), Y, X, TYPE, H2)   /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(A * X <= Y * A, TYPE, Hs) :-    /*[[NewNum_101]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        H1 = []
    ;
        safe_deduce(A >= 0, TYPE, H1),
        !,
        try_to_infer((<=), X, Y, TYPE, H2)
    ;
        safe_deduce(A <= 0, TYPE, H1),
        !,
        try_to_infer((<=), Y, X, TYPE, H2)   /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(A * X <= A * Y, TYPE, Hs) :-    /*[[NewNum_102]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        H1 = []
    ;
        safe_deduce(A >= 0, TYPE, H1),
        !,
        try_to_infer((<=), X, Y, TYPE, H2)
    ;
        safe_deduce(A <= 0, TYPE, H1),
        !,
        try_to_infer((<=), Y, X, TYPE, H2)   /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(X * A >= Y * A, TYPE, Hs) :-    /*[[NewNum_103]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        H1 = []
    ;
        safe_deduce(A >= 0, TYPE, H1),
        !,
        try_to_infer((>=), X, Y, TYPE, H2)
    ;
        safe_deduce(A <= 0, TYPE, H1),
        !,
        try_to_infer((>=), Y, X, TYPE, H2)   /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(X * A >= A * Y, TYPE, Hs) :-    /*[[NewNum_104]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        H1 = []
    ;
        safe_deduce(A >= 0, TYPE, H1),
        !,
        try_to_infer((>=), X, Y, TYPE, H2)
    ;
        safe_deduce(A <= 0, TYPE, H1),
        !,
        try_to_infer((>=), Y, X, TYPE, H2)   /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(A * X >= Y * A, TYPE, Hs) :-    /*[[NewNum_105]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        H1 = []
    ;
        safe_deduce(A >= 0, TYPE, H1),
        !,
        try_to_infer((>=), X, Y, TYPE, H2)
    ;
        safe_deduce(A <= 0, TYPE, H1),
        !,
        try_to_infer((>=), Y, X, TYPE, H2)   /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(A * X >= A * Y, TYPE, Hs) :-    /*[[NewNum_106]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        H1 = []
    ;
        safe_deduce(A >= 0, TYPE, H1),
        !,
        try_to_infer((>=), X, Y, TYPE, H2)
    ;
        safe_deduce(A <= 0, TYPE, H1),
        !,
        try_to_infer((>=), Y, X, TYPE, H2)   /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(X * A < Y * A, TYPE, Hs) :-     /*[[NewNum_107]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        fail
    ;
        safe_deduce(A > 0, TYPE, H1),
        !,
        try_to_infer((<), X, Y, TYPE, H2)
    ;
        safe_deduce(A < 0, TYPE, H1),
        !,
        try_to_infer((<), Y, X, TYPE, H2)    /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(X * A < A * Y, TYPE, Hs) :-     /*[[NewNum_108]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        fail
    ;
        safe_deduce(A > 0, TYPE, H1),
        !,
        try_to_infer((<), X, Y, TYPE, H2)
    ;
        safe_deduce(A < 0, TYPE, H1),
        !,
        try_to_infer((<), Y, X, TYPE, H2)    /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(A * X < Y * A, TYPE, Hs) :-     /*[[NewNum_109]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        fail
    ;
        safe_deduce(A > 0, TYPE, H1),
        !,
        try_to_infer((<), X, Y, TYPE, H2)
    ;
        safe_deduce(A < 0, TYPE, H1),
        !,
        try_to_infer((<), Y, X, TYPE, H2)    /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(A * X < A * Y, TYPE, Hs) :-     /*[[NewNum_110]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        fail
    ;
        safe_deduce(A > 0, TYPE, H1),
        !,
        try_to_infer((<), X, Y, TYPE, H2)
    ;
        safe_deduce(A < 0, TYPE, H1),
        !,
        try_to_infer((<), Y, X, TYPE, H2)    /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(X * A > Y * A, TYPE, Hs) :-     /*[[NewNum_111]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        fail
    ;
        safe_deduce(A > 0, TYPE, H1),
        !,
        try_to_infer((>), X, Y, TYPE, H2)
    ;
        safe_deduce(A < 0, TYPE, H1),
        !,
        try_to_infer((>), Y, X, TYPE, H2)    /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(X * A > A * Y, TYPE, Hs) :-     /*[[NewNum_112]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        fail
    ;
        safe_deduce(A > 0, TYPE, H1),
        !,
        try_to_infer((>), X, Y, TYPE, H2)
    ;
        safe_deduce(A < 0, TYPE, H1),
        !,
        try_to_infer((>), Y, X, TYPE, H2)    /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(A * X > Y * A, TYPE, Hs) :-     /*[[NewNum_113]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        fail
    ;
        safe_deduce(A > 0, TYPE, H1),
        !,
        try_to_infer((>), X, Y, TYPE, H2)
    ;
        safe_deduce(A < 0, TYPE, H1),
        !,
        try_to_infer((>), Y, X, TYPE, H2)    /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.

try_new_numeric_strategies(A * X > A * Y, TYPE, Hs) :-     /*[[NewNum_114]]*/
    (
        safe_deduce(A = 0, TYPE, H2),
        !,
        fail
    ;
        safe_deduce(A > 0, TYPE, H1),
        !,
        try_to_infer((>), X, Y, TYPE, H2)
    ;
        safe_deduce(A < 0, TYPE, H1),
        !,
        try_to_infer((>), Y, X, TYPE, H2)    /* order reversal deliberate */
    ),
    append(H1, H2, HL),
    sort(HL, Hs),
    !.


% Integer division.

try_new_numeric_strategies(X div N = Y div N, integer, Hs) :-   /*[[NewNum_124]]*/
    int(N),
    simplify(N=0, false),
    !,
    safe_deduce(X = Y, integer, Hs).

try_new_numeric_strategies(X div N <= Y div N, integer, Hs) :-  /*[[NewNum_125]]*/
    int(N),
    simplify(N>0, true),
    !,
    safe_deduce(X <= Y, integer, Hs).

try_new_numeric_strategies(X div N <= Y div N, integer, Hs) :-  /*[[NewNum_126]]*/
    int(N),
    simplify(N<0, true),
    !,
    safe_deduce(X >= Y, integer, Hs).

try_new_numeric_strategies(X div N >= Y div N, integer, Hs) :-  /*[[NewNum_127]]*/
    int(N),
    simplify(N>0, true),
    !,
    safe_deduce(X >= Y, integer, Hs).

try_new_numeric_strategies(X div N >= Y div N, integer, Hs) :-  /*[[NewNum_128]]*/
    int(N),
    simplify(N<0, true),
    !,
    safe_deduce(X <= Y, integer, Hs).


% Extra equality rules for integers
%----------------------------------

try_new_numeric_strategies(X = N, integer, Hs) :-          /*[[NewNum_115]]*/
    int(N),
    !,
    (
        strict_deduce(X >= N, integer, H1)
    ;
        M1 iss N-1,
        strict_deduce(X > M1, integer, H1)
    ),
    (
        strict_deduce(X <= N, integer, H2)
    ;
        M2 iss N+1,
        strict_deduce(X < M2, integer, H2)
    ),
    !,
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_numeric_strategies(N = X, integer, Hs) :-          /*[[NewNum_116]]*/
    int(N),
    !,
    (
        strict_deduce(X >= N, integer, H1)
    ;
        M1 iss N-1,
        strict_deduce(X > M1, integer, H1)
    ),
    (
        strict_deduce(X <= N, integer, H2)
    ;
        M2 iss N+1,
        strict_deduce(X < M2, integer, H2)
    ),
    !,
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_numeric_strategies(X+1 < Y, integer, Hs) :-        /*[[NewNum_118]*/
    (
        strict_deduce(X < Y, integer, H1)
    ;
        strict_deduce(X+1 <= Y, integer, H1)
    ),
    (
        strict_deduce(X+1 <> Y, integer, H2)
    ;
        strict_deduce(X <> Y-1, integer, H2)
    ),
    !,
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_numeric_strategies(X < Y-1, integer, Hs) :-        /*[[NewNum_119]*/
    (
        strict_deduce(X < Y, integer, H1)
    ;
        strict_deduce(X <= Y-1, integer, H1)
    ),
    (
        strict_deduce(X <> Y-1, integer, H2)
    ;
        strict_deduce(X+1 <> Y, integer, H2)
    ),
    !,
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_numeric_strategies(Y > X+1, integer, Hs) :-        /*[[NewNum_120]*/
    (
        strict_deduce(X < Y, integer, H1)
    ;
        strict_deduce(X+1 <= Y, integer, H1)
    ),
    (
        strict_deduce(X+1 <> Y, integer, H2)
    ;
        strict_deduce(X <> Y-1, integer, H2)
    ),
    !,
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_numeric_strategies(Y-1 > X, integer, Hs) :-        /*[[NewNum_121]*/
    (
        strict_deduce(X < Y, integer, H1)
    ;
        strict_deduce(X <= Y-1, integer, H1)
    ),
    (
        strict_deduce(X <> Y-1, integer, H2)
    ;
        strict_deduce(X+1 <> Y, integer, H2)
    ),
    !,
    append(H1, H2, HL),
    sort(HL, Hs).

% Extra inequality rules for common quantifier bound proofs
%----------------------------------------------------------

try_new_numeric_strategies(X <= Y, integer, Hs) :-         /*[[NewNum_117]]*/
    \+ int(X),
    \+ int(Y),
    (
        strict_deduce(X <= Y+1, integer, H1)
    ;
        strict_deduce(X-1 <= Y, integer, H1)
    ;
        strict_deduce(X < Y+2, integer, H1)
    ;
        strict_deduce(X-2 < Y, integer, H1)
    ),
    !,
    (
        strict_deduce(X <> Y+1, integer, H2)
    ;
        strict_deduce(X-1 <> Y, integer, H2)
    ),
    !,
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_numeric_strategies(X <= Y, integer, Hs) :-         /*[[NewNum_122]]*/
    \+ int(X),
    int(Y),
    simplify(Y+1, Yplus1),
    (
        strict_deduce(X <= Yplus1, integer, H1)
    ;
        simplify(Y+2, Yplus2),
        strict_deduce(X < Yplus2, integer, H1)
    ),
    !,
    strict_deduce(X <> Yplus1, integer, H2),
    !,
    append(H1, H2, HL),
    sort(HL, Hs).

try_new_numeric_strategies(X <= Y, integer, Hs) :-         /*[[NewNum_123]]*/
    int(X),
    \+ int(Y),
    simplify(X-1, Xminus1),
    (
        strict_deduce(Xminus1 <= Y, integer, H1)
    ;
        simplify(X-2, Xminus2),
        strict_deduce(Xminus2 < Y, integer, H1)
    ),
    !,
    strict_deduce(Xminus1 <> Y, integer, H2),
    !,
    append(H1, H2, HL),
    sort(HL, Hs).

%===============================================================================
% find_upper_numeric_limit_for(+EXPRESSION, -NUM, +TYPE, -HYPS_USED).
%-------------------------------------------------------------------------------
% Determine an upper numeric limit for EXPRESSION of provided type TYPE as
% NUM, reporting hypotheses used as HYPS_USED.
%===============================================================================

find_upper_numeric_limit_for(X, U, Type, Hs) :-
    known_upper_numeric_limit(X, U, Type, Hs),      /* first, look to see if we've already found it */
    !.

find_upper_numeric_limit_for(X, U, Type, Hs) :-    /* if not, and it's an atom, calculate it */
    atom(X),
    !,
    calculate_known_upper_limit_for(X, U, Type, Hs).

find_upper_numeric_limit_for(N, N, integer, []) :-      /* if not, and it's an integer, look no further */
    int(N),                /*formerly [[UppLim_1]]*/
    !.

find_upper_numeric_limit_for(N, N, real, []) :-    /* if not, and it's a numeric literal, look no further */
    base_rational(N),                /*formerly [[UppLim_1]]*/
    !.

find_upper_numeric_limit_for(N, U, integer, []) :-      /* if it's a numeric literal that isn't an integer, */
    base_rational(N),                /* e.g. 1/2, find the largest integer below it. */
    !,
    find_largest_integer_literal_below(N, U).

/* otherwise, (1) apply strategies, (2) use hypotheses, and (3) combine the results to get the least upper bound */
find_upper_numeric_limit_for(X, U, Type, Hs) :-
    do_find_upper_numeric_limit_for(X, U1, Type, H1s),
    assertz(candidate_upper(X, U1, Type, H1s)),
    calculate_known_upper_limit_for(X, U, Type, Hs),
    !.

%===============================================================================
% do_find_upper_numeric_limit_for(+EXPRESSION, -NUM, +TYPE, -HYPS_USED).
%-------------------------------------------------------------------------------
% Determine an upper numeric limit for EXPRESSION of provided type TYPE as
% NUM, reporting hypotheses used as HYPS_USED.
%===============================================================================

% EXPRESSION is a numeric literal
%--------------------------------
% This case has already been considered by the parent clause above.

% EXPRESSION is -X (unary minus)
%-------------------------------

do_find_upper_numeric_limit_for(-X, U, TYPE, Hs) :-        /*[[UppLim_2]]*/
    find_lower_numeric_limit_for(X, L, TYPE, Hs),      /* X >= L, so -X <= -L */
    evaluate_rational_expression(-L, U).          /* [Works for int and real] */

% EXPRESSION is X+Y (addition)
%-----------------------------

do_find_upper_numeric_limit_for(X+N, U, integer, Hs) :-    /*[[UppLim_3(int)]]*/
    int(N),
    !,
    find_upper_numeric_limit_for(X, XU, integer, Hs),
    U iss XU+N.

do_find_upper_numeric_limit_for(X+N, U, real, Hs) :-       /*[[UppLim_3(real)]]*/
    base_rational(N),
    !,
    find_upper_numeric_limit_for(X, XU, real, Hs),
    evaluate_rational_expression(XU+N, U).

do_find_upper_numeric_limit_for(N+X, U, integer, Hs) :-    /*[[UppLim_4(int)]]*/
    int(N),
    !,
    find_upper_numeric_limit_for(X, XU, integer, Hs),
    U iss XU+N.

do_find_upper_numeric_limit_for(N+X, U, real, Hs) :-       /*[[UppLim_4(real)]]*/
    base_rational(N),
    !,
    find_upper_numeric_limit_for(X, XU, real, Hs),
    evaluate_rational_expression(XU+N, U).

do_find_upper_numeric_limit_for(X+Y, U, TYPE, Hs) :-       /*[[UppLim_5]]*/
    find_upper_numeric_limit_for(X, XU, TYPE, H1),     /* X <= XU, and */
    find_upper_numeric_limit_for(Y, YU, TYPE, H2),     /* Y <= YU, so  */
    evaluate_rational_expression(XU+YU, U),       /* X+Y <= XU+YU */
    append(H1, H2, HL),                 /* [Works for int and real] */
    sort(HL, Hs).

% EXPRESSION is X - X div Y [special case]
%-----------------------------------------

do_find_upper_numeric_limit_for(X - X div N, U, integer, Hs) :- /*[[UppLim_6]]*/
    int(N),
    simplify(N <> 0, true),
    !,
    find_upper_numeric_limit_for(X, XU, integer, Hs),
    U iss XU - XU div N.

do_find_upper_numeric_limit_for(X - X div Y, U, integer, Hs) :- /*[[UppLim_7]]*/
    (
        find_lower_numeric_limit_for(Y, YL, integer, H1),
        simplify(YL > 0, true)
    ;
        safe_deduce(Y > 0, integer, H1)
    ),
    (
        find_lower_numeric_limit_for(X, XL, integer, H2),
        simplify(XL >= 0, true)
    ;
        safe_deduce(X >= 0, integer, H2)
    ),
    find_upper_numeric_limit_for(X, XU, integer, H3),
    find_upper_numeric_limit_for(Y, YU, integer, H4),       /* use biggest Y to get smallest X div Y */
    U iss XU - XU div YU,
    append(H3, H4, H3to4),
    append(H2, H3to4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% EXPRESSION is Y - (X - X div Y * Y) [special case]
%---------------------------------------------------

do_find_upper_numeric_limit_for(Y - (X - X div Y * Y), U, integer, Hs) :-       /*[[UppLim_28a]]*/
    (                               /* X +ve, Y +ve case only. */
        find_lower_numeric_limit_for(X, XL, integer, H1),          /* In this case, the upper limit */
        simplify(XL >= 0, true)                     /* is YU */
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        safe_deduce(Y >= 1, integer, H2)
    ;
        safe_deduce(Y > 0, integer, H2)
    ),
    find_upper_numeric_limit_for(Y, U, integer, H3),
    simplify(U > 0, true),     /* sanity check */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(Y - (X - Y * (X div Y)), U, integer, Hs) :-     /*[[UppLim_28b]]*/
    (
        find_lower_numeric_limit_for(X, XL, integer, H1),
        simplify(XL >= 0, true)
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        safe_deduce(Y >= 1, integer, H2)
    ;
        safe_deduce(Y > 0, integer, H2)
    ),
    find_upper_numeric_limit_for(Y, U, integer, H3),
    simplify(U > 0, true),     /* sanity check */
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% EXPRESSION is X - X div Y * Y [special case]
%---------------------------------------------

do_find_upper_numeric_limit_for(X - X div Y * Y, U, integer, Hs) :-     /*[[UppLim_29a]]*/
    (                            /* X +ve, Y +ve case. */
        find_lower_numeric_limit_for(X, XL, integer, H1),       /* In this case, the upper limit */
        simplify(XL >= 0, true)                  /* is YU-1 */
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        safe_deduce(Y >= 1, integer, H2)
    ;
        safe_deduce(Y > 0, integer, H2)
    ),
    find_upper_numeric_limit_for(Y, YU, integer, H3),
    simplify(YU > 0, true),    /* sanity check */
    U iss YU - 1,
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(X - Y * (X div Y), U, integer, Hs) :-   /*[[UppLim_29b]]*/
    (
        find_lower_numeric_limit_for(X, XL, integer, H1),
        simplify(XL >= 0, true)
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        safe_deduce(Y >= 1, integer, H2)
    ;
        safe_deduce(Y > 0, integer, H2)
    ),
    find_upper_numeric_limit_for(Y, YU, integer, H3),
    simplify(YU > 0, true),    /* sanity check */
    U iss YU - 1,
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(X - X div Y * Y, U, integer, Hs) :-     /*[[UppLim_30a]]*/
    (                            /* X +ve, Y -ve case. */
        find_lower_numeric_limit_for(X, XL, integer, H1),       /* In this case, the upper limit */
        simplify(XL >= 0, true)                  /* is -YL-1, i.e. -(YL+1). */
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        find_upper_numeric_limit_for(Y, YU, integer, H2),
        simplify(YU < 0, true)
    ;
        safe_deduce(Y <= (- 1), integer, H2)
    ;
        safe_deduce(Y < 0, integer, H2)
    ),
    find_lower_numeric_limit_for(Y, YL, integer, H3),
    simplify(YL < 0, true),    /* sanity check */
    U iss -(YL + 1),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(X - Y* (X div Y), U, integer, Hs) :-    /*[[UppLim_30b]]*/
    (
        find_lower_numeric_limit_for(X, XL, integer, H1),
        simplify(XL >= 0, true)
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        find_upper_numeric_limit_for(Y, YU, integer, H2),
        simplify(YU < 0, true)
    ;
        safe_deduce(Y <= (- 1), integer, H2)
    ;
        safe_deduce(Y < 0, integer, H2)
    ),
    find_lower_numeric_limit_for(Y, YL, integer, H3),
    simplify(YL < 0, true),    /* sanity check */
    U iss -(YL + 1),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(X - X div Y * Y, 0, integer, Hs) :-     /*[[UppLim_31a]]*/
    (                            /* X +ve case (Y's sign irrelevant). */
        find_upper_numeric_limit_for(X, XU, integer, H1),       /* In this case, the upper limit */
        simplify(XU < 0, true)                   /* is zero, regardless of Y. */
    ;
        safe_deduce(X < 0, integer, H1)
    ),
    (
        safe_deduce(Y <> 0, integer, H2)
    ;
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        find_upper_numeric_limit_for(Y, YU, integer, H2),
        simplify(YU < 0, true)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(X - Y * (X div Y), 0, integer, Hs) :-   /*[[UppLim_31b]]*/
    (
        find_upper_numeric_limit_for(X, XU, integer, H1),
        simplify(XU < 0, true)
    ;
        safe_deduce(X < 0, integer, H1)
    ),
    (
        safe_deduce(Y <> 0, integer, H2)
    ;
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        find_upper_numeric_limit_for(Y, YU, integer, H2),
        simplify(YU < 0, true)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

% EXPRESSION is X-Y (subtraction)
%--------------------------------

do_find_upper_numeric_limit_for(X-N, U, integer, Hs) :-    /*[[UppLim_8(int)]]*/
    int(N),
    !,
    find_upper_numeric_limit_for(X, XU, integer, Hs),
    U iss XU-N.

do_find_upper_numeric_limit_for(X-N, U, real, Hs) :-       /*[[UppLim_8(real)]]*/
    base_rational(N),
    !,
    find_upper_numeric_limit_for(X, XU, real, Hs),
    evaluate_rational_expression(XU-N, U).

do_find_upper_numeric_limit_for(N-Y, U, integer, Hs) :-    /*[[UppLim_9(int)]]*/
    int(N),
    !,
    find_lower_numeric_limit_for(Y, YL, integer, Hs),       /* Y>=YL, so -Y <= -YL, so N-Y<=N-YL */
    U iss N-YL.

do_find_upper_numeric_limit_for(N-Y, U, real, Hs) :-       /*[[UppLim_9(real)]]*/
    base_rational(N),
    !,
    find_lower_numeric_limit_for(Y, YL, real, Hs),     /* Y>=YL, so -Y <= -YL, so N-Y<=N-YL */
    evaluate_rational_expression(N-YL, U).

do_find_upper_numeric_limit_for(X-Y, U, TYPE, Hs) :-       /*[[UppLim_10]]*/
    find_upper_numeric_limit_for(X, XU, TYPE, H1),     /* X <= XU, and */
    find_lower_numeric_limit_for(Y, YL, TYPE, H2),     /* Y >= YL, i.e. -Y <= -YL, so */
    evaluate_rational_expression(XU-YL, U),       /* X-Y <= XU-YL */
    append(H1, H2, HL),                 /* [Works for int and real] */
    sort(HL, Hs).

% EXPRESSION is X*Y (multiplication)
%-----------------------------------

do_find_upper_numeric_limit_for(X*N, U, integer, Hs) :-    /*[[UppLim_11(int)]]*/
    int(N),
    !,
    (
        simplify(N>0, true),
        !,
        find_upper_numeric_limit_for(X, XU, integer, Hs),
        U iss XU*N
    ;
        N = 0,
        !,
        U = 0,
        Hs = []
    ;
        simplify(N<0, true),
        !,
        find_lower_numeric_limit_for(X, XL, integer, Hs),
        U iss XL*N
    ).

do_find_upper_numeric_limit_for(X*N, U, real, Hs) :-       /*[[UppLim_11(real)]]*/
    base_rational(N),
    !,
    (
        simplify(N>0, true),
        !,
        find_upper_numeric_limit_for(X, XU, real, Hs),
        evaluate_rational_expression(XU*N, U)
    ;
        N = 0,
        !,
        U = 0,
        Hs = []
    ;
        simplify(N<0, true),
        !,
        find_lower_numeric_limit_for(X, XL, real, Hs),
        evaluate_rational_expression(XL*N, U)
    ).

do_find_upper_numeric_limit_for(N*X, U, integer, Hs) :-    /*[[UppLim_12(int)]]*/
    int(N),
    !,
    (
        simplify(N>0, true),
        !,
        find_upper_numeric_limit_for(X, XU, integer, Hs),
        U iss XU*N
    ;
        N = 0,
        !,
        U = 0,
        Hs = []
    ;
        simplify(N<0, true),
        !,
        find_lower_numeric_limit_for(X, XL, integer, Hs),
        U iss XL*N
    ).

do_find_upper_numeric_limit_for(N*X, U, real, Hs) :-       /*[[UppLim_12(real)]]*/
    base_rational(N),
    !,
    (
        simplify(N>0, true),
        !,
        find_upper_numeric_limit_for(X, XU, real, Hs),
        evaluate_rational_expression(XU*N, U)
    ;
        N = 0,
        !,
        U = 0,
        Hs = []
    ;
        simplify(N<0, true),
        !,
        find_lower_numeric_limit_for(X, XL, real, Hs),
        evaluate_rational_expression(XL*N, U)
    ).

do_find_upper_numeric_limit_for(X*X, U, TYPE, Hs) :-       /*[[UppLim_28]] -- special case */
    find_lower_numeric_limit_for(X, XL, TYPE, Hl),     /* [Works for both int and real] */
    simplify(XL>=0, true),
    find_upper_numeric_limit_for(X, XU, TYPE, Hu),
    base_rational(XU),
    simplify(XU>=XL, true),
    evaluate_rational_expression(XU*XU, U),
    append(Hl, Hu, H),
    sort(H, Hs).

do_find_upper_numeric_limit_for(X*Y, U, TYPE, Hs) :-       /*[[UppLim_13]]*/
    find_lower_numeric_limit_for(X, XL, TYPE, Hxl),    /* find X in XL..XU, Y in YL..YU, */
    find_upper_numeric_limit_for(X, XU, TYPE, Hxu),    /* calculate the four products    */
    find_lower_numeric_limit_for(Y, YL, TYPE, Hyl),    /* XL*YL, XL*YU, XU*YL and XU*YU  */
    find_upper_numeric_limit_for(Y, YU, TYPE, Hyu),    /* and determine the limits       */
    calc_product_bounds(XL, XU, YL, YU, TYPE, Hxl, Hxu, Hyl, Hyu, U, Hs, _, _).

% EXPRESSION is X div Y (integer division)
%-----------------------------------------

do_find_upper_numeric_limit_for(X div N, U, integer, Hs) :-     /*[[UppLim_14]]*/
    int(N),
    !,
    (
        simplify(N>0, true),
        find_upper_numeric_limit_for(X, XU, integer, Hs),
        U iss XU div N
    ;
        simplify(N<0, true),
        find_lower_numeric_limit_for(X, XL, integer, Hs),
        U iss XL div N
    ;
       search_for_upper_numeric_limit(X div N, U, integer, Hs)
    ).

do_find_upper_numeric_limit_for(0 div Y, 0, integer, Hs) :-     /*[[UppLim_15]]*/
    !,
    safe_deduce(Y <> 0, integer, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(N div Y, U, integer, Hs) :-     /*[[UppLim_16]]*/
    int(N),
    simplify(N>=0, true),
    !,
    find_lower_numeric_limit_for(Y, YL, integer, H1),
    (
        simplify(YL > 0, true),      /* so Y must definitely be +ve */
        H2 = []
    ;
        (             /* otherwise, check it's -ve */
            safe_deduce(Y < 0, integer, H2)
        ;
            safe_deduce(Y <= - 1, integer, H2)
        ),
        simplify(YL < 0, true)
    ),
    !,
    U iss N div YL,
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(N div Y, U, integer, Hs) :-     /*[[UppLim_17]]*/
    int(N),
    simplify(N<0, true),
    !,
    find_upper_numeric_limit_for(Y, YU, integer, H1),
    (
        simplify(YU < 0, true),      /* so Y must definitely be -ve */
        H2 = []
    ;
        (
            safe_deduce(Y > 0, integer, H2)
        ;
            safe_deduce(Y >= 1, integer, H2)
        ),
        simplify(YU > 0, true)
    ),
    !,
    U iss N div YU,
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(X div Y, U, integer, Hs) :-     /*[[UppLim_18]]*/
    find_lower_numeric_limit_for(Y, YL, integer, H1),       /* For integer division, allow    */
    simplify(YL>0, true),               /* only +ve or -ve divisors, i.e. */
    find_lower_numeric_limit_for(X, XL, integer, H2),       /* ignore cases where Y straddles */
    simplify(XL>=0, true),              /* and insist X +ve          */
    find_upper_numeric_limit_for(X, XU, integer, H3),
    simplify(XU>=0, true),              /* (sanity check) */
    U iss XU div YL,
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(X div Y, U, integer, Hs) :-     /*[[UppLim_19]]*/
    (
        find_upper_numeric_limit_for(Y, YU, integer, H1),
        simplify(YU < 0, true)
    ;
        safe_deduce(Y < 0, integer, H1)
    ;
        safe_deduce(Y <= - 1, integer, H1)
    ),
    find_lower_numeric_limit_for(Y, YL, integer, H2),
    simplify(YL<0, true),
    find_lower_numeric_limit_for(X, XL, integer, H3),
    simplify(XL>=0, true),
    U iss XL div YL,
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% EXPRESSION is X mod Y (integer modulus)
%----------------------------------------

do_find_upper_numeric_limit_for(X mod N, U, integer, Hs) :-     /*[[UppLim_20]]*/
    get_provenance_framework(spark),
    int(N),
    (
        int(X),
        N \= 0,
        !,
        Hs = [],
        U iss X mod N
    ;
        simplify(N>0, true),
        !,
        (
            (
       find_lower_numeric_limit_for(X, XL, integer, H1),
       simplify(XL>=0, true)
            ;
       safe_deduce(X >= 0, integer, H1)
            ),
            find_upper_numeric_limit_for(X, XU, integer, H2),
            XU < N-1,    /* 0 <= X <= XU <= N-1, so X mod N <= XU too */
            !,
            append(H1, H2, HL),
            sort(HL, Hs),
            U = XU
        ;
            Hs = [],
            U iss N-1       /* X mod N <= N-1, for +ve N */
        )
    ;
        simplify(N<0, true),
        !,
        Hs = [],
        U = 0         /* X mod N <= 0, for -ve N */
    ),
    !.

do_find_upper_numeric_limit_for(_ mod Y, U, integer, Hs) :-     /*[[UppLim_21]]*/
    get_provenance_framework(spark),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H1),
        simplify(YL > 0, true)          /* so Y must be +ve */
    ;
        safe_deduce(Y > 0, integer, H1)
    ;
        safe_deduce(Y >= 1, integer, H1)
    ),
    !,
    find_upper_numeric_limit_for(Y, YU, integer, H2),
    U iss YU - 1,
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(_ mod Y, 0, integer, Hs) :-     /*[[UppLim_22]]*/
    get_provenance_framework(spark),
    (
        find_upper_numeric_limit_for(Y, YU, integer, Hs),
        simplify(YU < 0, true)          /* so Y must be -ve */
    ;
        safe_deduce(Y < 0, integer, Hs)
    ;
        safe_deduce(Y <= - 1, integer, Hs)
    ),
    !.

% EXPRESSION is abs(X) (absolute value function)
%-----------------------------------------------

do_find_upper_numeric_limit_for(abs(N), U, integer, []) :-      /*[[UppLim_23(int)]]*/
    int(N),
    !,
    (
        simplify(N>=0, true),
        !,
        U = N
    ;
        simplify(N<0, true),
        !,
        U iss -N
    ),
    !.

do_find_upper_numeric_limit_for(abs(N), U, real, []) :-    /*[[UppLim_23(real)]]*/
    base_rational(N),
    !,
    (
        simplify(N>=0, true),
        !,
        U = N
    ;
        simplify(N<0, true),
        !,
        evaluate_rational_expression(-N, U)
    ),
    !.

do_find_upper_numeric_limit_for(abs(X), U, TYPE, Hs) :-    /*[[UppLim_24]]*/
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* [Works for int and real] */
    find_upper_numeric_limit_for(X, XU, TYPE, H2),
    (
        simplify(XL >= 0, true),
        simplify(XU >= XL, true),
        U = XU
    ;
        simplify(XU >= 0, true),
        simplify(0 >= XL, true),
        evaluate_rational_expression(-XL, MinusXL),
        choose_max([MinusXL, XU], U)
    ;
        simplify(0 >= XU, true),
        simplify(XU >= XL, true),
        evaluate_rational_expression(-XL, U)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

% EXPRESSION is X**Y (exponentiation)
%------------------------------------

do_find_upper_numeric_limit_for(X**N, U, integer, Hs) :-   /*[[UppLim_25(int)]]*/
    int(N),
    !,
    (
        N = 0,
        U = 1,
        Hs = []
    ;
        N = 1,
        !,
        find_upper_numeric_limit_for(X, U, integer, Hs)
    ;
        N < 0,
        !,
        fail                   /* rule out -ve powers */
    ;
        int(X),
        U iss X**N,
        int(U),
        !,
        Hs = []
    ;
        safe_deduce(X >= 0, integer, H1),
        find_upper_numeric_limit_for(X, XU, integer, H2),
        U iss XU**N,
        append(H1, H2, HL),
        sort(HL, Hs)
    ;
        simplify(N mod 2, 0),            /* so even power */
        find_upper_numeric_limit_for(abs(X), XU, integer, Hs),
        U iss XU**N
    ;
        safe_deduce(X < 0, integer, H1),
        simplify(N mod 2, 1),            /* so odd power & -ve number */
        find_upper_numeric_limit_for(X, XU, integer, H2),
        simplify(XU < 0, true),
        U iss XU**N,
        append(H1, H2, HL),
        sort(HL, Hs)
    ).

do_find_upper_numeric_limit_for(X**N, U, real, Hs) :-      /*[[UppLim_25(real)]]*/
    int(N),
    !,
    (
        N = 0,
        U = 1,
        Hs = []
    ;
        N = 1,
        !,
        find_upper_numeric_limit_for(X, U, real, Hs)
    ;
        N < 0,
        !,
        fail                   /* rule out -ve powers */
    ;
        base_rational(X),
        evaluate_rational_expression(X**N, U),
        base_rational(U),    /* sanity check */
        !,
        Hs = []
    ;
        safe_deduce(X >= 0, real, H1),
        find_upper_numeric_limit_for(X, XU, real, H2),
        simplify(XU >= 0, true),     /* sanity check */
        evaluate_rational_expression(XU**N, U),
        append(H1, H2, HL),
        sort(HL, Hs)
    ;
        simplify(N mod 2, 0),            /* so even power */
        find_upper_numeric_limit_for(abs(X), XU, real, Hs),
        evaluate_rational_expression(XU**N, U)
    ;
        safe_deduce(X < 0, real, H1),
        simplify(N mod 2, 1),            /* so odd power & -ve number */
        find_upper_numeric_limit_for(X, XU, real, H2),
        simplify(XU < 0, true),
        evaluate_rational_expression(XU**N, U),
        append(H1, H2, HL),
        sort(HL, Hs)
    ).

do_find_upper_numeric_limit_for(X**Y, U, integer, Hs) :-   /*[[UppLim_26(int)]]*/
    (
        find_lower_numeric_limit_for(X, XL, integer, H1),    /* Ignore real powers for now */
        simplify(XL >= 0, true)
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL >= 0, true)
    ;
        safe_deduce(Y >= 0, integer, H2)
    ),
    find_upper_numeric_limit_for(X, XU, integer, H3),
    find_upper_numeric_limit_for(Y, YU, integer, H4),
    simplify(XU >= 1, true),
    simplify(YU >= 1, true),
    U iss XU ** YU,
    append(H3, H4, H3to4),
    append(H2, H3to4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(X**Y, U, real, Hs) :-      /*[[UppLim_26(real)]]*/
    (
        find_lower_numeric_limit_for(X, XL, real, H1),       /* Ignore real powers for now */
        simplify(XL >= 0, true)
    ;
        safe_deduce(X >= 0, real, H1)
    ),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL >= 0, true)
    ;
        safe_deduce(Y >= 0, integer, H2)
    ),
    find_upper_numeric_limit_for(X, XU, real, H3),
    find_upper_numeric_limit_for(Y, YU, integer, H4),
    simplify(XU >= 1, true),
    simplify(YU >= 1, true),
    evaluate_rational_expression(XU ** YU, U),
    append(H3, H4, H3to4),
    append(H2, H3to4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% EXPRESSION is X/Y (real division)
%----------------------------------

do_find_upper_numeric_limit_for(X/N, U, real, Hs) :-       /*[[UppLim_32]]*/
    base_rational(N),
    !,
    (
        simplify(N>0, true),
        find_upper_numeric_limit_for(X, XU, real, Hs),
        evaluate_rational_expression(XU/N, U)
    ;
        simplify(N<0, true),
        find_lower_numeric_limit_for(X, XL, real, Hs),
        evaluate_rational_expression(XL/N, U)
    ;
       search_for_upper_numeric_limit(X/N, U, real, Hs)
    ).

do_find_upper_numeric_limit_for(0/Y, 0, real, Hs) :-       /*[[UppLim_33]]*/
    !,
    safe_deduce(Y <> 0, real, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(N/Y, U, real, Hs) :-       /*[[UppLim_34]]*/
    base_rational(N),
    simplify(N>=0, true),
    !,
    find_lower_numeric_limit_for(Y, YL, real, H1),
    (
        simplify(YL > 0, true),         /* so Y must definitely be +ve */
        H2 = []
    ;
        safe_deduce(Y < 0, real, H2),   /* otherwise, check it's -ve */
        simplify(YL < 0, true)
    ),
    !,
    evaluate_rational_expression(N/YL, U),
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(N/Y, U, real, Hs) :-       /*[[UppLim_35]]*/
    base_rational(N),
    simplify(N<0, true),
    !,
    find_upper_numeric_limit_for(Y, YU, real, H1),
    (
        simplify(YU < 0, true),         /* so Y must definitely be -ve */
        H2 = []
    ;
        safe_deduce(Y > 0, real, H2),   /* otherwise, check it's +ve */
        simplify(YU > 0, true)
    ),
    !,
    evaluate_rational_expression(N/YU, U),
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(X/Y, U, real, Hs) :-       /*[[UppLim_36]]*/
    find_lower_numeric_limit_for(Y, YL, real, H1),     /* For real division, allow only  */
    simplify(YL>0, true),               /* +ve or -ve divisors, i.e.      */
    find_lower_numeric_limit_for(X, XL, real, H2),     /* ignore cases where Y straddles */
    simplify(XL>=0, true),              /* and insist X +ve          */
    find_upper_numeric_limit_for(X, XU, real, H3),
    simplify(XU>=0, true),              /* (sanity check) */
    evaluate_rational_expression(XU/YL, U),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_upper_numeric_limit_for(X/Y, U, real, Hs) :-       /*[[UppLim_37]]*/
    (
        find_upper_numeric_limit_for(Y, YU, real, H1),
        simplify(YU < 0, true)
    ;
        safe_deduce(Y < 0, real, H1)
    ),
    find_lower_numeric_limit_for(Y, YL, real, H2),
    simplify(YL<0, true),
    find_lower_numeric_limit_for(X, XL, real, H3),
    simplify(XL>=0, true),
    evaluate_rational_expression(XL/YL, U),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% EXPRESSION is anything else (catch-all)
%----------------------------------------

do_find_upper_numeric_limit_for(X, U, Type, Hs) :-         /*[[UppLim_27]]*/
    calculate_known_upper_limit_for(X, U, Type, Hs).   /* Catch-all case */

%===============================================================================
% find_lower_numeric_limit_for(+EXPRESSION, -NUM, +TYPE, -HYPS_USED).
%-------------------------------------------------------------------------------
% Determine a lower numeric limit for EXPRESSION of provided type TYPE as
% NUM, reporting hypotheses used as HYPS_USED.
%===============================================================================

find_lower_numeric_limit_for(X, L, Type, Hs) :-
    known_lower_numeric_limit(X, L, Type, Hs),      /* first, look to see if we've already found it */
    !.
find_lower_numeric_limit_for(X, L, Type, Hs) :-    /* if not, and it's an atom, calculate it */
    atom(X),
    !,
    calculate_known_lower_limit_for(X, L, Type, Hs).
find_lower_numeric_limit_for(N, N, integer, []) :-      /* if not, and it's a numeric literal, look no further */
    int(N),                /*formerly [[LowLim_1]]*/
    !.
find_lower_numeric_limit_for(N, N, real, []) :-    /* if not, and it's a numeric literal, look no further */
    base_rational(N),                /*formerly [[LowLim_1]]*/
    !.
find_lower_numeric_limit_for(N, L, integer, []) :-      /* if it's a numeric literal that isn't an integer, */
    base_rational(N),                /* e.g. 1/2, find the smallest integer above it. */
    !,
    find_smallest_integer_literal_above(N, L).
/* otherwise, (1) apply strategies, (2) use hypotheses, and (3) combine the results to get the least upper bound */
find_lower_numeric_limit_for(X, L, Type, Hs) :-
    do_find_lower_numeric_limit_for(X, L1, Type, H1s),
    assertz(candidate_lower(X, L1, Type, H1s)),
    calculate_known_lower_limit_for(X, L, Type, Hs),
    !.

%===============================================================================
% find_lower_numeric_limit_for(+EXPRESSION, -NUM, +TYPE, -HYPS_USED).
%-------------------------------------------------------------------------------
% Determine a lower numeric limit for EXPRESSION of provided type TYPE as
% NUM, reporting hypotheses used as HYPS_USED.
%===============================================================================

% EXPRESSION is a numeric literal
%--------------------------------
% This case has already been considered by the parent clause above.

% EXPRESSION is -X (unary minus)
%-------------------------------

do_find_lower_numeric_limit_for(-X, L, TYPE, Hs) :-        /*[[LowLim_2]]*/
    find_upper_numeric_limit_for(X, U, TYPE, Hs),      /* X <= U, so -X >= -U */
    evaluate_rational_expression(-U, L).

% EXPRESSION is X+Y (addition)
%-----------------------------

do_find_lower_numeric_limit_for(X+N, L, integer, Hs) :-    /*[[LowLim_3(int)]]*/
    int(N),
    !,
    find_lower_numeric_limit_for(X, XL, integer, Hs),
    L iss XL+N.

do_find_lower_numeric_limit_for(X+N, L, real, Hs) :-       /*[[LowLim_3(real)]]*/
    base_rational(N),
    !,
    find_lower_numeric_limit_for(X, XL, real, Hs),
    evaluate_rational_expression(XL+N, L).

do_find_lower_numeric_limit_for(N+X, L, integer, Hs) :-    /*[[LowLim_4(int)]]*/
    int(N),
    !,
    find_lower_numeric_limit_for(X, XL, integer, Hs),
    L iss XL+N.

do_find_lower_numeric_limit_for(N+X, L, real, Hs) :-       /*[[LowLim_4(real)]]*/
    base_rational(N),
    !,
    find_lower_numeric_limit_for(X, XL, real, Hs),
    evaluate_rational_expression(XL+N, L).

do_find_lower_numeric_limit_for(X+Y, L, TYPE, Hs) :-       /*[[LowLim_5]]*/
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* X >= XL, and */
    find_lower_numeric_limit_for(Y, YL, TYPE, H2),     /* Y >= YL, so  */
    evaluate_rational_expression(XL+YL, L),       /* X+Y >= XL+YL */
    append(H1, H2, HL),                 /* [Works for int and real] */
    sort(HL, Hs).

% EXPRESSION is X - X div Y [special case]
%-----------------------------------------

do_find_lower_numeric_limit_for(X - X div N, L, integer, Hs) :- /*[[LowLim_6]]*/
    int(N),
    simplify(N <> 0, true),
    !,
    find_lower_numeric_limit_for(X, XL, integer, Hs),
    L iss XL - XL div N.

do_find_lower_numeric_limit_for(X - X div Y, L, integer, Hs) :- /*[[LowLim_7]]*/
    find_lower_numeric_limit_for(Y, YL, integer, H1),       /* use biggest Y to get smallest X div Y */
    simplify(YL > 0, true),             /* and check it's strictly positive      */
    find_lower_numeric_limit_for(X, XL, integer, H2),
    (
        simplify(XL >= 0, true),        /* so X is >= 0, too */
        H3 = []
    ;
        safe_deduce(X >= 0, integer, H3)
    ),
    L iss XL - XL div YL,
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% EXPRESSION is Y - (X - X div Y * Y) [special case]
%---------------------------------------------------

do_find_lower_numeric_limit_for(Y - (X - X div Y * Y), 1, integer, Hs) :-       /*[[LowLim_28a]]*/
    (                               /* X +ve, Y +ve case only. */
        find_lower_numeric_limit_for(X, XL, integer, H1),          /* In this case, the lower limit */
        simplify(XL >= 0, true)                     /* is one. */
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        safe_deduce(Y > 0, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(Y - (X - Y * (X div Y)), 1, integer, Hs) :-     /*[[LowLim_28a]]*/
    (
        find_lower_numeric_limit_for(X, XL, integer, H1),
        simplify(XL >= 0, true)
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        safe_deduce(Y > 0, integer, H2)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

% EXPRESSION is X - X div Y * Y [special case]
%---------------------------------------------

do_find_lower_numeric_limit_for(X - X div Y * Y, 0, integer, Hs) :-     /*[[LowLim_29a]]*/
    (                            /* X +ve case (Y's sign irrelevant). */
        find_lower_numeric_limit_for(X, XL, integer, H1),       /* In this case, the lower limit */
        simplify(XL >= 0, true)                  /* is zero, regardless of Y. */
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        safe_deduce(Y <> 0, integer, H2)
    ;
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        find_upper_numeric_limit_for(Y, YU, integer, H2),
        simplify(YU < 0, true)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(X - Y * (X div Y), 0, integer, Hs) :-   /*[[LowLim_29b]]*/
    (
        find_lower_numeric_limit_for(X, XL, integer, H1),
        simplify(XL >= 0, true)
    ;
        safe_deduce(X >= 0, integer, H1)
    ),
    (
        safe_deduce(Y <> 0, integer, H2)
    ;
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        find_upper_numeric_limit_for(Y, YU, integer, H2),
        simplify(YU < 0, true)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(X - X div Y * Y, L, integer, Hs) :-     /*[[LowLim_30a]]*/
    (                            /* X -ve, Y +ve case. */
        find_upper_numeric_limit_for(X, XU, integer, H1),       /* In this case, the lower limit */
        simplify(XU < 0, true)                   /* is -(YU-1). */
    ;
        safe_deduce(X < 0, integer, H1)
    ),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        safe_deduce(Y >= 1, integer, H2)
    ;
        safe_deduce(Y > 0, integer, H2)
    ),
    find_upper_numeric_limit_for(Y, YU, integer, H3),
    simplify(YU > 0, true),    /* sanity check */
    L iss -(YU - 1),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(X - Y * (X div Y), L, integer, Hs) :-   /*[[LowLim_30b]]*/
    (
        find_upper_numeric_limit_for(X, XU, integer, H1),
        simplify(XU < 0, true)
    ;
        safe_deduce(X < 0, integer, H1)
    ),
    (
        find_lower_numeric_limit_for(Y, YL, integer, H2),
        simplify(YL > 0, true)
    ;
        safe_deduce(Y >= 1, integer, H2)
    ;
        safe_deduce(Y > 0, integer, H2)
    ),
    find_upper_numeric_limit_for(Y, YU, integer, H3),
    simplify(YU > 0, true),    /* sanity check */
    L iss -(YU - 1),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(X - X div Y * Y, L, integer, Hs) :-     /*[[LowLim_31a]]*/
    (                            /* X -ve, Y -ve case. */
        find_upper_numeric_limit_for(X, XU, integer, H1),       /* In this case, the lower limit */
        simplify(XU < 0, true)                   /* is YL+1. */
    ;
        safe_deduce(X < 0, integer, H1)
    ),
    (
        find_upper_numeric_limit_for(Y, YU, integer, H2),
        simplify(YU < 0, true)
    ;
        safe_deduce(Y <= (- 1), integer, H2)
    ;
        safe_deduce(Y < 0, integer, H2)
    ),
    find_lower_numeric_limit_for(Y, YL, integer, H3),
    simplify(YL < 0, true),    /* sanity check */
    L iss YL + 1,
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(X - Y * (X div Y), L, integer, Hs) :-   /*[[LowLim_31b]]*/
    (
        find_upper_numeric_limit_for(X, XU, integer, H1),
        simplify(XU < 0, true)
    ;
        safe_deduce(X < 0, integer, H1)
    ),
    (
        find_upper_numeric_limit_for(Y, YU, integer, H2),
        simplify(YU < 0, true)
    ;
        safe_deduce(Y <= (- 1), integer, H2)
    ;
        safe_deduce(Y < 0, integer, H2)
    ),
    find_lower_numeric_limit_for(Y, YL, integer, H3),
    simplify(YL < 0, true),    /* sanity check */
    L iss YL + 1,
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% EXPRESSION is X-Y (subtraction)
%--------------------------------

do_find_lower_numeric_limit_for(X-N, L, integer, Hs) :-    /*[[LowLim_8(int)]]*/
    int(N),
    !,
    find_lower_numeric_limit_for(X, XL, integer, Hs),
    L iss XL-N.

do_find_lower_numeric_limit_for(X-N, L, real, Hs) :-       /*[[LowLim_8(real)]]*/
    base_rational(N),
    !,
    find_lower_numeric_limit_for(X, XL, real, Hs),
    evaluate_rational_expression(XL-N, L).

do_find_lower_numeric_limit_for(N-Y, L, integer, Hs) :-    /*[[LowLim_9(int)]]*/
    int(N),
    !,
    find_upper_numeric_limit_for(Y, YU, integer, Hs),
    L iss N-YU.

do_find_lower_numeric_limit_for(N-Y, L, real, Hs) :-       /*[[LowLim_9(real)]]*/
    base_rational(N),
    !,
    find_upper_numeric_limit_for(Y, YU, real, Hs),
    evaluate_rational_expression(N-YU, L).

do_find_lower_numeric_limit_for(X-Y, L, TYPE, Hs) :-       /*[[LowLim_10]]*/
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* X >= XL, and */
    find_upper_numeric_limit_for(Y, YU, TYPE, H2),     /* Y <= YU, i.e. -Y >= -YU, so */
    evaluate_rational_expression(XL-YU, L),       /* X-Y >= XL-YU */
    append(H1, H2, HL),                 /* [Works for int and real] */
    sort(HL, Hs).

% EXPRESSION is X*Y (multiplication)
%-----------------------------------

do_find_lower_numeric_limit_for(X*N, L, integer, Hs) :-    /*[[LowLim_11(int)]]*/
    int(N),
    !,
    (
        simplify(N>0, true),
        !,
        find_lower_numeric_limit_for(X, XL, integer, Hs),
        L iss XL*N
    ;
        0 iss N,
        !,
        L = 0,
        Hs = []
    ;
        simplify(N<0, true),
        !,
        find_upper_numeric_limit_for(X, XU, integer, Hs),
        L iss XU*N
    ).

do_find_lower_numeric_limit_for(X*N, L, real, Hs) :-       /*[[LowLim_11(real)]]*/
    base_rational(N),
    !,
    (
        simplify(N>0, true),
        !,
        find_lower_numeric_limit_for(X, XL, real, Hs),
        evaluate_rational_expression(XL*N, L)
    ;
        N = 0,
        !,
        L = 0,
        Hs = []
    ;
        simplify(N<0, true),
        !,
        find_upper_numeric_limit_for(X, XU, real, Hs),
        evaluate_rational_expression(XU*N, L)
    ).

do_find_lower_numeric_limit_for(N*X, L, integer, Hs) :-    /*[[LowLim_12(int)]]*/
    int(N),
    !,
    (
        simplify(N>0, true),
        !,
        find_lower_numeric_limit_for(X, XL, integer, Hs),
        L iss XL*N
    ;
        N = 0,
        !,
        L = 0,
        Hs = []
    ;
        simplify(N<0, true),
        !,
        find_upper_numeric_limit_for(X, XU, integer, Hs),
        L iss XU*N
    ).

do_find_lower_numeric_limit_for(N*X, L, real, Hs) :-       /*[[LowLim_12(real)]]*/
    base_rational(N),
    !,
    (
        simplify(N>0, true),
        !,
        find_lower_numeric_limit_for(X, XL, real, Hs),
        evaluate_rational_expression(XL*N, L)
    ;
        N = 0,
        !,
        L = 0,
        Hs = []
    ;
        simplify(N<0, true),
        !,
        find_upper_numeric_limit_for(X, XU, real, Hs),
        evaluate_rational_expression(XU*N, L)
    ).

do_find_lower_numeric_limit_for(X*X, L, TYPE, Hs) :-       /*[[LowLim_28]] -- special case */
    find_lower_numeric_limit_for(X, XL, TYPE, Hs),     /* [Works for int and real] */
    base_rational(XL),
    simplify(XL>=0, true),
    evaluate_rational_expression(XL*XL, L).

do_find_lower_numeric_limit_for(X*Y, L, TYPE, Hs) :-       /*[[LowLim_13]]*/
    find_lower_numeric_limit_for(X, XL, TYPE, Hxl),    /* find X in XL..XU, Y in YL..YU, */
    find_upper_numeric_limit_for(X, XU, TYPE, Hxu),    /* calculate the four products    */
    find_lower_numeric_limit_for(Y, YL, TYPE, Hyl),    /* XL*YL, XL*YU, XU*YL and XU*YU  */
    find_upper_numeric_limit_for(Y, YU, TYPE, Hyu),    /* and determine the limits       */
    calc_product_bounds(XL, XU, YL, YU, TYPE, Hxl, Hxu, Hyl, Hyu, _, _, L, Hs).

% EXPRESSION is X div Y (integer division)
%-----------------------------------------

do_find_lower_numeric_limit_for(X div N, L, integer, Hs) :-     /*[[LowLim_14]]*/
    int(N),
    !,
    (
        simplify(N>0, true),
        find_lower_numeric_limit_for(X, XL, integer, Hs),
        L iss XL div N
    ;
        simplify(N<0, true),
        find_upper_numeric_limit_for(X, XU, integer, Hs),
        L iss XU div N
    ;
        search_for_lower_numeric_limit(X div N, L, integer, Hs)
    ).

do_find_lower_numeric_limit_for(0 div Y, 0, integer, Hs) :-     /*[[LowLim_15]]*/
    !,
    (
        safe_deduce(Y <> 0, integer, HL)
    ;
        find_lower_numeric_limit_for(Y, YL, integer, HL),
        simplify(YL > 0, true)
    ;
        find_upper_numeric_limit_for(Y, YU, integer, HL),
        simplify(YU < 0, true)
    ),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(N div Y, L, integer, Hs) :-     /*[[LowLim_16]]*/
    int(N),
    simplify(N>=0, true),
    !,
    find_upper_numeric_limit_for(Y, YU, integer, H1),
    (
        simplify(YU < 0, true),      /* so Y must definitely be -ve, too */
        H2 = []
    ;
        (
            find_lower_numeric_limit_for(Y, YL, integer, H2),
            simplify(YL > 0, true)
        ;
            safe_deduce(Y > 0, integer, H2)
        ;
            safe_deduce(Y >= 1, integer, H2)
        ),
        simplify(YU > 0, true)
    ),
    !,
    L iss N div YU,
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(N div Y, L, integer, Hs) :-     /*[[LowLim_17]]*/
    int(N),
    simplify(N<0, true),
    !,
    find_lower_numeric_limit_for(Y, YL, integer, H1),
    (
        simplify(YL > 0, true),      /* so Y must be definitely +ve, too */
        H2 = []
    ;
        (
            find_upper_numeric_limit_for(Y, YU, integer, H2),
            simplify(YU < 0, true)
        ;
            safe_deduce(Y < 0, integer, H2)
        ;
            safe_deduce(Y <= - 1, integer, H2)
        ),
        simplify(YL < 0, true)
    ),
    !,
    L iss N div YL,
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(X div Y, L, integer, Hs) :-     /*[[LowLim_18]]*/
    (                         /* For integer division, allow    */
        find_lower_numeric_limit_for(Y, YL, integer, H1),    /* only +ve or -ve divisors, i.e. */
        simplify(YL > 0, true)                /* ignore cases where Y straddles */
    ;                         /* zero (unlikely anyway) and     */
        safe_deduce(Y > 0, integer, H1)            /* insist X +ve.        */
    ;
        safe_deduce(Y >= 1, integer, H1)
    ),
    find_upper_numeric_limit_for(Y, YU, integer, H2),
    simplify(YU>0, true),
    find_lower_numeric_limit_for(X, XL, integer, H3),
    simplify(XL>=0, true),              /* So X>=0 also, given X>=XL */
    L iss XL div YU,
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(X div Y, L, integer, Hs) :-     /*[[LowLim_19]]*/
    find_upper_numeric_limit_for(Y, YU, integer, H1),       /* so Y must be -ve, too */
    simplify(YU<0, true),
    find_upper_numeric_limit_for(X, XU, integer, H2),
    simplify(XU>=0, true),
    L iss XU div YU,
    append(H1, H2, HL),
    sort(HL, Hs).

% EXPRESSION is X mod Y (integer modulus)
%----------------------------------------

do_find_lower_numeric_limit_for(X mod N, L, integer, Hs) :-     /*[[LowLim_20]]*/
    get_provenance_framework(spark),
    int(N),
    !,
    (
        int(X),
        N \= 0,
        !,
        Hs = [],
        L iss X mod N
    ;
        simplify(N > 0, true),
        !,
        (
            find_lower_numeric_limit_for(X, XL, integer, H1),
            simplify(XL > 0, true),
            find_upper_numeric_limit_for(X, XU, integer, H2),
            simplify(XU < N, true),   /* so 0<XL<=X<=XU<N, so X mod N>=XL */
            simplify(XL<=XU, true),   /* extra sanity check */
            append(H1, H2, HL),
            sort(HL, Hs),
            L = XL
        ;
            Hs = [],
            L = 0
        )
    ;
        simplify(N < 0, true),
        Hs = [],
        L iss N+1
    ),
    !.

do_find_lower_numeric_limit_for(_ mod Y, 0, integer, Hs) :-     /*[[LowLim_21]]*/
    get_provenance_framework(spark),
    (
        find_lower_numeric_limit_for(Y, YL, integer, Hs),
        simplify(YL > 0, true)
    ;
        safe_deduce(Y > 0, integer, Hs)
    ;
        safe_deduce(Y >= 1, integer, Hs)
    ),
    !.

do_find_lower_numeric_limit_for(_ mod Y, L, integer, Hs) :-     /*[[LowLim_22]]*/
    get_provenance_framework(spark),
    (
        find_upper_numeric_limit_for(Y, YU, integer, H1),
        simplify(YU < 0, true)
    ;
        safe_deduce(Y < 0, integer, H1)
    ;
        safe_deduce(Y <= - 1, integer, H1)
    ),
    !,
    find_lower_numeric_limit_for(Y, YL, integer, H2),
    L iss YL+1,
    append(H1, H2, HL),
    sort(HL, Hs).

% EXPRESSION is abs(X) (absolute value function)
%-----------------------------------------------

do_find_lower_numeric_limit_for(abs(N), L, integer, []) :-      /*[[LowLim_23(int)]]*/
    int(N),
    !,
    (
        simplify(N>=0, true),
        !,
        L = N
    ;
        simplify(N<0, true),
        !,
        L iss -N
    ),
    !.

do_find_lower_numeric_limit_for(abs(N), L, real, []) :-    /*[[LowLim_23(real)]]*/
    base_rational(N),
    !,
    (
        simplify(N>=0, true),
        !,
        L = N
    ;
        simplify(N<0, true),
        !,
        evaluate_rational_expression(-N, L)
    ),
    !.

do_find_lower_numeric_limit_for(abs(X), L, TYPE, Hs) :-    /*[[LowLim_24]]*/
    find_lower_numeric_limit_for(X, XL, TYPE, H1),     /* [Works for int and real] */
    find_upper_numeric_limit_for(X, XU, TYPE, H2),
    (
        simplify(XU>=XL, true),
        simplify(XL>=0, true),
        L = XL
    ;
        simplify(XU>=0, true),
        simplify(0>=XL, true),
        L = 0
    ;
        simplify(0>=XU, true),
        simplify(XU>=XL, true),
        evaluate_rational_expression(-XU, L)
    ),
    append(H1, H2, HL),
    sort(HL, Hs).

% EXPRESSION is X**Y (exponentiation)
%------------------------------------

do_find_lower_numeric_limit_for(X**N, L, integer, Hs) :-   /*[[LowLim_25(int)]]*/
    int(N),
    !,
    (
        N = 0,
        L = 1,
        Hs = []
    ;
        N = 1,
        !,
        find_lower_numeric_limit_for(X, L, integer, Hs)
    ;
        N =< 0,
        !,
        fail                   /* rule out non +ve powers */
    ;
        int(X),
        L iss X**N,
        int(L),
        !,
        Hs = []
    ;
        safe_deduce(X >= 0, integer, H1),
        find_lower_numeric_limit_for(X, XL, integer, H2),
        simplify(XL >= 0, true),
        L iss XL**N,
        append(H1, H2, HL),
        sort(HL, Hs)
    ;
        simplify(N mod 2, 0),            /* so even power */
        find_lower_numeric_limit_for(abs(X), XL, integer, Hs),
        simplify(XL >= 0, true),              /* sanity check */
        L iss XL**N
    ;
        safe_deduce(X < 0, integer, H1),
        simplify(N mod 2, 1),
        find_lower_numeric_limit_for(X, XL, integer, H2),
        simplify(XL < 0, true),
        L iss XL**N,
        append(H1, H2, HL),
        sort(HL, Hs)
    ).

do_find_lower_numeric_limit_for(X**N, L, real, Hs) :-      /*[[LowLim_25(real)]]*/
    int(N),
    !,
    (
        N = 0,
        L = 1,
        Hs = []
    ;
        N = 1,
        !,
        find_lower_numeric_limit_for(X, L, real, Hs)
    ;
        N =< 0,
        !,
        fail                   /* rule out non +ve powers */
    ;
        base_rational(X),
        evaluate_rational_expression(X**N, L),
        base_rational(L),    /* sanity check */
        !,
        Hs = []
    ;
        safe_deduce(X >= 0, real, H1),
        find_lower_numeric_limit_for(X, XL, real, H2),
        simplify(XL >= 0, true),     /* sanity check */
        evaluate_rational_expression(XL**N, L),
        append(H1, H2, HL),
        sort(HL, Hs)
    ;
        simplify(N mod 2, 0),            /* so even power */
        find_lower_numeric_limit_for(abs(X), XL, real, Hs),
        simplify(XL >= 0, true),              /* sanity check */
        evaluate_rational_expression(XL**N, L)
    ;
        safe_deduce(X < 0, real, H1),
        simplify(N mod 2, 1),
        find_lower_numeric_limit_for(X, XL, real, H2),
        simplify(XL < 0, true),
        evaluate_rational_expression(XL**N, L),
        append(H1, H2, HL),
        sort(HL, Hs)
    ).

do_find_lower_numeric_limit_for(X**Y, L, integer, Hs) :-   /*[[LowLim_26(int)]]*/
    safe_deduce(X >= 0, integer, H1),             /* Ignore real powers for now */
    safe_deduce(Y >= 0, integer, H2),
    find_lower_numeric_limit_for(X, XL, integer, H3),       /* Given integers, XL**YL is the lower limit. */
    find_lower_numeric_limit_for(Y, YL, integer, H4),       /* (This is NOT true for reals, e.g. X=1/2.)  */
    simplify(XL >= 0, true),
    simplify(YL >= 0, true),
    (
        simplify(XL >= 1, true),
        L iss XL ** YL
    ;
        XL = 0,                     /* Rule out XL<0 case: too obscure. */
        L = 0
    ),
    append(H3, H4, H3to4),
    append(H2, H3to4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(X**Y, L, real, Hs) :-      /*[[LowLim_26(real)]]*/
    safe_deduce(X >= 0, real, H1),           /* Ignore real powers for now */
    safe_deduce(Y >= 0, integer, H2),
    find_lower_numeric_limit_for(X, XL, real, H3),     /* Given integers, XL**YL is the lower limit. */
    find_lower_numeric_limit_for(Y, YL, integer, H4),       /* (This is NOT true for reals, e.g. X=1/2.)  */
    simplify(XL >= 0, true),
    simplify(YL >= 0, true),
    (
        simplify(XL > 0, true),
        evaluate_rational_expression(XL ** YL, L)
    ;
        XL = 0,                     /* Rule out XL<0 case: too obscure. */
        L = 0
    ),
    append(H3, H4, H3to4),
    append(H2, H3to4, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

% EXPRESSION is X/Y (real division)
%----------------------------------

do_find_lower_numeric_limit_for(X/N, L, real, Hs) :-       /*[[LowLim_32]*/
    base_rational(N),
    !,
    (
        simplify(N>0, true),
        find_lower_numeric_limit_for(X, XL, real, Hs),
        evaluate_rational_expression(XL/N, L)
    ;
        simplify(N<0, true),
        find_upper_numeric_limit_for(X, XU, real, Hs),
        evaluate_rational_expression(XU/N, L)
    ;
        search_for_lower_numeric_limit(X/N, L, real, Hs)
    ).

do_find_lower_numeric_limit_for(0/Y, 0, real, Hs) :-       /*[[LowLim_33]]*/
    !,
    (
        safe_deduce(Y <> 0, real, HL)
    ;
        find_lower_numeric_limit_for(Y, YL, real, HL),
        simplify(YL > 0, true)
    ;
        find_upper_numeric_limit_for(Y, YU, real, HL),
        simplify(YU < 0, true)
    ),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(N/Y, L, real, Hs) :-       /*[[LowLim_34]]*/
    base_rational(N),
    simplify(N>=0, true),
    !,
    find_upper_numeric_limit_for(Y, YU, real, H1),
    (
        simplify(YU < 0, true),      /* so Y must definitely be -ve, too */
        H2 = []
    ;
        (
            find_lower_numeric_limit_for(Y, YL, real, H2),
            simplify(YL > 0, true)
        ;
            safe_deduce(Y > 0, real, H2)
        ),
        simplify(YU > 0, true)
    ),
    !,
    evaluate_rational_expression(N/YU, L),
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(N/Y, L, real, Hs) :-       /*[[LowLim_35]]*/
    base_rational(N),
    simplify(N<0, true),
    !,
    find_lower_numeric_limit_for(Y, YL, real, H1),
    (
        simplify(YL > 0, true),      /* so Y must be definitely +ve, too */
        H2 = []
    ;
        (
            find_upper_numeric_limit_for(Y, YU, real, H2),
            simplify(YU < 0, true)
        ;
            safe_deduce(Y < 0, real, H2)
        ),
        simplify(YL < 0, true)
    ),
    !,
    evaluate_rational_expression(N/YL, L),
    append(H1, H2, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(X/Y, L, real, Hs) :-       /*[[LowLim_36]]*/
    (                         /* For real division, allow only  */
        find_lower_numeric_limit_for(Y, YL, real, H1),       /* +ve or -ve divisors, i.e.      */
        simplify(YL > 0, true)                /* ignore cases where Y straddles */
    ;                         /* zero (unlikely anyway) and     */
        safe_deduce(Y > 0, real, H1)          /* insist X +ve.        */
    ),
    find_upper_numeric_limit_for(Y, YU, real, H2),
    simplify(YU>0, true),
    find_lower_numeric_limit_for(X, XL, real, H3),
    simplify(XL>=0, true),              /* So X>=0 also, given X>=XL */
    evaluate_rational_expression(XL/YU, L),
    append(H2, H3, Hrest),
    append(H1, Hrest, HL),
    sort(HL, Hs).

do_find_lower_numeric_limit_for(X/Y, L, real, Hs) :-       /*[[LowLim_37]]*/
    find_upper_numeric_limit_for(Y, YU, real, H1),     /* so Y must be -ve, too */
    simplify(YU<0, true),
    find_upper_numeric_limit_for(X, XU, real, H2),
    simplify(XU>=0, true),
    evaluate_rational_expression(XU/YU, L),
    append(H1, H2, HL),
    sort(HL, Hs).


% EXPRESSION is anything else (catch-all)
%----------------------------------------

do_find_lower_numeric_limit_for(X, L, Type, Hs) :-            /*[[LowLim_27]]*/
    calculate_known_lower_limit_for(X, L, Type, Hs).   /* Catch-all case */

%===============================================================================
% calculate_known_upper_limit_for(+EXPRESSION, -NUM, +TYPE, -HYPS_USED).
%-------------------------------------------------------------------------------
% Determine a upper numeric limit for EXPRESSION of provided type TYPE as
% NUM, reporting hypotheses used as HYPS_USED. Here, the limit is primarily
% calcuated through deeper reasoning and exploiting additional information
% (hypotheses).
%===============================================================================

calculate_known_upper_limit_for(X, _, Type, _) :-
    search_for_upper_numeric_limit(X, U, Type, Hs),
    assertz(candidate_upper(X, U, Type, Hs)),
    fail.
calculate_known_upper_limit_for(X, U, Type, Hs) :-
    candidate_upper(X, U, Type, Hs),
    \+ (( candidate_upper(X, N, Type, _), simplify(N<U, true) )),   /* so there's no *smaller* upper */
    !,
    assertz(known_upper_numeric_limit(X, U, Type, Hs)),
    retractall(candidate_upper(X, _, Type, _)),
    !.

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

search_for_upper_numeric_limit(X, U, Type, Hs) :-          /*[[SrchUpp_1]]*/
    infrule(X<=U, Hs),
    (
        int(U)
    ;
        Type = real,
        strict_rational(U)
    ).

search_for_upper_numeric_limit(X, U, Type, Hs) :-          /*[[SrchUpp_2]]*/
    limited_extended_infrule(X<=A, H1, 1, Lim),
    \+ base_rational(A),
    limited_extended_infrule(A<=U, H2, Lim, _),
    (
        int(U)
    ;
        Type = real,
        strict_rational(U)
    ),
    append(H1, H2, Hs).

search_for_upper_numeric_limit(X, U, Type, Hs) :-          /*[[SrchUpp_3]]*/
    limited_extended_infrule(X<=A, H1, 1, Lim1),       /* Go at most three deep, transitively. */
    \+ base_rational(A),
    limited_extended_infrule(A<=B, H2, Lim1, Lim2),
    \+ base_rational(B),
    limited_extended_infrule(B<=U, H3, Lim2, _),
    (
        int(U)
    ;
        Type = real,
        strict_rational(U)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, Hs).

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_4]]*/
    infrule(X<XU, Hs),
    int(XU),
    U iss XU-1.                    /* since if X<XU, then X<=XU-1 */

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_5]]*/
    limited_extended_infrule(X<A, H1, 1, Lim),
    \+ int(A),
    limited_extended_infrule(A<=XU, H2, Lim, _),
    int(XU),
    U iss XU-1,                    /* since X<A<=XU -> X<=XU-1 */
    append(H1, H2, Hs).

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_6]]*/
    limited_extended_infrule(X<=A, H1, 1, Lim),
    \+ int(A),
    limited_extended_infrule(A<XU, H2, Lim, _),
    int(XU),
    U iss XU-1,                    /* since X<=A<XU -> X<=XU-1 */
    append(H1, H2, Hs).

/* Some other common searches for bounds... */
search_for_upper_numeric_limit(X, U, Type, Hs) :-          /*[[SrchUpp_7]]*/
    limited_extended_infrule(X<=A+I, H1, 1, Lim),      /* X<=A+I, and */
    \+ base_rational(A),
    (
        int(I)
    ;
        Type = real,
        strict_rational(I)
    ),
    limited_extended_infrule(A<=J, H2, Lim, _),        /* A<=J, so X<=I+J */
    (
        int(J)
    ;
        Type = real,
        strict_rational(J)
    ),
    evaluate_rational_expression(I+J, U),
    append(H1, H2, Hs).

search_for_upper_numeric_limit(X, U, Type, Hs) :-          /*[[SrchUpp_8]]*/
    limited_extended_infrule(X<=I+A, H1, 1, Lim),      /* X<=A+I, and */
    \+ base_rational(A),
    (
        int(I)
    ;
        Type = real,
        strict_rational(I)
    ),
    limited_extended_infrule(A<=J, H2, Lim, _),        /* A<=J, so X<=I+J */
    (
        int(J)
    ;
        Type = real,
        strict_rational(J)
    ),
    evaluate_rational_expression(I+J, U),
    append(H1, H2, Hs).

search_for_upper_numeric_limit(X, U, Type, Hs) :-          /*[[SrchUpp_9]]*/
    limited_extended_infrule(X<=A-I, H1, 1, Lim),      /* X<=A-I, and */
    \+ base_rational(A),
    (
        int(I)
    ;
        Type = real,
        strict_rational(I)
    ),
    limited_extended_infrule(A<=J, H2, Lim, _),        /* A<=J, so X<=J-I */
    (
        int(J)
    ;
        Type = real,
        strict_rational(J)
    ),
    evaluate_rational_expression(J-I, U),
    append(H1, H2, Hs).

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_10(int)]]*/
    infrule(X*I<=J, Hs),                /* X*I<=J (I,J integers), and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    U iss J div I.                 /* X <= J div I */

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_11(int)]]*/
    infrule(I*X<=J, Hs),                /* I*X<=J (I,J integers), and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    U iss J div I.                 /* X <= J div I */

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_12(int)]]*/
    infrule((X+N)*I<=J, Hs),            /* (X+N)*I<=J (I,J,N integers), and */
    int(I),
    int(J),
    int(N),
    simplify(I>0, true),                /* I > 0, so */
    U iss J div I - N.                  /* X <= J div I - N */

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_13(int)]]*/
    infrule(I*(X+N)<=J, Hs),            /* I*(X+N)<=J (I,J,N integers), and */
    int(I),
    int(J),
    int(N),
    simplify(I>0, true),                /* I > 0, so */
    U iss J div I - N.                  /* X <= J div I - N */

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_14(int)]]*/
    infrule((N+X)*I<=J, Hs),            /* (N+X)*I<=J (I,J,N integers), and */
    int(I),
    int(J),
    int(N),
    simplify(I>0, true),                /* I > 0, so */
    U iss J div I - N.                  /* X <= J div I - N */

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_15(int)]]*/
    infrule(I*(N+X)<=J, Hs),            /* I*(N+X)<=J (I,J,N integers), and */
    int(I),
    int(J),
    int(N),
    simplify(I>0, true),                /* I > 0, so */
    U iss J div I - N.                  /* X <= J div I - N */

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_16(int)]]*/
    infrule((X-N)*I<=J, Hs),            /* (X-N)*I<=J (I,J,N integers), and */
    int(I),
    int(J),
    int(N),
    simplify(I>0, true),                /* I > 0, so */
    U iss J div I + N.                  /* X <= J div I + N */

search_for_upper_numeric_limit(X, U, integer, Hs) :-       /*[[SrchUpp_17(int)]]*/
    infrule(I*(X-N)<=J, Hs),            /* I*(X-N)<=J (I,J,N integers), and */
    int(I),
    int(J),
    int(N),
    simplify(I>0, true),                /* I > 0, so */
    U iss J div I + N.                  /* X <= J div I + N */

search_for_upper_numeric_limit(X, U, real, Hs) :-          /*[[SrchUpp_10(real)]]*/
    infrule(X*I<=J, Hs),                /* X*I<=J (I,J literals), and */
    base_rational(I),
    base_rational(J),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I, U).         /* X <= J/I */

search_for_upper_numeric_limit(X, U, real, Hs) :-          /*[[SrchUpp_11(real)]]*/
    infrule(I*X<=J, Hs),                /* I*X<=J (I,J literals), and */
    base_rational(I),
    base_rational(J),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I, U).         /* X <= J/I */

search_for_upper_numeric_limit(X, U, real, Hs) :-          /*[[SrchUpp_12(real)]]*/
    infrule((X+N)*I<=J, Hs),            /* (X+N)*I<=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I - N, U).          /* X <= J/I - N */

search_for_upper_numeric_limit(X, U, real, Hs) :-          /*[[SrchUpp_13(real)]]*/
    infrule(I*(X+N)<=J, Hs),            /* I*(X+N)<=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I - N, U).          /* X <= J/I - N */

search_for_upper_numeric_limit(X, U, real, Hs) :-          /*[[SrchUpp_14(real)]]*/
    infrule((N+X)*I<=J, Hs),            /* (N+X)*I<=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I - N, U).          /* X <= J/I - N */

search_for_upper_numeric_limit(X, U, real, Hs) :-          /*[[SrchUpp_15(real)]]*/
    infrule(I*(N+X)<=J, Hs),            /* I*(N+X)<=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I - N, U).          /* X <= J/I - N */

search_for_upper_numeric_limit(X, U, real, Hs) :-          /*[[SrchUpp_16(real)]]*/
    infrule((X-N)*I<=J, Hs),            /* (X-N)*I<=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I + N, U).          /* X <= J/I + N */

search_for_upper_numeric_limit(X, U, real, Hs) :-          /*[[SrchUpp_17(real)]]*/
    infrule(I*(X-N)<=J, Hs),            /* I*(X-N)<=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I + N, U).          /* X <= J/I + N */

%===============================================================================
% calculate_known_lower_limit_for(+EXPRESSION, -NUM, +TYPE, -HYPS_USED).
%-------------------------------------------------------------------------------
% Determine a lower numeric limit for EXPRESSION of provided type TYPE as
% NUM, reporting hypotheses used as HYPS_USED. Here, the limit is primarily
% calcuated through deeper reasoning and exploiting additional information
% (hypotheses).
%===============================================================================

calculate_known_lower_limit_for(X, _, Type, _) :-
    search_for_lower_numeric_limit(X, L, Type, Hs),
    assertz(candidate_lower(X, L, Type, Hs)),
    fail.
calculate_known_lower_limit_for(X, L, Type, Hs) :-
    candidate_lower(X, L, Type, Hs),
    \+ (( candidate_lower(X, N, Type, _), simplify(N>L, true) )),   /* so there's no *larger* lower */
    !,
    assertz(known_lower_numeric_limit(X, L, Type, Hs)),
    retractall(candidate_lower(X, _, Type, _)),
    !.

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

search_for_lower_numeric_limit(X, L, Type, Hs) :-          /*[[SrchLow_1]]*/
    infrule(X>=L, Hs),
    (
        int(L)
    ;
        Type = real,
        strict_rational(L)
    ).

search_for_lower_numeric_limit(X, L, Type, Hs) :-          /*[[SrchLow_2]]*/
    limited_extended_infrule(X>=A, H1, 1, Lim),
    \+ base_rational(A),
    limited_extended_infrule(A>=L, H2, Lim, _),
    (
        int(L)
    ;
        Type = real,
        strict_rational(L)
    ),
    append(H1, H2, Hs).

search_for_lower_numeric_limit(X, L, Type, Hs) :-          /*[[SrchLow_3]]*/
    limited_extended_infrule(X>=A, H1, 1, Lim1),       /* Go at most three deep, transitively. */
    \+ base_rational(A),
    limited_extended_infrule(A>=B, H2, Lim1, Lim2),
    \+ base_rational(B),
    limited_extended_infrule(B>=L, H3, Lim2, _),
    (
        int(L)
    ;
        Type = real,
        strict_rational(L)
    ),
    append(H2, H3, Hrest),
    append(H1, Hrest, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_4]]*/
    infrule(X>XL, Hs),
    int(XL),
    L iss XL+1.                    /* since if X>XL, then X>=XL+1 */

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_5]]*/
    limited_extended_infrule(X>A, H1, 1, Lim),
    \+ int(A),
    limited_extended_infrule(A>=XL, H2, Lim, _),
    int(XL),
    L iss XL+1,                    /* since X>A>=XL -> X>=XL+1 */
    append(H1, H2, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_6]]*/
    limited_extended_infrule(X>=A, H1, 1, Lim),
    \+ int(A),
    limited_extended_infrule(A>XL, H2, Lim, _),
    int(XL),
    L iss XL+1,                    /* since X>=A>XL -> X>=XL+1 */
    append(H1, H2, Hs).

/* Some other common searches for bounds... */
search_for_lower_numeric_limit(X, L, Type, Hs) :-          /*[[SrchLow_7]]*/
    limited_extended_infrule(X>=A+I, H1, 1, Lim),      /* X>=A+I, and */
    \+ base_rational(A),
    (
        int(I)
    ;
        Type = real,
        strict_rational(I)
    ),
    limited_extended_infrule(A>=J, H2, Lim, _),        /* A>=J, so X>=I+J */
    (
        int(J)
    ;
        Type = real,
        strict_rational(J)
    ),
    evaluate_rational_expression(I+J, L),
    append(H1, H2, Hs).

search_for_lower_numeric_limit(X, L, Type, Hs) :-          /*[[SrchLow_8]]*/
    limited_extended_infrule(X>=I+A, H1, 1, Lim),      /* X>=A+I, and */
    \+ base_rational(A),
    (
        int(I)
    ;
        Type = real,
        strict_rational(I)
    ),
    limited_extended_infrule(A>=J, H2, Lim, _),        /* A>=J, so X>=I+J */
    (
        int(J)
    ;
        Type = real,
        strict_rational(J)
    ),
    evaluate_rational_expression(I+J, L),
    append(H1, H2, Hs).

search_for_lower_numeric_limit(X, L, Type, Hs) :-          /*[[SrchLow_9]]*/
    limited_extended_infrule(X>=A-I, H1, 1, Lim),      /* X>=A-I, and */
    \+ base_rational(A),
    (
        int(I)
    ;
        Type = real,
        strict_rational(I)
    ),
    limited_extended_infrule(A>=J, H2, Lim, _),        /* A>=J, so X>=J-I */
    (
        int(J)
    ;
        Type = real,
        strict_rational(J)
    ),
    evaluate_rational_expression(J-I, L),
    append(H1, H2, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_10(int)]]*/
    infrule(X*I>=J, H1),                /* X*I>=J (I,J integers), and */
    safe_deduce(X >= 1, integer, H2),             /* X >= 1, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-1) div I + 1,              /* X >= (J-1) div I + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_11(int)]]*/
    infrule(I*X>=J, H1),                /* X*I>=J (I,J integers), and */
    safe_deduce(X >= 1, integer, H2),             /* X >= 1, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-1) div I + 1,              /* X >= (J-1) div I + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_12(int)]]*/
    infrule((X+N)*I>=J, H1),            /* (X+N)*I>=J (I,J,N integers), and */
    int(N),
    OneMinusN iss 1-N,
    safe_deduce(X >= OneMinusN, integer, H2),          /* X >= 1-N, so X+N>=1, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-1) div I - N + 1,               /* X >= (J-1) div I - N + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_13(int)]]*/
    infrule(I*(X+N)>=J, H1),            /* I*(X+N)>=J (I,J,N integers), and */
    int(N),
    OneMinusN iss 1-N,
    safe_deduce(X >= OneMinusN, integer, H2),          /* X >= 1-N, so X+N>=1, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-1) div I - N + 1,               /* X >= (J-1) div I - N + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_14(int)]]*/
    infrule((N+X)*I>=J, H1),            /* (N+X)*I>=J (I,J,N integers), and */
    int(N),
    OneMinusN iss 1-N,
    safe_deduce(X >= OneMinusN, integer, H2),          /* X >= 1-N, so X+N>=1, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-1) div I - N + 1,               /* X >= (J-1) div I - N + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_15(int)]]*/
    infrule(I*(N+X)>=J, H1),            /* I*(N+X)>=J (I,J,N integers), and */
    int(N),
    OneMinusN iss 1-N,
    safe_deduce(X >= OneMinusN, integer, H2),          /* X >= 1-N, so X+N>=1, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-1) div I - N + 1,               /* X >= (J-1) div I - N + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_16(int)]]*/
    infrule((X-N)*I>=J, H1),            /* (X-N)*I>=J (I,J,N integers), and */
    int(N),
    Nplus1 iss N+1,
    safe_deduce(X >= Nplus1, integer, H2),        /* X >= N+1, so X-N>=1, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-1) div I + N + 1,               /* X >= (J-1) div I + N + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_17(int)]]*/
    infrule(I*(X-N)>=J, H1),            /* I*(X-N)>=J (I,J,N integers), and */
    int(N),
    Nplus1 iss N+1,
    safe_deduce(X >= Nplus1, integer, H2),        /* X >= N+1, so X-N>=1, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-1) div I + N + 1,               /* X <= I div J + N */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_18(int)]]*/
    infrule(X*I>=J, H1),                /* X*I>=J (I,J integers), and */
    safe_deduce(X <= 0, integer, H2),             /* X <= 0, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-I) div I + 1,              /* X >= (J-I) div I + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_19(int)]]*/
    infrule(I*X>=J, H1),                /* X*I>=J (I,J integers), and */
    safe_deduce(X <= 0, integer, H2),             /* X <= 0, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-I) div I + 1,              /* X >= (J-I) div I + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_20(int)]]*/
    infrule((X+N)*I>=J, H1),            /* (X+N)*I>=J (I,J,N integers), and */
    int(N),
    MinusN iss -N,
    safe_deduce(X <= MinusN, integer, H2),        /* X <= -N, so X+N<=0, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-I) div I - N + 1,               /* X >= (J-I) div I - N + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_21(int)]]*/
    infrule(I*(X+N)>=J, H1),            /* I*(X+N)>=J (I,J,N integers), and */
    int(N),
    MinusN iss -N,
    safe_deduce(X <= MinusN, integer, H2),        /* X <= -N, so X+N<=0, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-I) div I - N + 1,               /* X >= (J-1) div I - N + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_22(int)]]*/
    infrule((N+X)*I>=J, H1),            /* (N+X)*I>=J (I,J,N integers), and */
    int(N),
    MinusN iss -N,
    safe_deduce(X <= MinusN, integer, H2),        /* X <= -N, so X+N<=0, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-I) div I - N + 1,               /* X >= (J-1) div I - N + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_23(int)]]*/
    infrule(I*(N+X)>=J, H1),            /* I*(N+X)>=J (I,J,N integers), and */
    int(N),
    MinusN iss -N,
    safe_deduce(X <= MinusN, integer, H2),        /* X <= -N, so X+N<=0, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-I) div I - N + 1,               /* X >= (J-1) div I - N + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_24(int)]]*/
    infrule((X-N)*I>=J, H1),            /* (X-N)*I>=J (I,J,N integers), and */
    int(N),
    safe_deduce(X <= N, integer, H2),             /* X <= N, so X-N<=0, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-I) div I + N + 1,               /* X >= (J-1) div I + N + 1 */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, integer, Hs) :-       /*[[SrchLow_25(int)]]*/
    infrule(I*(X-N)>=J, H1),            /* I*(X-N)>=J (I,J,N integers), and */
    int(N),
    safe_deduce(X <= N, integer, H2),             /* X <= N, so X-N<=0, and */
    int(I),
    int(J),
    simplify(I>0, true),                /* I > 0, so */
    L iss (J-I) div I + N + 1,               /* X <= I div J + N */
    append(H1, H2, HL),
    sort(HL, Hs).

search_for_lower_numeric_limit(X, L, real, Hs) :-          /*[[SrchLow_10(real)]]*/
    infrule(X*I>=J, H1),                /* X*I>=J (I,J literals), and */
    base_rational(I),
    base_rational(J),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I, L),         /* X >= J/I */
    sort(H1, Hs).

search_for_lower_numeric_limit(X, L, real, Hs) :-          /*[[SrchLow_11(real)]]*/
    infrule(I*X>=J, H1),                /* X*I>=J (I,J literals), and */
    base_rational(I),
    base_rational(J),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I, L),         /* X >= J/I */
    sort(H1, Hs).

search_for_lower_numeric_limit(X, L, real, Hs) :-          /*[[SrchLow_12(real)]]*/
    infrule((X+N)*I>=J, H1),            /* (X+N)*I>=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I - N, L),          /* X >= J/I - N */
    sort(H1, Hs).

search_for_lower_numeric_limit(X, L, real, Hs) :-          /*[[SrchLow_13(real)]]*/
    infrule(I*(X+N)>=J, H1),            /* I*(X+N)>=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I - N, L),          /* X >= J/I - N */
    sort(H1, Hs).

search_for_lower_numeric_limit(X, L, real, Hs) :-          /*[[SrchLow_14(real)]]*/
    infrule((N+X)*I>=J, H1),            /* (N+X)*I>=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I - N, L),          /* X >= J/I - N */
    sort(H1, Hs).

search_for_lower_numeric_limit(X, L, real, Hs) :-          /*[[SrchLow_15(real)]]*/
    infrule(I*(N+X)>=J, H1),            /* I*(N+X)>=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I - N, L),          /* X >= J/I - N */
    sort(H1, Hs).

search_for_lower_numeric_limit(X, L, real, Hs) :-          /*[[SrchLow_16(real)]]*/
    infrule((X-N)*I>=J, H1),            /* (X-N)*I>=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I + N, L),          /* X >= J/I + N */
    sort(H1, Hs).

search_for_lower_numeric_limit(X, L, real, Hs) :-          /*[[SrchLow_17(real)]]*/
    infrule(I*(X-N)>=J, H1),            /* I*(X-N)>=J (I,J,N literals), and */
    base_rational(I),
    base_rational(J),
    base_rational(N),
    simplify(I>0, true),                /* I > 0, so */
    evaluate_rational_expression(J/I + N, L),          /* X >= J/I + N */
    sort(H1, Hs).

%===============================================================================
% limited_extended_infrule(+Goal, -HypsUsed, +StartLim, -EndLim).
%-------------------------------------------------------------------------------
% StartLim must be a non-negative integer.  If it it zero, we've "used up"
% our allowance of uses of extended_infrules (which use quantified
% hypotheses) and may only use standard_infrules.  If StartLim is strictly
% positive, we may use extended_infrules too.  The value of EndLim that is
% returned by the predicate on succeeding is either StartLim (if no
% extended_infrule was used) or StartLim-1 (if we did make use of an
% extended_infrule).  This returned limit value is used for the next call
% of limited_extended_infrule within a chain of reasoning, so we can call
% three infrules overall, and ensure that at most one of them is an
% extended_infrule.  This reduces the number of such matches, both for
% efficiency of search and for termination within reasonable space and time
% bounds.
%===============================================================================

limited_extended_infrule(Goal, Hyps, StartLim, EndLim) :-
    StartLim = 0,
    EndLim = 0,
    !,
    standard_infrule(Goal, Hyps).
limited_extended_infrule(Goal, Hyps, StartLim, EndLim) :-
    StartLim > 0,
    (
        standard_infrule(Goal, Hyps),
        EndLim = StartLim
    ;
        extended_infrule(Goal, Hyps),
        EndLim is StartLim - 1
    ).

%===============================================================================
% calc_product_bounds(XL, XU, YL, YU, Type, Hxl, Hxu, Hyl, Hyu, U, HU, L, HL).
%-------------------------------------------------------------------------------
% Find the lower (L) and upper (U) limits on a product, and the hypotheses
% used to deduce this.
%===============================================================================

calc_product_bounds(XL, XU, YL, YU, integer, Hxl, Hxu, Hyl, Hyu, U, HU, L, HL) :-
    Bound1 iss XL * YL,
    Bound2 iss XL * YU,
    Bound3 iss XU * YL,
    Bound4 iss XU * YU,
    choose_max([Bound1, Bound2, Bound3, Bound4], U),
    choose_min([Bound1, Bound2, Bound3, Bound4], L),
    (
        U = Bound1,
        append(Hxl, Hyl, HU)
    ;
        U = Bound2,
        append(Hxl, Hyu, HU)
    ;
        U = Bound3,
        append(Hxu, Hyl, HU)
    ;
        append(Hxu, Hyu, HU)
    ),
    !,
    (
        L = Bound1,
        append(Hxl, Hyl, HL)
    ;
        L = Bound2,
        append(Hxl, Hyu, HL)
    ;
        L = Bound3,
        append(Hxu, Hyl, HL)
    ;
        append(Hxu, Hyu, HL)
    ),
    !.

calc_product_bounds(XL, XU, YL, YU, real, Hxl, Hxu, Hyl, Hyu, U, HU, L, HL) :-
    evaluate_rational_expression(XL * YL, Bound1),
    evaluate_rational_expression(XL * YU, Bound2),
    evaluate_rational_expression(XU * YL, Bound3),
    evaluate_rational_expression(XU * YU, Bound4),
    choose_max([Bound1, Bound2, Bound3, Bound4], U),
    choose_min([Bound1, Bound2, Bound3, Bound4], L),
    (
        U = Bound1,
        append(Hxl, Hyl, HU)
    ;
        U = Bound2,
        append(Hxl, Hyu, HU)
    ;
        U = Bound3,
        append(Hxu, Hyl, HU)
    ;
        append(Hxu, Hyu, HU)
    ),
    !,
    (
        L = Bound1,
        append(Hxl, Hyl, HL)
    ;
        L = Bound2,
        append(Hxl, Hyu, HL)
    ;
        L = Bound3,
        append(Hxu, Hyl, HL)
    ;
        append(Hxu, Hyu, HL)
    ),
    !.

%===============================================================================
% choose_max(+List, -Max).
%-------------------------------------------------------------------------------
% Return maximum number from List (of at least 2 elements).
%===============================================================================

choose_max([A, B], Max) :-
    simplify(A >= B, true),
    !,
    Max = A.
choose_max([A, B], Max) :-
    simplify(A < B, true),
    !,
    Max = B.
choose_max([H|T], Max) :-
    choose_max(T, MaxT),
    !,
    (
        simplify(H > MaxT, true),
        Max = H
    ;
        Max = MaxT
    ),
    !.

%===============================================================================
% choose_max(+List, -Max).
%-------------------------------------------------------------------------------
% Return minimum number from List (of at least 2 elements).
%===============================================================================

choose_min([A, B], Min) :-
    simplify(A >= B, true),
    !,
    Min = B.
choose_min([A, B], Min) :-
    simplify(A < B, true),
    !,
    Min = A.
choose_min([H|T], Min) :-
    choose_min(T, MinT),
    !,
    (
        simplify(H < MinT, true),
        Min = H
    ;
        Min = MinT
    ),
    !.

%===============================================================================
% find_largest_integer_literal_below(+Rational, -Integer).
%-------------------------------------------------------------------------------
% Return the largest integer below Rational [N.B. This is only ever called
% when Rational is a strict rational number, not an integer literal.]
%===============================================================================

find_largest_integer_literal_below(A/B, N) :-
    int(A), A >= 0,
    int(B), B > 0,
    !,
    N iss A div B.
find_largest_integer_literal_below(-(A/B), N) :-
    int(A), A > 0,
    int(B), B > 0,
    !,
    N iss -(A div B) - 1.

%===============================================================================
% find_smallest_integer_literal_above(+Rational, -Integer).
%-------------------------------------------------------------------------------
% Return the smallest integer above Rational [N.B. This is only ever called
% when Rational is a strict rational number, not an integer literal.]
%===============================================================================

find_smallest_integer_literal_above(R, N) :-
    find_largest_integer_literal_below(R, M),
    !,
    N iss M + 1.

%===============================================================================
% safe_deduce(+Goal, +Type, -Hyps).
%-------------------------------------------------------------------------------
% Call deduce, but via try_to_infer.  [This is safe, because no infinite
% cycling is possible: try_to_infer imposes a recursive-call depth limit,
% and buffers and restores the used(_) facts as it goes.]
%===============================================================================

safe_deduce(Goal, _, Hyps) :-
    infer_subgoal(Goal, Hyps),
    !.

%===============================================================================
% strict_deduce(+Goal, +Type, -Hyps).
%-------------------------------------------------------------------------------
% Call infer_subgoal, but temporarily turn off the new (deduction and
% numeric) strategies, to prevent unbounded recursion.
%===============================================================================

strict_deduce(Goal, _, Hyps) :-
    inhibit_new_strategies(NeedToRestore),
    !,
    (
        infer_subgoal(Goal, Hyps),
        Success = true
    ;
        Success = fail
    ),
    !,
    restore_new_strategies(NeedToRestore),
    !,
    call(Success).

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

inhibit_new_strategies(true) :- retract(allow_new_strategies).

inhibit_new_strategies(false).

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

restore_new_strategies(true) :- asserta(allow_new_strategies).

restore_new_strategies(false).

%===============================================================================
% i_am_using_rule(RuleName).
%-------------------------------------------------------------------------------
% Always succeeds once. Allows the 'name' (e.g. gt_trans_1) of the rule
% being used to be observed when Simplifier is run in trace mode.  Could
% also be used if more information on the strategy were to be recorded,
% e.g.  for inclusion in the proof log -- this is not done at present.
%===============================================================================

i_am_using_rule(_).

%===============================================================================
% try_new_logic_strategies(+Formula, -Hs).
%-------------------------------------------------------------------------------
% Sucessfull where boolean typed Formula can be proved, reporting used
% hypotheses as Hs.
%===============================================================================

% Implication
%------------

try_new_logic_strategies(Left -> Right, Hs) :-        /*[[Logic_1]]*/
    fetch_conjunction_list(Left, Ls),
    fetch_conjunction_list(Right, Rs),
    get_hyp(HLeft -> HRight, _, N),
    fetch_conjunction_list(HLeft, HLs),
    fetch_conjunction_list(HRight, HRs),
    establish_implies(Ls, HLs, H1),
    establish_implies(HRs, Rs, H2),
    !,
    append(H1, [N|H2], Hyps),
    sort(Hyps, Hs).

% Equivalence
%------------

try_new_logic_strategies(Left <-> Right, Hs) :-                 /*[[Logic_2]]*/
        (                                       /* Transitivity of <-> and equivalence */
           get_hyp(Left <-> Int, x, N)            /* of A <-> B and (not A) <-> (not B). */
        ;
           get_hyp(Int <-> Left, x, N)
        ;
           get_hyp((not Left) <-> NegIntL, x, N),
           simplify(not NegIntL, Int)
        ;
           get_hyp(NegIntL <-> (not Left), x, N),
           simplify(not NegIntL, Int)
        ),
        (
           get_hyp(Right <-> Int, x, M)
        ;
           get_hyp(Int <-> Right, x, M)
        ;
           get_hyp((not Right) <-> NegIntR, x, N),
           simplify(not NegIntR, Int)
        ;
           get_hyp(NegIntR <-> (not Right), x, N),
           simplify(not NegIntR, Int)
        ),
        !,
        sort([M,N], Hs).

try_new_logic_strategies(Left <-> Right, Hs) :-       /*[[Logic_3]]*/
    fetch_conjunction_list(Left, Ls),
    fetch_conjunction_list(Right, Rs),
    establish_implies(Ls, Rs, H1),     /* so the Lefts imply the Rights...  */
    establish_implies(Rs, Ls, H2),     /* ...and the Rights imply the Lefts */
    !,
    append(H1, H2, Hyps),
    sort(Hyps, Hs).

%===============================================================================
% fetch_conjunction_list(+Formula, List).
%-------------------------------------------------------------------------------
% Turn a Formula of the form:
% A and B and (C1 or C2) and (D and E)
% Into a list:
% [A, B, (C1 or C2), D, E].
% (Order is not important.)
%===============================================================================

fetch_conjunction_list(A and B, List) :-
    fetch_conjunction_list(A, As),
    fetch_conjunction_list(B, Bs),
    !,
    append(As, Bs, List).

fetch_conjunction_list(not (A or B), List) :-   /* N.B.  This is (not A) and (not B) */
    fetch_conjunction_list(not A, As),
    fetch_conjunction_list(not B, Bs),
    !,
    append(As, Bs, List).

fetch_conjunction_list(A, [A]).

%===============================================================================
% establish_implies(+LHS, +RHS, -Hs).
%-------------------------------------------------------------------------------
% Succeeds if the goals in list RHS all follow from the formulae in LHS,
% together with the existing hypotheses.  (Any hypotheses needed to
% establish elements of RHS are included within the list of hypothesis
% numbers Hs which is also returned.)
%===============================================================================

establish_implies(List, [Goal|Goals], Hs) :-
    establish_atomic_implies(List, Goal, H1),
    !,
    establish_implies(List, Goals, H2),
    !,
    append(H1, H2, Hs).
establish_implies(_, [], []).

%===============================================================================
% establish_atomic_implies(+List, +Goal, -Hs).
%-------------------------------------------------------------------------------
% Succeeds if the single goal Goal follows from the formulae in list List,
% together with the existing hypotheses.  (The numbers of any hypotheses
% which are used to infer Goal are included in the returned value of list
% Hs.)
%===============================================================================

establish_atomic_implies(List, Goal, []) :-
    is_in(Goal, List),      /* If Goal is in List, it follows from it */
    !.

establish_atomic_implies(_, Goal, Hs) :-
    safe_deduce_in_logical_strategies(Goal, Hs),    /* If Goal holds anyway, it follows */
    !.

establish_atomic_implies(List, _, Hs) :-
    find_false_element_in(List, Hs),
    !.            /* If List contains a contradiction, Goal follows */

establish_atomic_implies(List, Goal, []) :-
    is_relational_expression(Goal, Type),   /* Type: integer, real or enum */
    find_rel_exp_in(List, Exp, Type),       /* Find something that may imply it */
    establish_implication(Exp, Goal, Type), /* and check via appropriate mechanism */
    !.

establish_atomic_implies(List, B1 or B2, Hs) :- /* Disjunction with two elements   */
    is_in(A1 or A2, List),        /* and similar disjunction in List */
    (
        establish_atomic_implies(A1, B1, H1),   /* If (A1->B1) and (A2->B2), then */
        establish_atomic_implies(A2, B2, H2)    /* (A1 or A2) -> (B1 or B2).      */
    ;
        establish_atomic_implies(A1, B2, H1),   /* If (A1->B2) and (A2->B1), then */
        establish_atomic_implies(A2, B1, H2)    /* (A1 or A2) -> (B1 or B2).      */
    ),
    !,
    append(H1, H2, Hs).

establish_atomic_implies(List, B1 or B2 or B3, Hs) :-   /* 3 elements [N.B. ignore 4+]       */
    is_in(A1 or A2 or A3, List),          /* and 3-element disjunction in List */
    (
        establish_atomic_implies(A1, B1, H1),   /* N.B. 1,2,3 and 2,1,3 patterns */
        establish_atomic_implies(A2, B3, H2),   /* are already covered by the 2- */
        establish_atomic_implies(A3, B2, H3)    /* element rule above in effect. */
    ;
        establish_atomic_implies(A1, B2, H1),
        establish_atomic_implies(A2, B3, H2),
        establish_atomic_implies(A3, B1, H3)
    ;
        establish_atomic_implies(A1, B3, H1),
        (
            establish_atomic_implies(A2, B2, H2),
            establish_atomic_implies(A3, B1, H3)
        ;
            establish_atomic_implies(A2, B1, H2),
            establish_atomic_implies(A3, B2, H3)
        )
    ),
    !,
    append(H2, H3, HL),
    append(H1, HL, Hs).

%===============================================================================
% is_relational_expression(+XopY, +Type).
%-------------------------------------------------------------------------------
% Succeed if XopY is a relational expression of type Type.
%===============================================================================

is_relational_expression(X=Y, Type)  :- find_mutual_types(X, Y, Type).
is_relational_expression(X<>Y, Type) :- find_mutual_types(X, Y, Type).
is_relational_expression(X>=Y, Type) :- find_mutual_types(X, Y, Type).
is_relational_expression(X<=Y, Type) :- find_mutual_types(X, Y, Type).
is_relational_expression(X>Y, Type)  :- find_mutual_types(X, Y, Type).
is_relational_expression(X<Y, Type)  :- find_mutual_types(X, Y, Type).

%===============================================================================
% find_rel_exp_in(+List, +RelExp, +Type)
%-------------------------------------------------------------------------------
% Search through list looking for candidates.
%===============================================================================

find_rel_exp_in([H|_T], H, Type) :- is_relational_expression(H, Type).
find_rel_exp_in([_H|T], X, Type) :- !, find_rel_exp_in(T, X, Type).

%===============================================================================
% find_false_element_in(+List, -Hs).
%-------------------------------------------------------------------------------
% Look for a contradiction in List.  If any hypotheses are needed to
% establish this, return the numbers of the hypotheses used in Hs.
%===============================================================================

find_false_element_in([false|_], []).      /* explicit contradiction present */

find_false_element_in([H|_T], Hs) :-
    simplify(not H, NotH),
    safe_deduce_in_logical_strategies(NotH, Hs),    /* H in List, (not H) follows from hypotheses */
    !.

find_false_element_in([_H|T], Hs) :-
    !,
    find_false_element_in(T, Hs).      /* otherwise, look at the tail of the list */

%===============================================================================
% safe_deduce_in_logical_strategies(+Goal, -Hyps).
%-------------------------------------------------------------------------------
% Allow a single use of the new numeric strategies within the new
% try_new_logic_strategies predicate.
%===============================================================================

safe_deduce_in_logical_strategies(Goal, Hyps):-
    safe_deduce_in_logical_strategies_x(Goal, Hyps),
    !.

safe_deduce_in_logical_strategies(Goal, Hyps):-
    is_inequality(Goal, Left, Right, _Op),
    !,
    find_mutual_types(Left, Right, Type),
    try_new_numeric_strategies(Goal, Type, Hyps),
    !.

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

% Note that:
% 1: infer_subgoal/2 may be called from safe_deduce_in_logical_strategies/2.
% 2: safe_deduce_in_logical_strategies/2 may be called from try_new_logic_strategies/2.
% 3: try_new_logic_strategies/2 may be called from do_infer/2.
% 4: do_infer/2 may be called from infer_subgoal/2.
%
% To guard against the potentially infinite recursion, we inhibit use of
% the new logic strategies. This cuts the loop by preventing call '3' in
% the call to infer_subgoal/2 below.
safe_deduce_in_logical_strategies_x(Goal, Hyps):-
    inhibit_new_strategies(NeedToRestore),
    !,
    safe_deduce_in_logical_strategies_xx(Goal, Hyps, NeedToRestore),
    !.

% Succeed, if can successfully infer subgoal.
safe_deduce_in_logical_strategies_xx(Goal, Hyps, NeedToRestore):-
    infer_subgoal(Goal, Hyps),
    restore_new_strategies(NeedToRestore),
    !.

% From above, can can not successfully infer subgoal. So, fail.
safe_deduce_in_logical_strategies_xx(_Goal, _Hyps, NeedToRestore):-
    restore_new_strategies(NeedToRestore),
    !,
    fail.

%===============================================================================
% establish_implication(+Antecedent, -Consequent, +Type).
%-------------------------------------------------------------------------------
% Given Antecedent, return alternative consequents that may be implied as
% Consequent.
%
% Note that the third argument, 'Type', is not currently used, but is
% included to allow for possible future extension to add extra Integer- or
% Real-specific rules.
%
% Further, note that we don't need an establish_implication(Goal, Goal, _)
% clause: identical goals already covered by establish_atomic_implies,
% which calls this predicate.)
%===============================================================================

% (1): simple permutation cases
%------------------------------

establish_implication(X=Y,  Y=X,  _).      /* (a) X=Y  -> Y=X  */
establish_implication(X<>Y, Y<>X, _).      /* (b) X<>Y -> Y<>X */
establish_implication(X>=Y, Y<=X, _).      /* (c) X>=Y -> Y<=X */
establish_implication(X<=Y, Y>=X, _).      /* (d) X<=Y -> Y>=X */
establish_implication(X>Y,  Y<X,  _).      /* (e) X>Y  -> Y<X  */
establish_implication(X<Y,  Y>X,  _).      /* (f) X<Y  -> Y>X  */


% (2): simple 'weakening' cases
%------------------------------

establish_implication(X=Y,  X>=Y, _).      /* (a) X=Y  -> X>=Y */
establish_implication(X=Y,  X<=Y, _).      /* (b) X=Y  -> X<=Y */
establish_implication(X=Y,  Y>=X, _).      /* (c) X=Y  -> Y>=X */
establish_implication(X=Y,  Y<=X, _).      /* (d) X=Y  -> Y<=X */

establish_implication(X>Y,  X>=Y, _).      /* (e) X>Y  -> X>=Y */
establish_implication(X>Y,  Y<=X, _).      /* (f) X>Y  -> Y<=X */
establish_implication(X>Y,  X<>Y, _).      /* (g) X>Y  -> X<>Y */
establish_implication(X>Y,  Y<>X, _).      /* (h) X>Y  -> Y<>X */

establish_implication(X<Y,  X<=Y, _).      /* (i) X<Y  -> X<=Y */
establish_implication(X<Y,  Y>=X, _).      /* (j) X<Y  -> Y>=X */
establish_implication(X<Y,  X<>Y, _).      /* (k) X<Y  -> X<>Y */
establish_implication(X<Y,  Y<>X, _).      /* (l) X<Y  -> Y<>X */


% (3): X=E1 and E1 relop E2
%--------------------------

% Cases:
% (3)(a)[1] (X=E1 and E1=E2)  -> X=E2  :  covered by: unit & (1)(a)
% (3)(a)[2] (X=E1 and E1=E2)  -> X>=E2 :  covered by: (2)(a)..(2)(d)
% (3)(a)[3] (X=E1 and E1=E2)  -> X<=E2 :  covered by: (2)(a)..(2)(d)
% (3)(b)    (X=E1 and E1>=E2) -> X>=E2 :  implemented below
% (3)(c)    (X=E1 and E1<=E2) -> X<=E2 :  implemented below
% (3)(d)[1] (X=E1 and E1>E2)  -> X>E2  :  implemented below
% (3)(d)[2] (X=E1 and E1>E2)  -> X>=E2 :  subsumed by (3)(b)
% (3)(d)[3] (X=E1 and E1>E2)  -> X<>E2 :  subsumed by (3)(f)
% (3)(e)[1] (X=E1 and E1<E2)  -> X<E2  :  implemented below
% (3)(e)[2] (X=E1 and E1<E2)  -> X<=E2 :  subsumed by (3)(c)
% (3)(e)[3] (X=E1 and E1<E2)  -> X<>E2 :  subsumed by (3)(f)
% (3)(f)    (X=E1 and E1<>E2) -> X<>E2 :  implemented below.

% (3)(a): cases omitted, as above.

% (3)(b): (X=E1 and E1>=E2) -> X>=E2

establish_implication(X=E1, X>=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(X=E1, E2<=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(E1=X, X>=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(E1=X, E2<=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

% (3)(c): (X=E1 and E1<=E2) -> X<=E2

establish_implication(X=E1, X<=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(X=E1, E2>=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(E1=X, X<=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(E1=X, E2>=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

% (3)(d)[1]: (X=E1 and E1>E2)  -> X>E2

establish_implication(X=E1, X>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).

establish_implication(X=E1, E2<X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).

establish_implication(E1=X, X>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).

establish_implication(E1=X, E2<X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).

% (3)(d)[2]: (X=E1 and E1>E2)  -> X>=E2: subsumed by (3)(b)

% (3)(d)[3]: (X=E1 and E1>E2)  -> X<>E2: subsumed by (3)(f)

% (3)(e)[1]: (X=E1 and E1<E2)  -> X<E2

establish_implication(X=E1, X<E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

establish_implication(X=E1, E2>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

establish_implication(E1=X, X<E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

establish_implication(E1=X, E2>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

% (3)(e)[2]: (X=E1 and E1<E2)  -> X<=E2: subsumed by (3)(c)

% (3)(e)[3]: (X=E1 and E1<E2)  -> X<>E2: subsumed by (3)(f)

% (3)(f): (X=E1 and E1<>E2) -> X<>E2

establish_implication(X=E1, X<>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<>E2, true).

establish_implication(X=E1, E2<>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<>E2, true).

establish_implication(E1=X, X<>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<>E2, true).
establish_implication(E1=X, E2<>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<>E2, true).

% (4): X>=E1 and E1 relop E2
%---------------------------

% Cases:
% (4)(a)    (X>=E1 and E1=E2)  -> X>=E2 :  covered by: unit & (1)(c)..(1)(d)
% (4)(b)    (X>=E1 and E1>=E2) -> X>=E2 :  implemented below
% (4)(c)    (X>=E1 and E1<=E2) -> ----- :  doesn't imply anything
% (4)(d)[1] (X>=E1 and E1>E2)  -> X>E2  :  implemented below
% (4)(d)[2] (X>=E1 and E1>E2)  -> X>=E2 :  subsumed by (4)(b)
% (4)(d)[3] (X>=E1 and E1>E2)  -> X<>E2 :  implemented below
% (4)(e)    (X>=E1 and E1<E2)  -> ----- :  doesn't imply anything
% (4)(f)    (X>=E1 and E1<>E2) -> ----- :  doesn't imply anything.

% (4)(a): cases omitted, as above.

% (4)(b): (X>=E1 and E1>=E2) -> X>=E2

establish_implication(X>=E1, X>=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).
establish_implication(X>=E1, E2<=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).
establish_implication(E1<=X, X>=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).
establish_implication(E1<=X, E2<=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

% (4)(c): (X>=E1 and E1<=E2): omitted

% (4)(d)[1]: (X>=E1 and E1>E2)  -> X>E2

establish_implication(X>=E1, X>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).
establish_implication(X>=E1, E2<X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).
establish_implication(E1<=X, X>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).
establish_implication(E1<=X, E2<X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).

% (4)(d)[2]: (X>=E1 and E1>E2)  -> X>=E2: subsumed by (4)(b)

% (4)(d)[3]: (X>=E1 and E1>E2)  -> X<>E2

establish_implication(X>=E1, X<>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).

establish_implication(X>=E1, E2<>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).

establish_implication(E1<=X, X<>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).

establish_implication(E1<=X, E2<>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>E2, true).

% (4)(e): (X>=E1 and E1<E2): omitted

% (4)(f): (X>=E1 and E1<>E2): omitted

% (5): X<=E1 and E1 relop E2
%---------------------------

% Cases:
% (5)(a)    (X<=E1 and E1=E2)  -> X<=E2 :  covered by: unit & (1)(c)..(1)(d)
% (5)(b)    (X<=E1 and E1>=E2) -> ----- :  doesn't imply anything
% (5)(c)    (X<=E1 and E1<=E2) -> X<=E2 :  implemented below
% (5)(d)    (X<=E1 and E1>E2)  -> ----- :  doesn't imply anything
% (5)(e)[1] (X<=E1 and E1<E2)  -> X<E2  :  implemented below
% (5)(e)[2] (X<=E1 and E1<E2)  -> X<=E2 :  subsumed by (5)(c)
% (5)(e)[3] (X<=E1 and E1<E2)  -> X<>E2 :  implemented below
% (5)(f)    (X<=E1 and E1<>E2) -> ----- :  doesn't imply anything.

% (5)(a): cases omitted, as above.

% (5)(b): (X<=E1 and E1>=E2): omitted

% (5)(c): (X<=E1 and E1<=E2) -> X<=E2

establish_implication(X<=E1, X<=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(X<=E1, E2>=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(E1>=X, X<=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(E1>=X, E2>=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

% (5)(d): (X<=E1 and E1>E2): omitted

% (5)(e)[1]: (X<=E1 and E1<E2)  -> X<E2

establish_implication(X<=E1, X<E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

establish_implication(X<=E1, E2>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

establish_implication(E1>=X, X<E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

establish_implication(E1>=X, E2>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

% (5)(e)[2]: (X<=E1 and E1<E2)  -> X<=E2: subsumed by (5)(c)

% (5)(e)[3]: (X<=E1 and E1<E2)  -> X<>E2

establish_implication(X<=E1, X<>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

establish_implication(X<=E1, E2<>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

establish_implication(E1>=X, X<>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

establish_implication(E1>=X, E2<>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<E2, true).

% (5)(f): (X<=E1 and E1<>E2): omitted

% (6): X>E1 and E1 relop E2
%--------------------------

% Cases:
% (6)(a)[1] (X>E1 and E1=E2)  -> X>E2  :  covered by: unit & (1)(e)..(1)(f)
% (6)(a)[2] (X>E1 and E1=E2)  -> X>=E2 :  covered by: (2)(e)..(2)(f) & (2)(i)..(2)(j)
% (6)(a)[3] (X>E1 and E1=E2)  -> X<>E2 :  covered by: (2)(g)..(2)(h) & (2)(k)..(2)(l)
% (6)(b)[1] (X>E1 and E1>=E2) -> X>E2  :  implemented below
% (6)(b)[2] (X>E1 and E1>=E2) -> X>=E2 :  implemented below
% (6)(b)[3] (X>E1 and E1>=E2) -> X<>E2 :  implemented below
% (6)(c)    (X>E1 and E1<=E2) -> ----- :  doesn't imply anything
% (6)(d)[1] (X>E1 and E1>E2)  -> X>E2  :  subsumed by (6)(b)(1)
% (6)(d)[2] (X>E1 and E1>E2)  -> X>=E2 :  subsumed by (6)(b)(2)
% (6)(d)[3] (X>E1 and E1>E2)  -> X<>E2 :  subsumed by (6)(b)(3)
% (6)(e)    (X>E1 and E1<E2)  -> ----- :  doesn't imply anything
% (6)(f)    (X>E1 and E1<>E2) -> ----- :  doesn't imply anything.

% (6)(a): cases omitted, as above.

% (6)(b)[1]: (X>E1 and E1>=E2) -> X>E2

establish_implication(X>E1, X>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(X>E1, E2<X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(E1<X, X>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(E1<X, E2<X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

% (6)(b)[2]: (X>E1 and E1>=E2) -> X>=E2

establish_implication(X>E1, X>=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(X>E1, E2<=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(E1<X, X>=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(E1<X, E2<=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

/* (6)(b)[3] (X>E1 and E1>=E2) -> X<>E2 */
establish_implication(X>E1, X<>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(X>E1, E2<>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(E1<X, X<>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

establish_implication(E1<X, E2<>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1>=E2, true).

%(6)(c): (X>E1 and E1<=E2): omitted

%(6)(d)[1]: (X>E1 and E1>E2)  -> X>E2: subsumed by (6)(b)(1)

%(6)(d)[2]: (X>E1 and E1>E2)  -> X>=E2: subsumed by (6)(b)(2)

%(6)(d)[3]: (X>E1 and E1>E2)  -> X<>E2: subsumed by (6)(b)(3)

%(6)(e): (X>E1 and E1<E2): omitted

%(6)(f): (X>E1 and E1<>E2): omitted


% (7): X<E1 and E1 relop E2
%--------------------------

% Cases:
% (7)(a)[1] (X<E1 and E1=E2)  -> X<E2  :  covered by: unit & (1)(e)..(1)(f)
% (7)(a)[2] (X<E1 and E1=E2)  -> X<=E2 :  covered by: (2)(e)..(2)(f) & (2)(i)..(2)(j)
% (7)(a)[3] (X<E1 and E1=E2)  -> X<>E2 :  covered by: (2)(e)..(2)(f) & (2)(i)..(2)(j)
% (7)(b)    (X<E1 and E1>=E2) -> ----- :  doesn't imply anything
% (7)(c)[1] (X<E1 and E1<=E2) -> X<E2  :  implemented below
% (7)(c)[2] (X<E1 and E1<=E2) -> X<=E2 :  implemented below
% (7)(c)[3] (X<E1 and E1<=E2) -> X<>E2 :  implemented below
% (7)(d)    (X<E1 and E1>E2)  -> ----- :  doesn't imply anything
% (7)(e)[1] (X<E1 and E1<E2)  -> X<E2  :  subsumed by (7)(c)(1)
% (7)(e)[2] (X<E1 and E1<E2)  -> X<=E2 :  subsumed by (7)(c)(2)
% (7)(e)[3] (X<E1 and E1<E2)  -> X<>E2 :  subsumed by (7)(c)(3)
% (7)(f)    (X<E1 and E1<>E2) -> ----- :  doesn't imply anything.

% (7)(c)[1]: (X<E1 and E1<=E2) -> X<E2
establish_implication(X<E1, X<E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(X<E1, E2>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(E1>X, X<E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(E1>X, E2>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

% (7)(c)[2]: (X<E1 and E1<=E2) -> X<=E2
establish_implication(X<E1, X<=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(X<E1, E2>=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(E1>X, X<=E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(E1>X, E2>=X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

% (7)(c)[3]: (X<E1 and E1<=E2) -> X<>E2

establish_implication(X<E1, X<>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(X<E1, E2<>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(E1>X, X<>E2, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

establish_implication(E1>X, E2<>X, _) :-
    int_or_enum_lit(E1, T),
    int_or_enum_lit(E2, T),
    !,
    simplify(E1<=E2, true).

% (7)(d): (X<E1 and E1>E2): omitted

% (7)(e)[1]: (X<E1 and E1<E2)  -> X<E2: subsumed by (7)(c)(1)

% (7)(e)[2]: (X<E1 and E1<E2)  -> X<=E2: subsumed by (7)(c)(2)

% (7)(e)[3]: (X<E1 and E1<E2)  -> X<>E2: subsumed by (7)(c)(3)

% (7)(f): (X<E1 and E1<>E2): omitted


% (8): X<>E1 and E1 relop E2
%---------------------------

% Cases:
% (8)(a)    (X<>E1 and E1=E2)  -> X<>E2 :  covered by: unit & (1)(b)
% (8)(b)    (X<>E1 and E1>=E2) -> ----- :  doesn't imply anything
% (8)(c)    (X<>E1 and E1<=E2) -> ----- :  doesn't imply anything
% (8)(d)    (X<>E1 and E1>E2)  -> ----- :  doesn't imply anything
% (8)(e)    (X<>E1 and E1<E2)  -> ----- :  doesn't imply anything
% (8)(f)    (X<>E1 and E1<>E2) -> ----- :  doesn't imply anything.
% (So no clauses at all for these combinations.)

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