packages feed

language-Modula2-0.1: examples/Modula-2_Libraries/PMOS/sources/new/longadd.mod

MODULE LongAdd;

    (* Quick prototype, crudely tested, undocumented.  PJM 31/8/94. *)

FROM Storage1 IMPORT
    (* proc *)	ALLOCATE;

FROM InOut IMPORT
    (* proc *)	Write;

(************************************************************************)

TYPE
    Digit = [0..9];

    DigitPtr = POINTER TO DigitRecord;
    DigitRecord = RECORD
			value: Digit;
			previous, next: DigitPtr;
		  END (*RECORD*);

    LongNumber = POINTER TO RECORD
				head, tail: DigitPtr;
			    END (*RECORD*);

(************************************************************************)

PROCEDURE CreateLongNumber(): LongNumber;

    VAR result: LongNumber;

    BEGIN
	NEW (result);
	WITH result^ DO
	    head := NIL;  tail := NIL;
	END (*WITH*);
	RETURN result;
    END CreateLongNumber;

(************************************************************************)

PROCEDURE InsertAtHead (newelt: DigitPtr;  number: LongNumber);

    (* Inserts a new element at the head of "number"; the caller is	*)
    (* responsible for filling in the value.				*)

    BEGIN
	WITH newelt^ DO
	    previous := NIL;  next := number^.head;
	END (*WITH*);
	WITH number^ DO
	    head := newelt;
	    IF tail = NIL THEN tail := head END(*IF*);
	END (*WITH*);
    END InsertAtHead;

(************************************************************************)

PROCEDURE InsertAtTail (newelt: DigitPtr;  number: LongNumber);

    (* Inserts a new element at the tail of "number"; the caller is	*)
    (* responsible for filling in the value.				*)

    BEGIN
	WITH newelt^ DO
	    previous := number^.tail;  next := NIL;
	END (*WITH*);
	WITH number^ DO
	    tail := newelt;
	    IF head = NIL THEN head := tail END(*IF*);
	END (*WITH*);
    END InsertAtTail;

(************************************************************************)

PROCEDURE StringToLongNumber (s: ARRAY OF CHAR): LongNumber;

    CONST Nul = CHR(0);

    VAR result: LongNumber;
	j: CARDINAL;  current: DigitPtr;

    BEGIN
	result := CreateLongNumber();
	j := 0;
	WHILE (j <= HIGH(s)) AND (s[j] <> Nul) DO
	    NEW (current);
	    current^.value := ORD(s[j]) - ORD("0");
	    InsertAtTail (current, result);
	    INC (j);
	END (*WHILE*);
	RETURN result;
    END StringToLongNumber;

(************************************************************************)

PROCEDURE WriteLongNumber (num: LongNumber);

    VAR current: DigitPtr;

    BEGIN
	current := num^.head;
	IF current = NIL THEN Write ("0")
	ELSE
	    WHILE current <> NIL DO
		Write (CHR(current^.value+ORD("0")));
		current := current^.next;
	    END (*WHILE*);
	END (*IF*);
    END WriteLongNumber;

(************************************************************************)

PROCEDURE DigitAdd (x, y: Digit;  sum: DigitPtr;
					CarryIn: BOOLEAN): BOOLEAN;

    (* Single digit addition.  The function result is the carry out.	*)

    VAR result: CARDINAL;

    BEGIN
	result := x + y;
	IF CarryIn THEN INC(result) END(*IF*);
	IF result < 10 THEN
	    sum^.value := result;
	    RETURN FALSE;
	ELSE
	    sum^.value := result - 10;
	    RETURN TRUE;
	END (*IF*);
    END DigitAdd;

(************************************************************************)

PROCEDURE Add (first, second: LongNumber): LongNumber;

    (* Adds two numbers.  A bit too complicated, we ought to be able	*)
    (* to use some hindsight to simplify this.				*)

    VAR result: LongNumber;  shorter, longer, current: DigitPtr;
	carry: BOOLEAN;

    BEGIN
	result := CreateLongNumber();
	longer := first^.tail;
	shorter := second^.tail;
	carry := FALSE;
	WHILE shorter <> NIL DO
	    IF longer = NIL THEN
		longer := shorter;  shorter := NIL;
	    ELSE
		NEW (current);
		InsertAtHead (current, result);
		carry := DigitAdd (shorter^.value, longer^.value,
						current, carry);
		shorter := shorter^.previous;  longer := longer^.previous;
	    END (*IF*);
	END (*WHILE*);

	(* We've now traversed the shorter input list, but there	*)
	(* might be some of the longer still left.			*)

	WHILE longer <> NIL DO
	    NEW (current);
	    InsertAtHead (current, result);
	    carry := DigitAdd (0, longer^.value, current, carry);
	    longer := longer^.previous;
	END (*WHILE*);

	(* Allow for a final carry. *)

	IF carry THEN
	    NEW (current);
	    InsertAtHead (current, result);
	    current^.value := 1;
	END (*IF*);

	RETURN result;

    END Add;

(************************************************************************)

VAR first, second, sum: LongNumber;

BEGIN

    (* Test program. *)

    first := StringToLongNumber ("54321");
    second := StringToLongNumber ("42");
    sum := Add (first, second);
    WriteLongNumber (sum);

END LongAdd.