% -----------------------------------------------------------------------------
%  (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
%-------------------------------------------------------------------------------
% defines the (large) predicate simplify(Old, New) which attempts to
% simplify any expression given to it.
%###############################################################################

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

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

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

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

%===============================================================================
% simplify(+In, -Out).
%-------------------------------------------------------------------------------
% Simplify expression In to get Out.
%===============================================================================

% Simplify expressions of the form 'not(A)' where possible
%=========================================================

% NOT1
simplify(not(true),false) :- !.

% NOT2
simplify(not(false),true) :- !.

% NOT3
simplify(not(not(X)),A) :- simplify(X,A), !.

% NOT4
simplify((not X),B) :-
    simplify(X,A),
    (
        A=true,
        B=false
    ;
        A=false,
        B=true
    ;
        A=(not B)    /* i.e. if A is "not Something", return the Something */
    ;
        B=(not A)
    ), !.

% Simplify expressions of the form 'A and B' where possible
%==========================================================

% AND1
simplify(false and _X,false) :- !.

% AND2
simplify(_X and false,false) :- !.

% AND3
simplify(X and (not X),false) :- !.

% AND4
simplify((not X) and X,false) :- !.

% AND5
simplify(true and X,Y) :- simplify(X,Y), !.

% AND6
simplify(X and true,Y) :- simplify(X,Y), !.

% AND7
simplify(X and X,Y) :- simplify(X,Y), !.

% AND8
simplify(X and Y,Z) :-
    simplify(X,A),
    (
        A=true,
        simplify(Y,Z)
    ;
        A=false,
        Z=A
    ;
        simplify(Y,B),
        (
            (
                B=true,
                Z=A
            ;
                B=false,
                Z=B
            )
        ;
            (
                A=(not B)
            ;
                A=(not Y)
            ;
                B=(not A)
            ;
                B=(not X)
            ),
            Z=false
        ;
            A=B,
            Z=A
        ;
            Z=(A and B)
        )
    ), !.

% Simplify expressions of the form 'A or B' where possible
%=========================================================

% OR1
simplify(true or _X,true) :- !.

% OR2
simplify(_X or true,true) :- !.

% OR3
simplify(X or (not X),true) :- !.

% OR4
simplify((not X) or X,true) :- !.

% OR5
simplify(false or X,Y) :- simplify(X,Y), !.

% OR6
simplify(X or false,Y) :- simplify(X,Y), !.

% OR7
simplify(X or X,Y) :- simplify(X,Y), !.

% OR8
simplify(X or Y,Z) :-
    simplify(X,A),
    (
        A=true,
        Z=A
    ;
        A=false,
        simplify(Y,Z)
    ;
        simplify(Y,B),
        (
            B=true,
            Z=B
        ;
            B=false,
            Z=A
        ;
            (
                A=(not B)
            ;
                A=(not Y)
            ;
                B=(not A)
            ;
                B=(not X)
            ),
            Z=true
        ;
            A=B,
            Z=A
        ;
            Z=(A or B)
        )
    ), !.

% Simplify expressions of the form 'A -> B' where possible
%=========================================================

% IMP1
simplify(false->_X,true) :- !.

% IMP2
simplify(_X->true,true) :- !.

% IMP3
simplify(X->X,true) :- !.

% IMP4
simplify(true->X,Y) :- simplify(X,Y), !.

% IMP5
simplify(X->false,A) :- simplify((not X),A), !.

% IMP6
simplify(X->(not X),Y) :- simplify((not X),Y), !.

% IMP7
simplify((not X)->X,Y) :- simplify(X,Y), !.

% IMP8
simplify(X -> (Y -> Z),A) :- simplify((X and Y) -> Z,A), !.

% IMP9
simplify(X->Y,Z) :-
    simplify(X,A),
    (
        A=true,
        simplify(Y,Z)
    ;
        A=false,
        Z=true
    ;
        simplify(Y,B),
        (
            B=true,
            Z=B
        ;
            B=false,
            simplify((not X),Z)
        ;
            (
                A=(not B)
            ;
                A=(not Y)
            ;
                B=(not A)
            ;
                B=(not X)
            ),
            Z=B
        ;
            A=B,
            Z=true
        ;
            Z=(A->B)
        )
    ), !.

% Simplify expressions of the form 'A <-> B' where possible
%==========================================================

% EQV1
simplify(X<->X,true) :- !.

% EQV2
simplify(X<->(not X),false) :- !.

% EQV3
simplify((not X)<->X,false) :- !.

% EQV4
simplify(X<->true,Y) :- simplify(X,Y), !.

% EQV5
simplify(true<->X,Y) :- simplify(X,Y), !.

% EQV6
simplify(X<->false,Y) :- simplify((not X),Y), !.

% EQV7
simplify(false<->X,Y) :- simplify((not X),Y), !.

% EQV8
simplify(X <-> (Y <-> Z),B) :-
    simplify(X <-> Y,A),
    simplify(A <-> Z,B), !.

% EQV9
simplify(X <-> Y <-> Z,B) :-
    simplify(Y <-> Z,A),
    (Y <-> Z)\=A,
    simplify(X <-> A,B), !.
% EQV10
simplify(X <-> Y <-> Z,B) :-
    simplify(X <-> Z,A),
    (X <-> Z)\=A,
    simplify(A <-> Y,B), !.
% EQV11
simplify(X<->Y,Z) :-
    simplify(X,A),
    (
        A=true,
        simplify(Y,Z)
    ;
        A=false,
        simplify((not Y),Z)
    ;
        simplify(Y,B),
        (
            B=true,
            Z=A
        ;
            B=false,
            simplify((not X),Z)
        ;
            (
                A=(not B)
            ;
                A=(not Y)
            ;
                B=(not A)
            ;
                B=(not X)
            ),
            Z=false
        ;
            A=B,
            Z=true
        ;
            Z=(A<->B)
        )
    ), !.

% Simplify 'for_all( )' exprs. where possible
%============================================

% ALL3
simplify(for_all(V:T,X),NEW) :-
    find_core_type(T, CT),
    (
        var_const(V, CT, _),
        STATE = dont_retract
    ;
        asserta(var_const(V, CT, temp)),
        STATE = retract
    ),
    simplify(X,Y),
    (
        Y = true,
        NEW = true
    ;
        Y = false,
        NEW = false
    ;
        uq_normalise(V, Y, NEWY),
        NEW = (for_all(V:T, NEWY))
    ),
    !,
    (
        STATE = dont_retract
    ;
        retract(var_const(V, CT, temp))
    ),
    !.

% Simplify 'for_some( )' exprs. where possible
%=============================================

% EXI3
simplify(for_some(V:T,X),NEW) :-
    find_core_type(T, CT),
    (
        var_const(V, CT, _),
        STATE = dont_retract
    ;
        asserta(var_const(V, CT, temp)),
        STATE = retract
    ),
    simplify(X,Y),
    (
        Y = true,
        NEW = true
    ;
        Y = false,
        NEW = false
    ;
        NEW = (for_some(V:T, Y))
    ),
    !,
    (
        STATE = dont_retract
    ;
        retract(var_const(V, CT, temp))
    ),
    !.

% Simplify set-type expressions where possible
%=============================================

% SET1
simplify(A \/ B, S) :-
    !,
    set_simplify(A \/ B, S),
    !.
% SET2
simplify(A \ B, S) :-
    !,
    set_simplify(A \ B, S),
    !.
% SET3
simplify(A /\ B, S) :-
    !,
    set_simplify(A /\ B, S),
    !.
% SET4
simplify((set A), S) :-
    !,
    set_simplify((set A), S),
    !.
% SET5
simplify(A subset_of B, S) :-
    !,
    set_simplify(A subset_of B, S),
    !.
% SET6
simplify(A strict_subset_of B, S) :-
    !,
    set_simplify(A strict_subset_of B, S),
    !.
% SET7
simplify(A in B, S) :-
    !,
    set_simplify(A in B, S),
    !.
% SET8
simplify(A not_in B, S) :-
    !,
    set_simplify(A not_in B, S),
    !.

% Simplify atomic formulae where possible
%========================================

% REL1
simplify(X=Y,Z) :-
    checktype(X,T),
    (
        type(T, set(_)),
        !,
        set_simplify(X=Y,Z)
    ;
        type(T,enumerated),
        !,
        enumerated_simplify(X=Y,Z)
    ;
        simplify(X,A),
        simplify(Y,B),
        (
            A=B,
            Z=true
        ;
            base_rational(A),
            (
                base_rational(B),
                A\=B,
                Z=false
            ;
                B=P+Q,
                (
                    base_rational(P),
                    simplify(A-P,R),
                    Z=(Q=R)
                ;
                    base_rational(Q),
                    simplify(A-Q,R),
                    Z=(P=R)
                )
            ;
                B=P-Q,
                (
                    base_rational(P),
                    simplify(P-A,R),
                    Z=(Q=R)
                ;
                    base_rational(Q),
                    simplify(A+Q,R),
                    Z=(P=R)
                )
            )
        ;
            base_rational(B),
            (
                A=P+Q,
                (
                    base_rational(P),
                    simplify(B-P,R),
                    Z=(Q=R)
                ;
                    base_rational(Q),
                    simplify(B-Q,R),
                    Z=(P=R)
                )
            ;
                A=P-Q,
                (
                    base_rational(P),
                    simplify(P-B,R),
                    Z=(Q=R)
                ;
                    base_rational(Q),
                    simplify(B+Q,R),
                    Z=(P=R)
                )
            )
        ;            /* handle A,B boolean */
            A=true,
            Z=B
        ;
            B=true,
            Z=A
        ;
            A=false,
            simplify(not B, Z)
        ;
            B=false,
            simplify(not A, Z)
        ;
            T=boolean,
            Z=(A<->B)
        ;
            Z=(A=B)
        )
    ), !.

% REL2
simplify(X>Y,Z) :-
    checktype(X,T),
    (
        type(T,enumerated),
        !,
        enumerated_simplify(X>Y,Z)
    ;
        simplify(X,A),
        simplify(Y,B),
        (
            base_rational(A),
            (
                base_rational(B),
                (
                    B less_than A,
                    Z=true
                ;
                    (
                        A=B
                    ;
                        A less_than B
                    ),
                    Z=false
                )
            ;
                B=P+Q,
                (
                    base_rational(P),
                    simplify(A-P,R),
                    Z=(Q<R)
                ;
                    base_rational(Q),
                    simplify(A-Q,R),
                    Z=(P<R)
                )
            ;
                B=P-Q,
                (
                    base_rational(P),
                    simplify(P-A,R),
                    Z=(Q>R)
                ;
                    base_rational(Q),
                    simplify(A+Q,R),
                    Z=(P<R)
                )
            )
        ;
            base_rational(B),
            (
                A=P+Q,
                (
                    base_rational(P),
                    simplify(B-P,R),
                    Z=(Q>R)
                ;
                    base_rational(Q),
                    simplify(B-Q,R),
                    Z=(P>R)
                )
            ;
                A=P-Q,
                (
                    base_rational(P),
                    simplify(P-B,R),
                    Z=(Q<R)
                ;
                    base_rational(Q),
                    simplify(B+Q,R),
                    Z=(P>R)
                )
            )
        ;
            Z=(A>B)
        )
    ), !.

% REL3
simplify(X<Y,Z) :-
    checktype(X,T),
    (
        type(T,enumerated),
        !,
        enumerated_simplify(X<Y,Z)
    ;
        simplify(X,A),
        simplify(Y,B),
        (
            base_rational(A),
            (
                base_rational(B),
                (
                    A less_than B,
                    Z=true
                ;
                    (
                        A=B
                    ;
                        B less_than A
                    ),
                    Z=false
                )
            ;
                B=P+Q,
                (
                    base_rational(P),
                    simplify(A-P,R),
                    Z=(Q>R)
                ;
                    base_rational(Q),
                    simplify(A-Q,R),
                    Z=(P>R)
                )
            ;
                B=P-Q,
                (
                    base_rational(P),
                    simplify(P-A,R),
                    Z=(Q<R)
                ;
                    base_rational(Q),
                    simplify(A+Q,R),
                    Z=(P>R)
                )
            )
        ;
            base_rational(B),
            (
                A=P+Q,
                (
                    base_rational(P),
                    simplify(B-P,R),
                    Z=(Q<R)
                ;
                    base_rational(Q),
                    simplify(B-Q,R),
                    Z=(P<R)
                )
            ;
                A=P-Q,
                (
                    base_rational(P),
                    simplify(P-B,R),
                    Z=(Q>R)
                ;
                    base_rational(Q),
                    simplify(B+Q,R),
                    Z=(P<R)
                )
            )
        ;
            Z=(A<B)
        )
    ), !.

% REL4
simplify(X<>Y,Z) :-
    checktype(X,T),
    (
        type(T, set(_)),
        !,
        set_simplify(X<>Y,Z)
    ;
        type(T,enumerated),
        !,
        enumerated_simplify(X<>Y,Z)
    ;
        simplify(X,A),
        simplify(Y,B),
        (
            A=B,
            Z=false
        ;
            base_rational(A),
            (
                base_rational(B),
                (
                    A\=B,
                    Z=true
                ;
                    A=B,
                    Z=true
                )
            ;
                B=P+Q,
                (
                    base_rational(P),
                    simplify(A-P,R),
                    Z=(Q<>R)
                ;
                    base_rational(Q),
                    simplify(A-Q,R),
                    Z=(P<>R)
                )
            ;
                B=P-Q,
                (
                    base_rational(P),
                    simplify(P-A,R),
                    Z=(Q<>R)
                ;
                    base_rational(Q),
                    simplify(A+Q,R),
                    Z=(P<>R)
                )
            )
        ;
            base_rational(B),
            (
                A=P+Q,
                (
                    base_rational(P),
                    simplify(B-P,R),
                    Z=(Q<>R)
                ;
                    base_rational(Q),
                    simplify(B-Q,R),
                    Z=(P<>R)
                )
            ;
                A=P-Q,
                (
                    base_rational(P),
                    simplify(P-B,R),
                    Z=(Q<>R)
                ;
                    base_rational(Q),
                    simplify(B+Q,R),
                    Z=(P<>R)
                )
            )
        ;            /* handle A,B boolean */
            A=true,
            simplify(not B, Z)
        ;
            B=true,
            simplify(not A, Z)
        ;
            A=false,
            Z=B
        ;
            B=false,
            Z=A
        ;
            T=boolean,
            Z=(not (A<->B))
        ;
            Z=(A<>B)
        )
    ), !.

% REL5
simplify(X<=Y,Z) :-
    checktype(X,T),
    (
        type(T,enumerated),
        !,
        enumerated_simplify(X<=Y,Z)
    ;
        simplify(X,A),
        simplify(Y,B),
        (
            A=B,
            Z=true
        ;
            base_rational(A),
            (
                base_rational(B),
                (
                    A less_than B,
                    Z=true
                ;
                    B less_than A,
                    Z=false
                )
            ;
                B=P+Q,
                (
                    base_rational(P),
                    simplify(A-P,R),
                    Z=(R<=Q)
                ;
                    base_rational(Q),
                    simplify(A-Q,R),
                    Z=(R<=P)
                )
            ;
                B=P-Q,
                (
                    base_rational(P),
                    simplify(P-A,R),
                    Z=(Q<=R)
                ;
                    base_rational(Q),
                    simplify(A+Q,R),
                    Z=(R<=P)
                )
            )
        ;
            base_rational(B),
            (
                A=P+Q,
                (
                    base_rational(P),
                    simplify(B-P,R),
                    Z=(Q<=R)
                ;
                    base_rational(Q),
                    simplify(B-Q,R),
                    Z=(P<=R)
                )
            ;
                A=P-Q,
                (
                    base_rational(P),
                    simplify(P-B,R),
                    Z=(R<=Q)
                ;
                    base_rational(Q),
                    simplify(B+Q,R),
                    Z=(P<=R)
                )
            )
        ;
            Z=(A<=B)
        )
    ), !.

% REL6
simplify(X>=Y,Z) :-
    checktype(X,T),
    (
        type(T,enumerated),
        !,
        enumerated_simplify(X>=Y,Z)
    ;
        simplify(X,A),
        simplify(Y,B),
        (
            A=B,
            Z=true
        ;
            base_rational(A),
            (
                base_rational(B),
                (
                    B less_than A,
                    Z=true
                ;
                    A less_than B,
                    Z=false
                )
            ;
                B=P+Q,
                (
                    base_rational(P),
                    simplify(A-P,R),
                    Z=(Q<=R)
                ;
                    base_rational(Q),
                    simplify(A-Q,R),
                    Z=(P<=R)
                )
            ;
                B=P-Q,
                (
                    base_rational(P),
                    simplify(P-A,R),
                    Z=(Q>=R)
                ;
                    base_rational(Q),
                    simplify(A+Q,R),
                    Z=(P<=R)
                )
            )
        ;
            base_rational(B),
            (
                A=P+Q,
                (
                    base_rational(P),
                    simplify(B-P,R),
                    Z=(Q>=R)
                ;
                    base_rational(Q),
                    simplify(B-Q,R),
                    Z=(P>=R)
                )
            ;
                A=P-Q,
                (
                    base_rational(P),
                    simplify(P-B,R),
                    Z=(Q<=R)
                ;
                    base_rational(Q),
                    simplify(B+Q,R),
                    Z=(P>=R)
                )
            )
        ;
            Z=(A>=B)
        )
    ), !.

% ODD
simplify(odd(X), ODD) :-
    simplify(X, NEWX),
    (
        signed_integer(NEWX),
        X1 is NEWX*NEWX,
        (
            X2 iss (X1 div 2)*2,
            X1 =:= X2,
            ODD=false
        ;
            X2 iss (X1 div 2)*2,
            X1 =\= X2,
            ODD=true
        )
    ;
        NEWX=A*B,
        simplify(odd(A), TFA),
        simplify(odd(B), TFB),
        (
            (
                TFA = false
            ;
                TFB = false
            ),
            ODD = false
        ;
            TFA = true,
            ODD = TFB
        ;
            TFB = true,
            ODD = TFA
        )
    ;
        (
            NEWX=A+B
        ;
            NEWX=A-B
        ),
        simplify(odd(A), TFA),
        simplify(odd(B), TFB),
        (
            TFA = true,
            (
                TFB = true,
                ODD = false
            ;
                TFB = false,
                ODD = true
            ;
                ODD = (not TFB)
            )
        ;
            TFA = false,
            ODD = TFB
        ;
            TFB = true,
            ODD = (not TFA)
        ;
            TFB = false,
            ODD = TFA
        )
    ;
        NEWX = (-A),
        simplify(odd(A), ODD)
    ;
        ODD = odd(NEWX)
    ), !.

% SQR1
simplify(sqr(abs(X)), SQR) :- simplify(X*X, SQR), !.

% SQR2
simplify(sqr(X), SQR) :- simplify(X*X, SQR), !.

% ABS
simplify(abs(X), ABS) :-
    simplify(X, NEWX),
    (
        signed_integer(NEWX),
        (
            NEWX >= 0,
            ABS = NEWX
        ;
            NEWX < 0,
            ABS is -NEWX
        )
    ;
        NEWX = A*A,
        ABS = NEWX
    ;
        NEWX = A*B,
        simplify(abs(A), ABSA),
        simplify(abs(B), ABSB),
        ABS = ABSA*ABSB
    ;
        NEWX = abs(_EXPR),
        ABS = NEWX
    ;
        ABS = abs(NEWX)
    ), !.


% Simplify bitwise functions (which support SPARK modular types) where possible
%==============================================================================

% BIT__AND
simplify(bit__and(X, Y), NEW) :-
    simplify(X, NEWX),
    simplify(Y, NEWY),
    (
        NEWX = 0,                       /* 0 /\ Y ==> 0 */
        NEW  = 0
    ;
        NEWY = 0,                       /* X /\ 0 ==> 0 */
        NEW  = 0
    ;
        NEWX = NEWY,                    /* X /\ X ==> X, provided X >= 0 */
        infer(NEWX >= 0),
        NEW  = NEWX
    ;
        signed_integer(NEWY),           /* X /\ 2**N-1 ==> ... */
        POW2 iss NEWY+1,
        is_a_power_of_2(POW2),          /* so Y = 2**N-1 */
        infer(NEWX >= 0),
        (
            infer(NEWX <= NEWY),         /*         ... ==> X, provided 0 <= X <= 2**N-1 */
            NEW = NEWX
        ;
            signed_integer(NEWX),        /*         ... ==> X mod 2**N, provided X >= 0 */
            NEW iss NEWX mod POW2
        ;
            NEW = NEWX mod POW2
        )
    ;
        signed_integer(NEWX),           /* 2**N-1 /\ Y ==> ... */
        POW2 iss NEWX+1,
        is_a_power_of_2(POW2),          /* so X = 2**N-1 */
        infer(NEWY >= 0),
        (
            infer(NEWY <= NEWX),         /*         ... ==> Y, provided 0 <= Y <= 2**N-1 */
            NEW = NEWY
        ;
            signed_integer(NEWY),        /*         ... ==> Y mod 2**N, provided Y >= 0 */
            NEW iss NEWY mod POW2
        ;
            NEW = NEWY mod POW2
        )
    ;
        (
            NEWY = POW2 - NEWX - 1,      /* X /\ 2**N-X-1 ==> 0, provided 0 <= X <= 2**N-1*/
            signed_integer(POW2),
            P2M1 iss POW2 - 1
        ;
            NEWY = P2M1 - NEWX,          /* X /\ 2**N-1-X ==> 0, provided 0 <= X <= 2**N-1 */
            signed_integer(P2M1),
            POW2 iss P2M1 + 1
        ),
        is_a_power_of_2(POW2),          /* i.e., and of X with its negation */
        infer(NEWX >= 0),
        infer(NEWX <= P2M1),
        NEW = 0
    ;
        (
            NEWX = POW2 - NEWY - 1,      /* 2**N-Y-1 /\ Y ==> 0, provided 0 <= Y <= 2**N-1 */
            signed_integer(POW2),
            P2M1 iss POW2 - 1
        ;
            NEWX = P2M1 - NEWY,          /* 2**N-1-Y /\ Y ==> 0, provided 0 <= Y <= 2**N-1 */
            signed_integer(P2M1),
            POW2 iss P2M1 + 1
        ),
        is_a_power_of_2(POW2),          /* i.e., and of Y with its negation */
        infer(NEWY >= 0),
        infer(NEWY <= P2M1),
        NEW = 0
    ;
        (
            NEWY = V * POW2
        ;
            NEWY = POW2 * V
        ),
        signed_integer(POW2),           /* X /\ Y * 2**N ==> 0, provided 0 <= X < 2**N */
        is_a_power_of_2(POW2),
        infer(NEWX >= 0),
        infer(V >= 0),
        infer(NEWX < POW2),
        NEW = 0
    ;
        (
            NEWX = V * POW2
        ;
            NEWX = POW2 * V
        ),
        signed_integer(POW2),           /* X * 2**N /\ Y ==> 0, provided 0 <= Y < 2**N */
        is_a_power_of_2(POW2),
        infer(NEWY >= 0),
        infer(V >= 0),
        infer(NEWY < POW2),
        NEW = 0
    ;
        signed_integer(NEWX),           /* evaluate, if both arguments are integers */
        signed_integer(NEWY),
        NEWX >= 0,
        NEWY >= 0,
        evaluate_bit_and(NEWX, NEWY, NEW)
    ;
        NEW = bit__and(NEWX, NEWY)      /* otherwise */
    ), !.

% BIT__OR
simplify(bit__or(X, Y), NEW) :-
    simplify(X, NEWX),
    simplify(Y, NEWY),
    (
        NEWX = 0,                       /* 0 \/ Y ==> Y, provided Y >= 0 */
        infer(NEWY >= 0),
        NEW  = NEWY
    ;
        NEWY = 0,                       /* X \/ 0 ==> X, provided X >= 0 */
        infer(NEWX >= 0),
        NEW  = NEWX
    ;
        NEWX = NEWY,                    /* X \/ X ==> X, provided X >= 0 */
        infer(NEWX >= 0),
        NEW  = NEWX
    ;
        (
            NEWY = POW2 - NEWX - 1,      /* X \/ 2**N-X-1 ==> 2**N-1, provided 0 <= X <= 2**N-1*/
            signed_integer(POW2),
            P2M1 iss POW2 - 1
        ;
            NEWY = P2M1 - NEWX,          /* X /\ 2**N-1-X ==> 2**N-1, provided 0 <= X <= 2**N-1 */
            signed_integer(P2M1),
            POW2 iss P2M1 + 1
        ),
        is_a_power_of_2(POW2),          /* i.e., or of X with its negation */
        infer(NEWX >= 0),
        infer(NEWX <= P2M1),
        NEW = P2M1
    ;
        (
            NEWX = POW2 - NEWY - 1,      /* 2**N-Y-1 \/ Y ==> 2**N-1, provided 0 <= Y <= 2**N-1 */
            signed_integer(POW2),
            P2M1 iss POW2 - 1
        ;
            NEWX = P2M1 - NEWY,          /* 2**N-1-Y \/ Y ==> 2**N-1, provided 0 <= Y <= 2**N-1 */
            signed_integer(P2M1),
            POW2 iss P2M1 + 1
        ),
        is_a_power_of_2(POW2),          /* i.e., or of Y with its negation */
        infer(NEWY >= 0),
        infer(NEWY <= P2M1),
        NEW = P2M1
    ;
        (
            NEWY = V * POW2
        ;
            NEWY = POW2 * V
        ),
        signed_integer(POW2),           /* X \/ Y*2**N ==> X+Y*2**N, provided 0 <= X < 2**N */
        is_a_power_of_2(POW2),
        infer(NEWX >= 0),
        infer(V >= 0),
        infer(NEWX < POW2),
        simplify(NEWX + POW2 * NEWY, NEW)
    ;
        (
            NEWX = V * POW2
        ;
            NEWX = POW2 * V
        ),
        signed_integer(POW2),           /* X*2**N \/ Y ==> Y+X*2**N, provided 0 <= Y < 2**N */
        is_a_power_of_2(POW2),
        infer(NEWY >= 0),
        infer(V >= 0),
        infer(NEWY < POW2),
        simplify(NEWY + POW2 * NEWX, NEW)
    ;
        signed_integer(NEWX),           /* evaluate, if both arguments are integers */
        signed_integer(NEWY),
        NEWX >= 0,
        NEWY >= 0,
        evaluate_bit_or(NEWX, NEWY, NEW)
    ;
        NEW = bit__or(NEWX, NEWY)       /* otherwise */
    ), !.

% BIT__XOR
simplify(bit__xor(X, Y), NEW) :-
    simplify(X, NEWX),
    simplify(Y, NEWY),
    (
        NEWX = 0,                       /* 0 xor Y ==> Y, provided Y >= 0 */
        infer(NEWY >= 0),
        NEW  = NEWY
    ;
        NEWY = 0,                       /* X xor 0 ==> X, provided X >= 0 */
        infer(NEWX >= 0),
        NEW  = NEWX
    ;
        NEWX = NEWY,                    /* X xor X ==> 0, provided X >= 0 */
        infer(NEWX >= 0),
        NEW  = 0
    ;
        (
            NEWY = POW2 - NEWX - 1,      /* X xor 2**N-X-1 ==> 2**N-1, provided 0 <= X <= 2**N-1*/
            signed_integer(POW2),
            P2M1 iss POW2 - 1
        ;
            NEWY = P2M1 - NEWX,          /* X xor 2**N-1-X ==> 2**N-1, provided 0 <= X <= 2**N-1 */
            signed_integer(P2M1),
            POW2 iss P2M1 + 1
        ),
        is_a_power_of_2(POW2),          /* i.e., xor of X with its negation */
        infer(NEWX >= 0),
        infer(NEWX <= P2M1),
        NEW = P2M1
    ;
        (
            NEWX = POW2 - NEWY - 1,      /* 2**N-Y-1 xor Y ==> 2**N-1, provided 0 <= Y <= 2**N-1 */
            signed_integer(POW2),
            P2M1 iss POW2 - 1
        ;
            NEWX = P2M1 - NEWY,          /* 2**N-1-Y xor Y ==> 2**N-1, provided 0 <= Y <= 2**N-1 */
            signed_integer(P2M1),
            POW2 iss P2M1 + 1
        ),
        is_a_power_of_2(POW2),          /* i.e., xor of Y with its negation */
        infer(NEWY >= 0),
        infer(NEWY <= P2M1),
        NEW = P2M1
    ;
        (
            NEWY = V * POW2
        ;
            NEWY = POW2 * V
        ),
        signed_integer(POW2),           /* X xor Y*2**N ==> X+Y*2**N, provided 0 <= X < 2**N */
        is_a_power_of_2(POW2),
        infer(NEWX >= 0),
        infer(V >= 0),
        infer(NEWX < POW2),
        simplify(NEWX + POW2 * NEWY, NEW)
    ;
        (
            NEWX = V * POW2
        ;
            NEWX = POW2 * V
        ),
        signed_integer(POW2),           /* X*2**N xor Y ==> Y+X*2**N, provided 0 <= Y < 2**N */
        is_a_power_of_2(POW2),
        infer(NEWY >= 0),
        infer(V >= 0),
        infer(NEWY < POW2),
        simplify(NEWY + POW2 * NEWX, NEW)
    ;
        signed_integer(NEWX),           /* evaluate, if both arguments are integers */
        signed_integer(NEWY),
        NEWX >= 0,
        NEWY >= 0,
        evaluate_bit_xor(NEWX, NEWY, NEW)
    ;
        NEW = bit__xor(NEWX, NEWY)      /* otherwise */
    ), !.

% Simplify array, record & sequence type objects if possible
%===========================================================

% ARR
simplify(X, Y) :- array_simplify(X, Y), !.

% REC
simplify(X, Y) :- record_simplify(X, Y), !.

% SEQ
simplify(X, Y) :- sequence_simplify(X, Y), !.

% ENU1
simplify(succ(X), Y) :- enumerated_simplify(succ(X), Y), !.

% ENU2
simplify(pred(X), Y) :- enumerated_simplify(pred(X), Y), !.

% Final catch-all
%================

% EVAL
simplify(X,Y) :- evaluate(X,Y), !.

%===============================================================================
% evaluate(+In, -Out).
%-------------------------------------------------------------------------------
% Evaluate (non-boolean) In to get Out.
%===============================================================================

% EVAL_BASE1
evaluate(X,X) :-
    (
        base_rational(X)
    ;
        X=true
    ;
        X=false
    ), !.
% EVAL_BASE2
evaluate(X,Y) :-
    integer(X), X<0,
    Y iss X, !.
% EVAL_BASE3
evaluate(X,Y) :-
    rational_expression(X),
    evaluate_rational_expression(X, Y),
    !.

% UMIN1
evaluate(-(-X),A) :- simplify(X,A), !.
% UMIN2
evaluate(-X,A) :-
    simplify(X,B),
    (
        base_rational(B),
        evaluate_rational_expression(-B, A)
    ;
        A=(-B)
    ), !.

% UPLUS
evaluate(+X, A) :- simplify(X, A), !.

%SPECIAL
evaluate(X+N-N,Y) :- simplify(X,Y), !.
evaluate(X-N+N,Y) :- simplify(X,Y), !.



% PLUS (Special Case)
% Where we see a repeated increment in a VC, such as a + 1 + 1 This gets
% parsed as (a + 1) + 1, but we would prefer to evaluate and simplify this
% as a + (1 + 1) and so on by collecting and evaluating the like terms on
% the right.  This case can crop up from VCs generated from SPARK code that
% has repeated increments on a single path, such as:
%  A := A + 1;
%  S;
%  A := A + 1;
% and so on...

evaluate((A + B) + C, D) :-
    integer(B), /* If B and C are both integer literals */
    integer(C),
    E iss B + C,
    simplify(A + E, D).

% PLUS (General Case)
evaluate(X+Y,Z) :-
    simplify(X,A),
    simplify(Y,B),
    (
        base_rational(A),
        base_rational(B),
        evaluate_rational_expression(A+B, Z)
    ;
        A=0,
        Z=B
    ;
        B=0,
        Z=A
    ;
        Z=A+B
    ), !.

% MINUS
evaluate(X-Y,Z) :-
    simplify(X,A),
    simplify(Y,B),
    (
        base_rational(A),
        base_rational(B),
        evaluate_rational_expression(A-B, Z)
    ;
        B=0,
        Z=A
    ;
        A=0,
        simplify(-Y,Z)
    ;
        A=B,
        Z=0
    ;
        Z=A-B
    ), !.

% MULT
evaluate(X*Y,Z) :-
    simplify(X,A),
    simplify(Y,B),
    (
        base_rational(A),
        base_rational(B),
        evaluate_rational_expression(A*B, Z)
    ;
        (
            A=0
        ;
            B=0
        ),
        Z=0
    ;
        A=1,
        Z=B
    ;
        B=1,
        Z=A
    ;
        Z=A*B
    ), !.

% DIV
evaluate(X div Y,Z) :-
    simplify(X,A),
    simplify(Y,B),
    (
        signed_integer(A),
        signed_integer(B),
        B\=0,
        Z iss A div B
    ;
        B=1,
        Z=A
    ;
        Z=A div B
    ), !.

%  /
evaluate(X / Y,Z) :-
    simplify(X,A),
    simplify(Y,B),
    (
        base_rational(A),
        base_rational(B),
        B\=0,
        evaluate_rational_expression(A/B, Z)
    ;
        B=1,
        Z=A
    ;
        Z=A / B
    ), !.

% MOD
evaluate(X mod Y, Z) :-
    simplify(X,A),
    simplify(Y,B),
    (
        signed_integer(A),
        signed_integer(B),
        B\=0,
        Z iss A mod B
    ;
        B=1,
        Z=0
    ;
        ( A = K*B ; A = B*K ),
        Z=0
    ;
        ( A = K*C ; A = C*K ),
        signed_integer(K),
        simplify(K mod B = 0, true),
        Z = 0
    ;
        Z=(A mod B)
    ), !.

%  EXP
evaluate(X**Y,Z) :-
    simplify(X,A),
    simplify(Y,B),
    (
        base_rational(A),
        signed_integer(B),
        evaluate_rational_expression(A**B, Z)
    ;
        B=0,
        Z=1
    ;
        B=1,
        Z=A
    ;
        B=2,
        Z=A*A
    ;
        Z=A**B
    ), !.

% EVAL_VAL
evaluate(X,Y) :- val(X,Y), !.

% FUNC_SPLIT
evaluate(X,Z) :-
    (\+ atomic(X)),
    X=..[H|T],
    eval_list(T,U),
    Z=..[H|U],
    !.

%===============================================================================
% eval_list(+InList, -OutList).
%-------------------------------------------------------------------------------
% Simplify each element of InList to get OutList.
%===============================================================================

% EVL1
eval_list([],[]) :- !.

% EVL2
eval_list([H1|T1],[H2|T2]) :-
    simplify(H1,H2),
    eval_list(T1,T2),
    !.

%===============================================================================
% val(+In, -Out).
%-------------------------------------------------------------------------------
% Hook for additional simplification rules.
%===============================================================================

val(X, X) :- atomic(X), !.

%===============================================================================
% signed_integer(+I).
%-------------------------------------------------------------------------------
% Succeeds if is I an integer (with an optional -)
%===============================================================================

signed_integer(I) :-
    (
       integer(I), I>=0
    ;
       I=(-I1), integer(I1), I1>0
    ).

%===============================================================================
% less_than(+X, +Y).
%-------------------------------------------------------------------------------
% Compare two signed_integers or rationals.
%===============================================================================

-X less_than -Y :- integer(X), integer(Y), !, X>0, Y>0, Y less_than X.
-X less_than Y  :- integer(X), integer(Y), !, X>0, Y>=0, !.
X less_than Y   :- integer(X), integer(Y), !, X>=0, Y>X.
X less_than Y   :-
    base_rational(X),
    base_rational(Y),
    split_rational(X, Xn, Xd),
    split_rational(Y, Yn, Yd),
    Xn * Yd < Yn * Xd.

%===============================================================================
% is_a_power_of_2(+X).
%-------------------------------------------------------------------------------
% Succeeds if X is a power of 2 (2, 4, 8, 16, 32, etc.). Fails otherwise.
%===============================================================================

is_a_power_of_2(         2).    /* 2**1 */
is_a_power_of_2(         4).    /* 2**2 */
is_a_power_of_2(         8).    /* 2**3 */
is_a_power_of_2(        16).    /* 2**4 */
is_a_power_of_2(        32).    /* 2**5 */
is_a_power_of_2(        64).    /* 2**6 */
is_a_power_of_2(       128).    /* 2**7 */
is_a_power_of_2(       256).    /* 2**8 */
is_a_power_of_2(       512).    /* 2**9 */
is_a_power_of_2(      1024).    /* 2**10 */
is_a_power_of_2(      2048).    /* 2**11 */
is_a_power_of_2(      4096).    /* 2**12 */
is_a_power_of_2(      8192).    /* 2**13 */
is_a_power_of_2(     16384).    /* 2**14 */
is_a_power_of_2(     32768).    /* 2**15 */
is_a_power_of_2(     65536).    /* 2**16 */
is_a_power_of_2(    131072).    /* 2**17 */
is_a_power_of_2(    262144).    /* 2**18 */
is_a_power_of_2(    524288).    /* 2**19 */
is_a_power_of_2(   1048576).    /* 2**20 */
is_a_power_of_2(   2097152).    /* 2**21 */
is_a_power_of_2(   4194304).    /* 2**22 */
is_a_power_of_2(   8388608).    /* 2**23 */
is_a_power_of_2(  16777216).    /* 2**24 */
is_a_power_of_2(  33554432).    /* 2**25 */
is_a_power_of_2(  67108864).    /* 2**26 */
is_a_power_of_2( 134217728).    /* 2**27 */
is_a_power_of_2( 268435456).    /* 2**28 */
is_a_power_of_2( 536870912).    /* 2**29 */
is_a_power_of_2(1073741824).    /* 2**30 */
is_a_power_of_2(2147483648).    /* 2**31 */
is_a_power_of_2(4294967296).    /* 2**32 */

/* [I805-018] identified a limitation in generating powers of two      */
/* above 2**32, so we now add ground entries here for 2**33 thru 2**64 */
is_a_power_of_2(          8589934592). /* 2**33 */
is_a_power_of_2(         17179869184). /* 2**34 */
is_a_power_of_2(         34359738368). /* 2**35 */
is_a_power_of_2(         68719476736). /* 2**36 */
is_a_power_of_2(        137438953472). /* 2**37 */
is_a_power_of_2(        274877906944). /* 2**38 */
is_a_power_of_2(        549755813888). /* 2**39 */
is_a_power_of_2(       1099511627776). /* 2**40 */
is_a_power_of_2(       2199023255552). /* 2**41 */
is_a_power_of_2(       4398046511104). /* 2**42 */
is_a_power_of_2(       8796093022208). /* 2**43 */
is_a_power_of_2(      17592186044416). /* 2**44 */
is_a_power_of_2(      35184372088832). /* 2**45 */
is_a_power_of_2(      70368744177664). /* 2**46 */
is_a_power_of_2(     140737488355328). /* 2**47 */
is_a_power_of_2(     281474976710656). /* 2**48 */
is_a_power_of_2(     562949953421312). /* 2**49 */
is_a_power_of_2(    1125899906842624). /* 2**50 */
is_a_power_of_2(    2251799813685248). /* 2**51 */
is_a_power_of_2(    4503599627370496). /* 2**52 */
is_a_power_of_2(    9007199254740992). /* 2**53 */
is_a_power_of_2(   18014398509481984). /* 2**54 */
is_a_power_of_2(   36028797018963968). /* 2**55 */
is_a_power_of_2(   72057594037927936). /* 2**56 */
is_a_power_of_2(  144115188075855872). /* 2**57 */
is_a_power_of_2(  288230376151711744). /* 2**58 */
is_a_power_of_2(  576460752303423488). /* 2**59 */
is_a_power_of_2( 1152921504606846976). /* 2**60 */
is_a_power_of_2( 2305843009213693952). /* 2**61 */
is_a_power_of_2( 4611686018427387904). /* 2**62 */
is_a_power_of_2( 9223372036854775808). /* 2**63 */
is_a_power_of_2(18446744073709551616). /* 2**64 */


is_a_power_of_2(X) :-
    \+ integer(X),
    !,
    fail.                   /* trap these */

is_a_power_of_2(X) :-
    X > 4294967296,         /* 2**32 */
    !,
    Y iss X div 4294967296,
    X iss Y * 4294967296,   /* so it's an exact multiple of 2**32 */
    !,
    is_a_power_of_2(Y).

%===============================================================================
% next_two_to_N_minus_1_above(X, P2M1).
%-------------------------------------------------------------------------------
% Fetch next 2**N-1 above integer X.
%===============================================================================

next_two_to_N_minus_1_above(X, _) :-    /* used in DEDUCTION module */
    \+ integer(X),
    !,
    fail.

next_two_to_N_minus_1_above(X, P2M1) :-
    is_a_power_of_2(POW2),
    POW2 > X,
    !,
    P2M1 is POW2 - 1.

%===============================================================================
% evaluate_bit_and(+X, +Y, -Result).
%-------------------------------------------------------------------------------
% Evaluate 'and' of two integers.
%===============================================================================

evaluate_bit_and(NEWX, NEWY, NEW) :-
    form_bit_string(NEWX, XList),
    form_bit_string(NEWY, YList),
    and_bit_strings(XList, YList, ZList),
    form_number(ZList, NEW).

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

and_bit_strings([0|XL], [_|YL], [0|Rest]) :-
    !,
    and_bit_strings(XL, YL, Rest).

and_bit_strings([1|XL], [Y|YL], [Y|Rest]) :-
    !,
    and_bit_strings(XL, YL, Rest).

and_bit_strings(_XL, [], []) :- !.      /* no more digits in one string or the other */
and_bit_strings([], _YL, []) :- !.

%===============================================================================
% evaluate_bit_or(+X, +Y, -Result).
%-------------------------------------------------------------------------------
% Evaluate 'or' of two integers.
%===============================================================================

evaluate_bit_or(NEWX, NEWY, NEW) :-
    evaluate_bit_and(NEWX, NEWY, BIT_AND),
    !,
    NEW iss NEWX + NEWY - BIT_AND.

%===============================================================================
% evaluate_bit_xor(+X, +Y, -Result).
%-------------------------------------------------------------------------------
% Evaluate 'xor' of two integers.
%===============================================================================

evaluate_bit_xor(NEWX, NEWY, NEW) :-
    evaluate_bit_and(NEWX, NEWY, BIT_AND),
    !,
    NEW iss NEWX + NEWY - 2 * BIT_AND.

%===============================================================================
% form_bit_string(+N, -L).
%-------------------------------------------------------------------------------
% Turn number N into a (reverse order) list, e.g. 6 ==> [0, 1, 1] etc.
%===============================================================================

form_bit_string(0, [0]) :- !.
form_bit_string(1, [1]) :- !.

form_bit_string(N, [LSbit|Rest]) :-
    LSbit iss N mod 2,
    NewN iss N div 2,
    !,
    form_bit_string(NewN, Rest).

%===============================================================================
% form_number(+L, -N).
%-------------------------------------------------------------------------------
% Turn a bit string back into a number.
%===============================================================================

form_number(List, Result) :-
    form_number_from_base(1, List, Result).

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

form_number_from_base(N, [B], Result) :-
    !,
    Result is N * B.

form_number_from_base(N, [LSbit|Rest], Result) :-
    NewBase is N * 2,
    form_number_from_base(NewBase, Rest, MSresult),
    !,
    Result is MSresult + N * LSbit.

form_number_from_base(_N, [], 0) :- !.  /* catchall */

%===============================================================================
% uq_normalise(+I, +Statements, -NewStatements).
%-------------------------------------------------------------------------------
% Normalises the form of a universally quantified hypothesis such that all
% parts are expressed in terms of <=.
%
% Also where there is a lower and upper bound to variable, order those
% inequalities lower-upper.  i.e. LOWER <= X and X <= UPPER
%===============================================================================

uq_normalise(I, Statements, NewStatements):-
    statement_norm(I, Statements, NewStatements).

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

statement_norm(I, A -> B, NEWA -> NEWB):-
    statement_norm(I, A, NEWA),
    statement_norm(I, B, NEWB).

statement_norm(I, A and B, NEWA and NEWB):-
    statement_norm(I, A, A2),
    statement_norm(I, B, B2),
    lower_upper(I, A2, B2, NEWA, NEWB).

statement_norm(I, A or B, NEWA or NEWB) :-
    statement_norm(I, A, NEWA),
    statement_norm(I, B, NEWB).

statement_norm(I, A >= B, NEWB <= NEWA):-
    statement_norm(I, A, NEWA),
    statement_norm(I, B, NEWB).

statement_norm(_I, X, X):- !.

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

lower_upper(I, I <= UPPER, LOWER <= I, LOWER <= I, I <= UPPER).
lower_upper(I, LOWER <= I, I <= UPPER, LOWER <= I, I <= UPPER).
lower_upper(_I, A, B, A, B).

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