Copyright (C) 1993, Digital Equipment Corporation
All rights reserved.
See the file COPYRIGHT for a full description.
File: TWord.m3
Last Modified On Fri Nov 19 09:32:56 PST 1993 By kalsow
Modified On Thu May 20 08:46:32 PDT 1993 By muller
MODULE TWord;
IMPORT Word, TInt;
FROM Target IMPORT Int, IByte;
CONST (* IMPORTS *)
RShift = Word.RightShift;
LShift = Word.LeftShift;
CONST
Mask = RShift (Word.Not (0), Word.Size - BITSIZE (IByte));
Base = Mask + 1;
------------------------------------------- unsigned integer operations ---
PROCEDURE New (READONLY x: ARRAY OF CHAR; base: [2..16]; VAR r: Int): BOOLEAN =
VAR rr: Int; digit: INTEGER; ch: CHAR;
BEGIN
r := TInt.Zero;
FOR i := FIRST (x) TO LAST (x) DO
ch := x [i];
IF ('0' <= ch) AND (ch <= '9') THEN digit := ORD (ch) - ORD ('0');
ELSIF ('A' <= ch) AND (ch <= 'F') THEN digit := ORD (ch) - ORD ('A')+10;
ELSIF ('a' <= ch) AND (ch <= 'f') THEN digit := ORD (ch) - ORD ('a')+10;
ELSE RETURN FALSE;
END;
(* rr := r * base *)
rr := TInt.Zero;
FOR i := 0 TO LAST(Int) DO
VAR carry := Word.Times (r[i], base);
BEGIN
FOR j := i TO LAST(Int) DO
IF carry = 0 THEN EXIT END;
INC (carry, rr [j]);
rr [j] := Word.And (carry, Mask);
carry := RShift (carry, BITSIZE (IByte));
END;
IF carry # 0 THEN RETURN FALSE END;
END;
END;
(* r := rr + digit *)
VAR carry := digit;
BEGIN
FOR i := 0 TO LAST(Int) DO
INC (carry, rr [i]);
r[i] := Word.And (carry, Mask);
carry := RShift (carry, BITSIZE (IByte));
END;
IF carry # 0 THEN RETURN FALSE END;
END;
END;
RETURN TRUE;
END New;
PROCEDURE Add (READONLY a, b: Int; VAR r: Int) =
VAR carry := 0;
BEGIN
FOR i := 0 TO LAST(Int) DO
carry := a[i] + b[i] + carry;
r[i] := Word.And (carry, Mask);
carry := RShift (carry, BITSIZE (IByte));
END;
END Add;
PROCEDURE Subtract (READONLY a, b: Int; VAR r: Int) =
VAR borrow := 0;
BEGIN
FOR i := 0 TO LAST(Int) DO
borrow := a [i] - b [i] - borrow;
r [i] := Word.And (borrow, Mask);
borrow := Word.And (RShift (borrow, BITSIZE (IByte)), 1);
END;
END Subtract;
PROCEDURE Multiply (READONLY a, b: Int; VAR r: Int) =
VAR carry: INTEGER;
BEGIN
r := TInt.Zero;
FOR i := 0 TO LAST(Int) DO
FOR j := 0 TO LAST(Int) DO
carry := Word.Times (a[i], b[j]);
FOR k := i + j TO LAST(Int) DO
IF carry = 0 THEN EXIT END;
carry := carry + r[k];
r[k] := Word.And (carry, Mask);
carry := RShift (carry, BITSIZE (IByte));
END;
END;
END;
END Multiply;
PROCEDURE Div (READONLY num, den: Int; VAR q: Int): BOOLEAN =
VAR r: Int;
BEGIN
IF TInt.EQ (den, TInt.Zero) THEN RETURN FALSE; END;
IF TInt.EQ (num, TInt.Zero) THEN q := TInt.Zero; RETURN TRUE; END;
DivMod (num, den, q, r);
RETURN TRUE;
END Div;
PROCEDURE Mod (READONLY num, den: Int; VAR r: Int): BOOLEAN =
VAR q: Int;
BEGIN
IF TInt.EQ (den, TInt.Zero) THEN RETURN FALSE; END;
IF TInt.EQ (num, TInt.Zero) THEN r := TInt.Zero; RETURN TRUE; END;
DivMod (num, den, q, r);
RETURN TRUE;
END Mod;
PROCEDURE DivMod (READONLY x, y: Int; VAR q, r: Int) =
VAR
carry : INTEGER;
borrow : INTEGER;
tmp : INTEGER;
max_den : CARDINAL := 0;
max_num : CARDINAL := 0;
scale : INTEGER;
quo_est : INTEGER;
num_hi : INTEGER;
x1,x2,x3: INTEGER;
num, den := ARRAY [0..NUMBER (Int)+1] OF INTEGER {0,..};
BEGIN
(* initialize the numerator and denominator,
and find the highest non-zero digits *)
FOR i := 0 TO LAST(Int) DO
num[i] := x[i]; IF num[i] # 0 THEN max_num := i; END;
den[i] := y[i]; IF den[i] # 0 THEN max_den := i; END;
END;
q := TInt.Zero;
r := TInt.Zero;
IF max_den = 0 THEN
(* single digit denominator case *)
carry := 0;
FOR j := max_num TO 0 BY -1 DO
tmp := carry * Base + num [j];
q [j] := tmp DIV den[0];
carry := tmp MOD den[0];
END;
r[0] := carry;
RETURN;
END;
(* Insure that the first digit of the divisor is at least Base/2.
This is required by the quotient digit estimation algorithm. *)
scale := Base DIV (den [max_den] + 1);
IF scale > 1 THEN (* scale divisor and dividend *)
carry := 0;
FOR i := FIRST (num) TO LAST (num) DO
tmp := (num[i] * scale) + carry;
num [i] := Word.And (tmp, Mask);
carry := RShift (tmp, BITSIZE (IByte));
IF num[i] # 0 THEN max_num := i; END;
END;
carry := 0;
FOR i := FIRST (den) TO LAST (den) DO
tmp := (den[i] * scale) + carry;
den[i] := Word.And (tmp, Mask);
carry := RShift (tmp, BITSIZE (IByte));
IF den[i] # 0 THEN max_den := i; END;
END;
END;
(* Main loop *)
FOR i := max_num - max_den + 1 TO 1 BY -1 DO
(* guess the next quotient digit, quo_est, by dividing the first
two remaining dividend digits by the high order quotient digit.
quo_est is never low and is at most 2 high. *)
num_hi := i + max_den; (* index of highest remaining dividend digit *)
tmp := (num [num_hi] * Base);
IF num_hi > 0 THEN tmp := tmp + num [num_hi - 1]; END;
IF num [num_hi] # den [max_den]
THEN quo_est := tmp DIV den [max_den];
ELSE quo_est := Base - 1;
END;
(* refine quo_est so it's usually correct, and at most one high. *)
x3 := 0; IF (num_hi > 1) THEN x3 := num[num_hi - 2] END;
LOOP
x1 := den[max_den - 1] * quo_est;
x2 := (tmp - (quo_est * den[max_den])) * Base;
IF (x1 <= x2 + x3) THEN EXIT END;
DEC (quo_est);
END;
(* Try quo_est as the quotient digit, by multiplying the
denominator by quo_est and subtracting from the remaining
numerator. Keep in mind that quo_est is the i-1st digit.
Because we combine the multiply and subtract, borrow
can be more than 1. *)
borrow := 0;
FOR j := 0 TO max_den DO
tmp := num[i + j - 1] - (quo_est * den[j]) + borrow;
num [i + j - 1] := tmp MOD Base;
borrow := tmp DIV Base;
END;
(* if quo_est was high by one, we need to correct things. *)
IF -borrow > num [num_hi] THEN
DEC (quo_est);
carry := 0;
FOR j := 0 TO max_den DO
tmp := num [i + j - 1] + den [j] + carry;
num [i + j - 1] := tmp MOD Base;
carry := tmp DIV Base;
END;
INC (num [i + max_den], borrow + carry);
END;
(* store the quotient digit. *)
q [i - 1] := quo_est;
END;
(* finally, compute the remainder *)
Multiply (q, y, r);
Subtract (x, r, r);
END DivMod;
PROCEDURE LT (READONLY a, b: Int): BOOLEAN =
BEGIN
FOR i := LAST(Int) TO 0 BY -1 DO
IF a [i] < b [i] THEN RETURN TRUE;
ELSIF a [i] > b [i] THEN RETURN FALSE;
END;
END;
RETURN FALSE;
END LT;
PROCEDURE LE (READONLY a, b: Int): BOOLEAN =
BEGIN
FOR i := LAST(Int) TO 0 BY -1 DO
IF a [i] < b [i] THEN RETURN TRUE;
ELSIF a [i] > b [i] THEN RETURN FALSE;
END;
END;
RETURN TRUE;
END LE;
PROCEDURE GE (READONLY a, b: Int): BOOLEAN =
BEGIN
RETURN LE(b, a);
END GE;
PROCEDURE GT (READONLY a, b: Int): BOOLEAN =
BEGIN
RETURN LT(b, a);
END GT;
PROCEDURE And (READONLY a, b: Int; VAR r: Int) =
BEGIN
FOR i := 0 TO LAST(Int) DO
r [i] := Word.And (a [i], b[i]);
END;
END And;
PROCEDURE Or (READONLY a, b: Int; VAR r: Int) =
BEGIN
FOR i := 0 TO LAST(Int) DO
r [i] := Word.Or (a [i], b[i]);
END;
END Or;
PROCEDURE Xor (READONLY a, b: Int; VAR r: Int) =
BEGIN
FOR i := 0 TO LAST(Int) DO
r [i] := Word.Xor (a [i], b[i]);
END;
END Xor;
PROCEDURE Not (READONLY a: Int; VAR r: Int) =
BEGIN
FOR i := 0 TO LAST(Int) DO
r [i] := Word.And (Word.Not (a [i]), Mask);
END;
END Not;
PROCEDURE Shift (READONLY a: Int; b: INTEGER; VAR r: Int) =
BEGIN
IF ABS (b) >= Size THEN
r := TInt.Zero;
ELSIF b > 0
THEN LeftShift(a, b, r);
ELSE RightShift(a, -b, r);
END;
END Shift;
PROCEDURE LeftShift (READONLY a: Int; b: [0..Size-1]; VAR r: Int) =
VAR w, i, j, z, x1, x2: INTEGER;
BEGIN
IF b = 0 THEN (* no shift *)
r := a;
ELSE
w := b DIV BITSIZE (IByte);
i := b MOD BITSIZE (IByte);
j := BITSIZE (IByte) - i;
FOR k := LAST(Int) TO 0 BY -1 DO
z := k - w; x1 := 0; x2 := 0;
IF z >= 0 THEN x1 := LShift (a[z], i); END;
IF z-1 >= 0 THEN x2 := RShift (a[z-1], j); END;
r[k] := Word.And (Word.Or (x1, x2), Mask);
END;
END;
END LeftShift;
PROCEDURE RightShift (READONLY a: Int; b: [0..Size-1]; VAR r: Int) =
VAR w, i, j, z, x1, x2: INTEGER;
BEGIN
IF b = 0 THEN (* no shift *)
r := a;
ELSE
w := b DIV BITSIZE (IByte);
i := b MOD BITSIZE (IByte);
j := BITSIZE (IByte) - i;
FOR k := 0 TO LAST(Int) DO
z := k + w; x1 := 0; x2 := 0;
IF z <= LAST(Int) THEN x1 := RShift (a[z], i); END;
IF z+1 <= LAST(Int) THEN x2 := LShift (a[z+1], j); END;
r[k] := Word.And (Word.Or (x1, x2), Mask);
END;
END;
END RightShift;
PROCEDURE Rotate (READONLY a: Int; b: INTEGER; n: CARDINAL; VAR r: Int) =
VAR
w, i, j, z, x1, x2: INTEGER;
tmp: Int;
size := n * BITSIZE (IByte);
BEGIN
b := b MOD size;
IF b = 0 THEN
r := a;
ELSIF b > 0 THEN (* left rotate *)
w := b DIV BITSIZE (IByte);
i := b MOD BITSIZE (IByte);
j := BITSIZE (IByte) - i;
FOR k := 0 TO n-1 DO
z := k - w; x1 := 0; x2 := 0;
x1 := LShift (a[z MOD n], i);
x2 := RShift (a[(z-1) MOD n], j);
tmp[k] := Word.And (Word.Or (x1, x2), Mask);
END;
r := tmp;
ELSE (* right rotate *)
w := (-b) DIV BITSIZE (IByte);
i := (-b) DIV BITSIZE (IByte);
j := BITSIZE (IByte) - i;
FOR k := 0 TO n-1 DO
z := k + w; x1 := 0; x2 := 0;
x1 := RShift (a[z MOD n], i);
x2 := LShift (a[(z+1) MOD n], j);
tmp[k] := Word.And (Word.Or (x1, x2), Mask);
END;
r := tmp;
END;
END Rotate;
PROCEDURE Extract (READONLY x: Int; i, n: CARDINAL; VAR r: Int): BOOLEAN =
VAR w, b: INTEGER;
BEGIN
IF i + n > Size THEN RETURN FALSE; END;
Shift (x, -i, r);
w := n DIV BITSIZE (IByte);
b := n MOD BITSIZE (IByte);
r[w] := Word.And (r[w], RShift (Mask, BITSIZE (IByte) - b));
FOR k := w + 1 TO LAST(Int) DO r[k] := 0; END;
RETURN TRUE;
END Extract;
PROCEDURE Insert (READONLY x, y: Int; i, n: CARDINAL; VAR r: Int): BOOLEAN =
VAR yy, yyy, yyyy: Int;
BEGIN
IF i + n > Size THEN RETURN FALSE; END;
Shift (x, -(i + n), yy);
Shift (yy, n, r);
Shift (y, Size - n, yy);
Shift (yy, -(Size - n), yyy);
Or (r, yyy, r);
Shift (r, i, yyyy);
r := yyyy;
Shift (x, Size - i, yy);
Shift (yy, -(Size - i), yyy);
Or (r, yyy, r);
RETURN TRUE;
END Insert;
PROCEDURE Truncate (READONLY a: Int; n: CARDINAL; VAR r: Int): BOOLEAN =
VAR result := TRUE;
BEGIN
FOR i := 0 TO n-1 DO r[i] := a[i] END;
FOR i := n TO LAST(Int) DO
IF a[i] # 0 THEN result := FALSE END;
r[i] := 0;
END;
RETURN result;
END Truncate;
BEGIN
END TWord.