% -----------------------------------------------------------------------------
%  (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.
% 
% =============================================================================


/****** INTEGER TEST ******/

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


/****** SIMPLE INFERENCE ******/

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

testused(X=Y) :-
        (
            ((\+ X=Y),
             (\+ used(X=Y)),
             (\+ used(Y=X)),
             assertz(used(X=Y))
            )
        ;
            (
                retract(used(X=Y))
            ;
                retract(used(Y=X))
            ),
            !,
            fail
        ).

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

testused(X>=Y) :-
        (
            (\+ used(X>=Y)),
            assertz(used(X>=Y))
        ;
            retract(used(X>=Y)),
            !,
            fail
        ).

fact(X) :- hyp(_,X).
fact(X=Y) :- hyp(_,not X<>Y) ; hyp(_,Y=X) ; hyp(_,not Y<>X).
fact(X<>Y) :- hyp(_,not X=Y) ; hyp(_,Y<>X) ; hyp(_,not Y=X).
fact(X>Y) :- hyp(_,not X<=Y) ; hyp(_,Y<X) ; hyp(_,not Y>=X).
fact(X<Y) :- hyp(_,not X>=Y) ; hyp(_,Y>X) ; hyp(_,not Y<=X).
fact(X>=Y) :- hyp(_,not X<Y) ; hyp(_,Y<=X) ; hyp(_,not Y>X).
fact(X<=Y) :- hyp(_,not X>Y) ; hyp(_,Y>=X) ; hyp(_,not Y<X).

infrule(X) :- fact(X).
infrule(X=Y) :- fact(X>=Y), fact(X<=Y).
infrule(X<>Y) :- fact(X>Y) ; fact(X<Y).
infrule(X>Y) :- fact(X>=Y), fact(X<>Y).
infrule(X<Y) :- fact(X<=Y), fact(X<>Y).
infrule(X>=Y) :- (fact(X=Y), testused(X=Y)) ; fact(X>Y).
infrule(X<=Y) :- (fact(X=Y), testused(X=Y)) ; fact(X<Y).

/****** DEDUCE(R) - deduce relational expression R from VC. ******/

deduce(true,_).
deduce(X,_) :- infrule(X).

/************ (1) SIMPLE ADDITION AND SUBTRACTION ************/

/****** EQUALITY ******/

deduce(X+Y=Z,T) :-
   (
      infrule(Y+X=Z)
   ;
      infrule(Y=Z-X)
   ;
      evaluate(-X,W),
      infrule(Y=W+Z)
   ;
      int(X),                           /* If X is an integer -           */
      (
         simplify(X=0,true),            /* - and =0, prove Y=Z or fail    */
         (
            deduce(Y=Z,T)
         ;
            !, fail
         )
      ;
         int(Y),                        /* - and Y is an integer, sum     */
         (                              /*   them & prove sum=Z or fail   */
            evaluate(Y+X,W),
            deduce(Z=W,T)
         ;
            !, fail
         )
      ;
         int(Z),                        /* - and Z is an integer subtract */
         (                              /*   & prove diff=Y or fail       */
            evaluate(Z-X,W),
            deduce(Y=W,T)
         ;
            !, fail
         )
      )
   ;
      (\+ int(Y)),                     /* If neither X nor Y is an       */
      (\+ int(Z)),                     /* integer, try finding a         */
      (                                 /* substitution for Y and         */
         nonvar(Y),
         infrule(Y=W),
         testused(Y=W),
         deduce(X+W=Z,T)
      ;
         !, 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) :- int(N), (\+ int(X)), deduce(N+X=Y,T).

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

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

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

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

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

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

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

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


/****** INEQUALITY ******/

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

deduce(X+A<>X+B,T) :- deduce(A<>B,T).                           /* CFR013 */

/* 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) :- int(N), (\+ int(X)), deduce(N+X<>Y,T).

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

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

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

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

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

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

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

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


/****** INEQUALITY ******/

deduce(X+Y<>Z,T) :-
   (
      infrule(Y+X<>Z)
   ;
      infrule(Y<>Z-X)
   ;
      evaluate(-X,W),
      infrule(Y<>W+Z)
   ;
      int(X),                            /* If X is an integer -           */
      (
         simplify(X=0,true),             /* - and =0, prove Y<>Z or fail   */
         (
            deduce(Y<>Z,T)
         ;
            !, fail
         )
      ;
         deduce(Y=Z,T)                     /* - 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)
         ;
            !, fail
         )
      ;
         int(Z),                         /* - and Z is an integer subtract */
         (                               /*   & prove diff<>Y or fail      */
            evaluate(Z-X,W),
            deduce(Y<>W,T)
         ;
            !, 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),                   /* proving resulting inequality.  */
         testused(Y=W),
         deduce(X+W<>Z,T)
      ;
         !, fail
      )
   ).


/****** GREATER-THAN ******/

deduce(X+Y>Z,T) :-
   (
      infrule(Y+X>Z)
   ;
      infrule(Y>Z-X)
   ;
      evaluate(-X,W),
      infrule(Y>W+Z)
   ;
      int(X),                              /* If X is an integer -           */
      (
         simplify(X=0,true),               /* - and X=0, prove Y>Z or fail   */
         ( deduce(Y>Z,T) ; !, fail )
      ;
         int(Y),                           /* - and Y is an integer, sum and */
         (                                 /*   prove sum>Z or fail          */
            evaluate(Y+X,W),
            deduce(Z<W,T)
         ;
            !, fail
         )
      ;
         int(Z),                           /* - and Z is an integer, take X  */
         (                                 /*   away & prove diff<Y or fail  */
            evaluate(Z-X,W),
            deduce(Y>W,T)
         ;
            !, fail
         )
      ;
         nonvar(Y),                        /* - and Y & Z are the same, show */
         nonvar(Z),                        /*   X>0 or fail                  */
         Y=Z,
         ( simplify(X>0,true) ; !, fail )
      ;
         simplify(X>0,true),               /* - and X>0, prove Y>=Z          */
         deduce(Y>=Z,T)
      ;
         T=integer,
         evaluate(X-1,W),                  /* - subtract 1 to give more      */
         (                                 /*   equivalent forms to search   */
            infrule(W+Y>=Z)                /*   for which use the operator   */
         ;                                 /*   >= instead of >.             */
            infrule(Y+W>=Z)
         ;
            infrule(Y>=Z-W)
         ;
            evaluate(-W,V),
            infrule(Y>=V+Z)
         )
      )
   ;
      (\+ 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),                  /*     >= followed by >.          */
            deduce(X+W>=Z,T)
         ;
            !, fail
         )
      ;
         (
            nonvar(Y),
            infrule(Y>=W),
            testused(Y>=W),
            deduce(X+W>Z,T)
         ;
            !, 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) :- int(N), (\+ int(X)), deduce(N+X>Y,T).

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

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

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

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

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

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

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

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


/****** LESS-THAN ******/

deduce(X+Y<Z,T) :-
   (
      infrule(Y+X<Z)
   ;
      infrule(Y<Z-X)
   ;
      evaluate(-X,W),
      infrule(Y<W+Z)
   ;
      int(X),                             /* If X is an integer -           */
      (
         simplify(X=0,true),              /* - and X=0, prove Y<Z or fail   */
         ( deduce(Y<Z,T) ; !, fail )
      ;
         int(Y),                          /* - and Y is an integer, sum and */
         (                                /*   prove sum<Z or fail          */
            evaluate(Y+X,W),
            deduce(Z>W,T)
         ;
            !, fail
         )
      ;
         int(Z),                          /* - and Z is an integer, take X  */
         (                                /*   away & prove diff>Y or fail  */
            evaluate(Z-X,W),
            deduce(Y<W,T)
         ;
            !, fail
         )
      ;
         nonvar(Y),                       /* - and Y & Z are the same, show */
         nonvar(Z),                       /*   X<0 or fail                  */
         Y=Z,
         ( simplify(0>X,true) ; !, fail )
      ;
         simplify(0>X,true),              /* - and X<0, prove Y<=Z          */
         deduce(Y<=Z,T)
      ;
         T=integer,
         evaluate(X+1,W),                 /* - add 1 to X to give more      */
         (                                /*   equivalent forms to search   */
            infrule(W+Y<=Z)               /*   for which use the operator   */
         ;                                /*   <= instead of <.             */
            infrule(Y+W<=Z)
         ;
            infrule(Y<=Z-W)
         ;
            evaluate(-W,V),
            infrule(Y<=V+Z)
         )
      )
   ;
      (\+ 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),                 /*     <= followed by <.          */
            deduce(X+W<=Z,T)
         ;
            !, fail
         )
      ;
         (
            nonvar(Y),
            infrule(Y<=W),
            testused(W>=Y),
            deduce(X+W>Z,T)
         ;
            !, 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) :- int(N), (\+ int(X)), deduce(N+X<Y,T).

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

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

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

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

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

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

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

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


/****** GREATER-THAN-OR-EQUALS ******/

deduce(X+Y>=Z,T) :-
   (
      infrule(Y+X>=Z)
   ;
      infrule(Y>=Z-X)
   ;
      evaluate(-X,W), infrule(Y>=W+Z)
   ;
      int(X),                             /* If X is an integer -           */
      (
         simplify(X=0,true),              /* - and X=0, prove Y>=Z or fail  */
         ( deduce(Y>=Z,T) ; !, fail )
      ;
         int(Y),                          /* - and Y is an integer, sum and */
         (                                /*   prove sum>=Z or fail         */
            evaluate(Y+X,W), deduce(Z<=W,T)
         ;
            !, fail
         )
      ;
         int(Z),                          /* - and Z is an integer, take    */
         (                                /*   away X & show diff<=Y or fail*/
            evaluate(Z-X,W), deduce(Y>=W,T)
         ;
            !, fail
         )
      ;
         nonvar(Y), nonvar(Z),            /* - and Y & Z are the same, show */
         Y=Z,                             /*   X>=0 or fail                 */
         ( simplify(X>=0,true) ; !, fail )
      ;
         simplify(X>=0,true),deduce(Y>=Z,T) /* - 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)                /*   operator > instead of >=     */
         ;
            infrule(Y+W>Z)
         ;
            infrule(Y>Z-W)
         ;
            evaluate(-W,V), infrule(Y>V+Z)
         )
      )
   ;
      (\+ int(Y)), (\+ int(Z)),         /* If neither Y nor Z are         */
      (                                   /* integers, try finding a        */
         nonvar(Y),
         infrule(Y>=W), testused(Y>=W),
         deduce(X+W>=Z,T)
      ;
         !, 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) :- int(N), (\+ int(X)), deduce(N+X>=Y,T).

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

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

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

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

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

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

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

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

/****** 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) :- int(N), (\+ int(X)), deduce(N+X<=Y,T).

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

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

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

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

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

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

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

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


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


/************ (2) MULTIPLICATION ************/

/****** EQUALITY ******/

deduce(X*Y=Z,T) :-
   (
      infrule(Y*X=Z)
   ;
      int(X),                              /* If X is an integer -           */
      (
         simplify(X=0,true),               /* - and X=0, prove Z=0 or fail   */
         ( deduce(Z=0,T) ; !, fail )
      ;
         simplify(X=1,true),               /* - and X=1, prove Y=Z or fail   */
         ( deduce(Y=Z,T) ; !, fail )
      ;
         simplify(X=(-1),true),            /* - and X=-1, prove -Y=Z or fail */
         (
            evaluate(-Y,W),
            deduce(W=Z,T)
         ;
            !, fail
         )
      ;
         int(Y),                           /* - and Y is an integer, multiply*/
         (                                 /*   by X & prove prod=Z or fail  */
            evaluate(X*Y,W),
            deduce(W=Z,T)
         ;
            !, fail
         )
      ;
         infrule(Y=W),                     /* - try substituting for Y       */
         testused(Y=W),
         deduce(X*W=Z,T)
      )
   ;
      (\+ 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)                       /* Y=0.                           */
      ;
         nonvar(Y),
         deduce(Y=0,T)
      )
   ).

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

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

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

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


/****** INEQUALITY ******/

deduce(X*Y<>Z,T) :-
   (
      infrule(Y*X<>Z)
   ;
      int(X),                             /* If X is an integer -           */
      (
         simplify(X=0,true),              /* - and X=0, prove Z<>0 or fail  */
         ( deduce(Z<>0,T) ; !, fail )
      ;
         simplify(X=1,true),              /* - and X=1, prove Y<>Z or fail  */
         ( deduce(Y<>Z,T) ; !, fail )
      ;
         deduce(Y=Z,T),                     /* - and X<>1, prove Y=Z<>0       */
         (
            infrule(Y<>0)
         ;
            infrule(Z<>0)
         )
      ;
         simplify(X=(-1),true),           /* - and X=-1, prove -Y<>Z or fail */
         (
            evaluate(-Y,W), deduce(W<>Z,T)
         ;
            !, fail
         )
      ;
         int(Y),                          /* - and Y is an integer, multiply*/
         (                                /*   by X & prove prod<>Z or fail */
            evaluate(X*Y,W), deduce(W<>Z,T)
         ;
            !, fail
         )
      ;
         infrule(Y=W), testused(Y=W),     /* - try substituting for Y       */
         deduce(X*W<>Z,T)
      )
   ;
      (\+ 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), deduce(Y<>0,T)   /*   Y is zero                    */
      ;
         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), deduce(Y<0,T)  /*   the other is less than zero  */
         ;
            deduce(X<0,T), deduce(Y>0,T)
         )
      ;
         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), deduce(Y>0,T)  /*   sign.                        */
         ;
            deduce(X<0,T), deduce(Y<0,T)
         )
      )
   ).


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

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

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

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

/****** GREATER-THAN ******/

deduce(X*Y>Z,T) :-
   (
      infrule(Y*X>Z)
   ;
      int(X),                             /* If X is an integer -           */
      (
         simplify(X=0,true),              /* - and X=0, prove Z<0 or fail   */
         ( deduce(Z<0,T) ; !, fail )
      ;
         simplify(X=1,true),              /* - and X=1, prove Y>Z or fail   */
         ( deduce(Y>Z,T) ; !, fail )
      ;
         simplify(X=(-1),true),           /* - and X=-1, prove -Y>Z or fail */
         (
            evaluate(-Y,W), deduce(W>Z,T)
         ;
            !, fail
         )
      ;
         int(Y),                          /* - and Y is an integer, multiply*/
         (                                /*   & prove prod>Z or fail       */
            evaluate(X*Y,W), deduce(W>Z,T)
         ;
            !, fail
         )
      ;
         simplify(X>0,true),              /* - try substituting for Y,      */
         (                                /*   depending on sign of X.  For */
            infrule(Y>=W), testused(Y>=W),/*   each sign of X, there are two*/
            deduce(X*W>Z,T)               /*   cases: >= then > and > then  */
         ;                                /*   >= for X>0, or <= then > and */
            infrule(Y>W), deduce(X*W>=Z,T)  /*   < then >= for X<0.           */
         )
      ;
         simplify(0>X,true),
         (
            infrule(Y<=W), testused(W>=Y), deduce(X*W>Z,T)
         ;
            infrule(Y<W), deduce(X*W>=Z,T)
         )
      )
   ;
      (\+ 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), deduce(Y>0,T)     /* try proving X*Y product>0, by  */
      ;                                   /* showing X & Y have same sign.  */
         deduce(X<0,T), deduce(Y<0,T)
      )
   ).


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

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

/****** LESS-THAN ******/

deduce(X*Y<Z,T) :-
   (
      infrule(Y*X<Z)
   ;
      int(X),                              /* If X is an integer -           */
      (
         simplify(X=0,true),               /* - and X=0, prove Z>0 or fail   */
         ( deduce(Z>0,T) ; !, fail )
      ;
         simplify(X=1,true),               /* - and X=1, prove Y<Z or fail   */
         ( deduce(Y<Z,T) ; !, fail )
      ;
         simplify(X=(-1),true),            /* - and X=-1, prove -Y<Z or fail */
         (
            evaluate(-Y,W), deduce(W<Z,T)
         ;
            !, fail
         )
      ;
         int(Y),                           /* - and Y is an integer, multiply*/
         (                                 /*   & prove prod<Z or fail       */
            evaluate(X*Y,W), deduce(W<Z,T)
         ;
            !, fail
         )
      ;
         simplify(X>0,true),               /* - try substituting for Y,      */
         (                                 /*   depending on sign of X.  For */
            infrule(Y<=W), testused(W>=Y), /*   each sign of X, there are two*/
            deduce(X*W<Z,T)                /*   cases: <= then < and < then  */
         ;                                 /*   <= for X>0, or >= then < and */
            infrule(Y<W), deduce(X*W<=Z,T) /*   > then <= for X<0.           */
         )
      ;
         simplify(0>X,true),
         (
            infrule(Y>=W), testused(Y>=W),
            deduce(X*W<Z,T)
         ;
            infrule(Y>W), deduce(X*W<=Z,T)
         )
      )
   ;
      (\+ 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), deduce(Y<0,T)      /* showing X & Y have diff signs. */
      ;
         deduce(X<0,T), deduce(Y>0,T)
      )
   ).


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

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

/****** GREATER-THAN-OR-EQUALS ******/

deduce(X*Y>=Z,T) :-
   (
      infrule(Y*X>=Z)
   ;
      int(X),                               /* If X is an integer -           */
      (
         simplify(X=0,true),                /* - and X=0, prove Z<=0 or fail  */
         ( deduce(Z<=0,T) ; !, fail )
      ;
         simplify(X=1,true),                /* - and X=1, prove Y>=Z or fail  */
         ( deduce(Y>=Z,T) ; !, fail )
      ;
         simplify(X=(-1),true),             /* - and X=-1, prove -Y>=Z or fail*/
         (
            evaluate(-Y,W), deduce(W>=Z,T)
         ;
            !, fail
         )
      ;
         int(Y),                            /* - and Y is an integer, multiply*/
         (                                  /*   and prove prod>=Z or fail    */
            evaluate(X*Y,W), deduce(W>=Z,T)
         ;
            !, fail
         )
      ;
         simplify(X>0,true),                /* - try finding substitution for */
         infrule(Y>=W), testused(Y>=W),     /*   Y depending on sign of X and */
         deduce(X*W>=Z,T)
      ;
         simplify(0>X,true),                /*   prove resulting expression.  */
         infrule(Y<=W), testused(W>=Y),
         deduce(X*W>=Z,T)
      )
   ;
      (\+ 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), deduce(Y>=0,T)     /* to 0.                          */
      ;
         deduce(X<=0,T), deduce(Y<=0,T)
      )
   ).

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

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


/****** LESS-THAN-OR-EQUALS ******/

deduce(X*Y<=Z,T) :-
   (
      infrule(Y*X<=Z)
   ;
      int(X),                              /* If X is an integer -           */
      (
         simplify(X=0,true),               /* - and X=0, prove Z>=0 or fail  */
         ( deduce(Z>=0,T) ; !, fail )
      ;
         simplify(X=1,true),               /* - and X=1, prove Y<=Z or fail  */
         ( deduce(Y<=Z,T) ; !, fail )
      ;
         simplify(X=(-1),true),            /* - and X=-1, prove -Y<=Z or fail*/
         (
            evaluate(-Y,W),
            deduce(W<=Z,T)
         ;
            !, fail
         )
      ;
         int(Y),                           /* - and Y is an integer, multiply*/
         (                                 /*   and prove prod<=Z or fail    */
            evaluate(X*Y,W),
            deduce(W<=Z,T)
         ;
            !, fail
         )
      ;
         simplify(X>0,true),               /* - try finding substitution for */
         infrule(Y<=W),                    /*   Y depending on sign of X and */
         testused(W>=Y),                   /*   prove resulting expression.  */
         deduce(X*W<=Z,T)
      ;
         simplify(0>X,true),
         infrule(Y>=W),
         testused(Y>=W),
         deduce(X*W<=Z,T)
      )
   ;
      (\+ 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),                    /* 0.                             */
         deduce(Y<=0,T)
      ;
         deduce(X<=0,T),
         deduce(Y>=0,T)
      )
   ).

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

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


/************ (3) GENERAL RULES ************/

/****** EQUALITY ******/

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


/****** INEQUALITY ******/

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


/****** GREATER-THAN ******/

deduce(X>Y,T) :-
   (
      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     */
      ;
         deduce(Y<X,T)                     /* - swap sides then prove.       */
      )
   ;
      (
         infrule(Z+X>=Y)                   /* Equivalent forms of the        */
      ;                                    /* expression using different     */
         infrule(X+Z>=Y)                   /* operator and integer addition  */
      ),                                   /* or subtraction.                */
      (
         int(Z), simplify(0>Z,true)
      ;
         infrule(Z<0)
      )
   ;
      infrule(X-Z>=Y),
      (
         int(Z), simplify(Z>0,true)
      ;
         infrule(Z>0)
      )
   ;
      infrule(X>=Z), testused(X>=Z),       /* Two possible intermediate      */
      deduce(Z>Y,T)                        /* steps of deduction.            */
   ;
      infrule(X>Z), deduce(Z>=Y,T)
   ).


/****** LESS-THAN ******/

deduce(X<Y,T) :-
   (
      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     */
      ;
         deduce(Y>X,T)                     /* - swap sides then prove.       */
      )
   ;
      (
         infrule(Z+X<=Y)                   /* Equivalent forms of the        */
      ;                                    /* expression using different     */
         infrule(X+Z<=Y)                   /* operator and integer addition  */
      ),                                   /* or subtraction.                */
      (
         int(Z), simplify(Z>0,true)
      ;
         infrule(Z>0)
      )
   ;
      infrule(X-Z<=Y),
      (
         int(Z), simplify(0>Z,true)
      ;
         infrule(Z<0)
      )
   ;
      infrule(X<=Z), testused(Z>=X),       /* Two possible intermediate      */
      deduce(Z<Y,T)                        /* steps of deduction.            */
   ;
      infrule(X<Z), deduce(Z<=Y,T)
   ).


/****** GREATER-THAN-OR-EQUALS ******/

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


/****** LESS-THAN-OR-EQUALS ******/

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