(* Copyright (C) 2011 Free Software Foundation, Inc. *)
(* This file is part of GNU Modula-2.
GNU Modula-2 is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GNU Modula-2 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 along
with gm2; see the file COPYING.  If not, write to the Free Software
Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. *)
MODULE integer ;
(*
    Title      : integer
    Author     : Gaius Mulley
    System     : GNU Modula-2
    Date       : Fri May 18 17:05:36 2012
    Revision   : $Version$
    Description: simple test module to test the principles of catching signed and unsigned
                 integer arithmetic overflow.
*)
FROM SYSTEM IMPORT ADDRESS ;
FROM libc IMPORT printf ;
FROM DynamicStrings IMPORT String, InitString, string, KillString, InitString ;
CONST
   Verbose = TRUE ;
   SizeOfIntAndLongSame = TRUE ;
PROCEDURE ssub (i, j: INTEGER) ;
BEGIN
   IF ((j>0) AND (i < MIN(INTEGER)+j)) OR
      ((j<0) AND (i > MAX(INTEGER)+j))
   THEN
      expecting(overflow, 'signed subtraction')
   ELSE
      expecting(none, 'signed subtraction')
   END
END ssub ;
PROCEDURE sadd (i, j: INTEGER) ;
BEGIN
   printf ("i = %d,  j = %d   MIN(INTEGER) = %d\n",
           i, j, MIN(INTEGER));
   printf ("MIN(INTEGER) = %d,  -j = %d\n", MIN(INTEGER), -j);
   IF ((j = MIN(INTEGER)) AND (i < 0)) OR
      ((i = MIN(INTEGER)) AND (j < 0)) OR
      ((j>0) AND (i > MAX(INTEGER)-j)) OR
      ((j<0) AND (i < MIN(INTEGER)-j))
   THEN
      expecting(overflow, 'signed addition')
   ELSE
      expecting(none, 'signed addition')
   END
END sadd ;
(*
   smallMult -
*)
PROCEDURE smallMult (i, j: INTEGER) ;
BEGIN
   IF i>0
   THEN
      IF j>0
      THEN
         IF i>maxInt DIV j
         THEN
            expecting(overflow, 'signed mult')
         ELSE
            expecting(none, 'signed mult')
         END
      ELSE
         IF j<minInt DIV i
         THEN
            expecting(overflow, 'signed mult')
         ELSE
            expecting(none, 'signed mult')
         END
      END
   ELSE
      IF j>0
      THEN
         IF i<minInt DIV j
         THEN
            expecting(overflow, 'signed mult')
         ELSE
            expecting(none, 'signed mult')
         END
      ELSE
         IF (i#0) AND (j<maxInt DIV i)
         THEN
            expecting(overflow, 'signed mult')
         ELSE
            expecting(none, 'signed mult')
         END
      END
   END
END smallMult ;
(*
   smult -
*)
PROCEDURE smult (i, j: INTEGER) ;
VAR
   li, lj, lt: LONGINT ;
BEGIN
   IF SizeOfIntAndLongSame OR (SIZE(LONGINT)=SIZE(INTEGER))
   THEN
      smallMult(i, j)
   ELSE
      li := i ;
      lj := j ;
      lt := li * lj ;
      IF (lt<VAL(LONGINT, minInt)) OR (lt>VAL(LONGINT, maxInt))
      THEN
         expecting(overflow, 'signed multiply')
      ELSE
         expecting(none, 'signed multiply')
      END
   END
END smult ;
(*
   sneg -
*)
PROCEDURE sneg (i: INTEGER) ;
BEGIN
   IF i=minInt
   THEN
      expecting(overflow, 'signed negate')
   ELSE
      expecting(none, 'signed negate')
   END
END sneg ;
(*
   passed -
*)
PROCEDURE expecting (e: error; a: ARRAY OF CHAR) ;
VAR
   s: String ;
   t: ADDRESS ;
BEGIN
   WITH test[testNo] DO
      IF expected#e
      THEN
         s := InitString(a) ;
         t := string(s) ;
         printf("test %s (%d) has failed\n", t, testNo) ;
         s := KillString(s)
      ELSIF Verbose
      THEN
         s := InitString(a) ;
         t := string(s) ;
         printf("test %s (%d) has passed\n", t, testNo) ;
         s := KillString(s)
      END
   END
END expecting ;
(*
   doTest -
*)
PROCEDURE doTest ;
BEGIN
   WITH test[testNo] DO
      CASE op OF
      iadd :  sadd(l, r) |
      isub :  ssub(l, r) |
      ineg :  sneg(l) |
      imult:  smult(l, r) |
      idiv :  |
      imod :  |
      END
   END
END doTest ;
(*
   doTests -
*)
PROCEDURE doTests ;
BEGIN
   testNo := 0 ;
   WHILE testNo<=maxTest DO
      doTest ;
      INC(testNo)
   END
END doTests ;
CONST
   maxTest = 25 ;
   maxInt = MAX(INTEGER) ;
   minInt = MIN(INTEGER) ;
TYPE
   opcode = (iadd, isub, ineg, imult, idiv, imod) ;
   error  = (overflow, underflow, none) ;
   case = RECORD
             l, r    : INTEGER ;
             op      : opcode ;
             expected: error ;
          END ;
   cases = ARRAY [0..maxTest] OF case ;
VAR
   test  : cases ;
   testNo: CARDINAL ;
BEGIN
   test := cases{{minInt, 0, ineg, overflow},
                 (* 1 *)
                 {maxInt, 0, ineg, none},
                 {minInt DIV 2, minInt DIV 2, iadd, none},
                 {minInt DIV 2, minInt DIV 2-1, iadd, overflow},
                 {maxInt DIV 2, maxInt DIV 2, iadd, none},
                 (* 4 *)
                 {maxInt DIV 2, maxInt DIV 2+1, iadd, none},
                 {maxInt DIV 2+1, maxInt DIV 2+1, iadd, overflow},
                 {maxInt, 1, iadd, overflow},
                 {maxInt, 0, iadd, none},
                 (* 8 *)
                 {minInt, -1, iadd, overflow},
                 {minInt, 0, iadd, none},
                 {-1, maxInt, isub, none},
                 {-2, maxInt, isub, overflow},
                 (* 12 *)
                 {minInt, 1, isub, overflow},
                 {minInt, 0, isub, none},
                 {maxInt, -2, isub, overflow},
                 {maxInt, minInt, isub, overflow},
                 (* 16 *)
                 {0, maxInt, isub, none},
                 {0, minInt, isub, overflow},
                 {-1, maxInt, isub, none},
                 {-2, maxInt, isub, overflow},
                 (* 20 *)
                 {maxInt, 2, imult, overflow},
                 {maxInt DIV 2, 2, imult, none},
                 {minInt DIV 2, 2, imult, none},
                 {minInt DIV 2-1, 2, imult, overflow},
		 (* 24 *)
                 {maxInt DIV 3, 3, imult, none},
                 {minInt DIV 3, 3, imult, none}
           } ;
   doTests
END integer.