(*====================================================================*) (* Peter M. Perchansky *) (* 412-1 Springside, Drive East *) (* Shillington, PA 19607 *) (* *) (* CompuServe 71301,344 *) (* FidoNet 1:273/101 *) (* Interlink *) (*====================================================================*) The following is my implementation of a generic deque using JPI's TopSpeed Modula-2 (version 1.17). A deque is a form of double-ended queue (or stack depending on your point of view). The deque abstract data type allows you to do perform queue and stack operations. The queue operations are based on the following: The front of the queue is the root node. Enqueueing data will place the node at the rear of the queue. Dequeueing (serving) data will remove the node from the front of the queue. The stack operations are based on the following: The top of the stack is the end node. Pushing data will place the node at the rear (top) of the stack. Popping data will remove data from the rear (top) of the stack. This library module is generic. Any data type (both standard and user-defined) can be operated on in the deque. You can even mix and match data types within the deque; however, you are responsible for keeping track of what types are in the deque. Future versions of this module will include procedures to compare deques, append one deque to another deque, search for elements within the deque, etc. Please feel free to contact me via postal mail, CompuServe, FidoNet, or Interlink (language or Pascal Conference ) if you have any questions or comments. <-------------------------- cut here ---------------------------------> (*==================================================================== = Program Id: PMPDEQUE = = Programmer: Peter M. Perchansky = = Purpose: Export procedures for dealing with deques. = = Date Written: 10-29-89 = ====================================================================*) DEFINITION MODULE PMPDeque; (*------------- Procedures from JPI's TopSpeed Modula II -------------*) FROM SYSTEM IMPORT BYTE; (*--------------------------------------------------------------------*) TYPE Deques; (* opaque type defined in implementation module *) (* A deque is a circular queue allowing for both queue and stack *) (* operations. This module is generic, allowing for operations on *) (* any data type. *) (* The deque must be initialized to NIL by calling Create (deque). *) (* Create (deque) should only be called once. DestroyDeque should *) (* be called when the deque is no longer needed. *) (* Procedures to initialize/remove the deque: *) (* CreateDeque - set deque to NIL *) (* DestroyDeque - remove deque from memory *) (* Functions providing status information on the deque: *) (* Empty - is deque NIL *) (* Full - is deque full *) (* DequeLength - number of nodes in deque *) (* DequePos - node number containing data (if found) *) (* InDeque - is data in deque *) (* Procedures to enter data in the deque: *) (* Enqueue - place data in rear of deque *) (* Push - place data in rear of deque *) (* Procedures to remove data from the deque: *) (* Dequeue - remove data from the front of the deque *) (* Pop - remove data from the rear of the deque *) (* Serve - remove nth item in the deque *) (* Procedures to show data in the deque: *) (* Front - show data from the front of the deque *) (* Back - show data from the rear of the deque *) (* Top - show data at top of the deque *) (* Retrieve - show data from nth item in the deque *) PROCEDURE CreateDeque (VAR deque: Deques); (* Creates the deque by making it NIL *) (* This procedure must be called prior to performing operations on *) (* the deque. *) PROCEDURE Empty (deque: Deques): BOOLEAN; (* Return TRUE if the deque is NIL *) PROCEDURE DestroyDeque (VAR deque: Deques); (* Destroys the deque by deallocating deque nodes and contents *) (* DestroyDeque should be called when the deque is no longer needed *) (* in memory. *) PROCEDURE Full (size: CARDINAL): BOOLEAN; (* Return TRUE if there is no Available memory for another deque node *) PROCEDURE DequeLength (deque: Deques): CARDINAL; (* Returns the number of nodes in the deque *) PROCEDURE DequePos (data: ARRAY OF BYTE; deque: Deques): CARDINAL; (* Returns the number of the node containing the data (if found). *) (* Returns 0 if not found. The front of the deque is treated as node *) (* number one. *) PROCEDURE InDeque (data: ARRAY OF BYTE; deque: Deques): BOOLEAN; (* Returns TRUE if data is found in deque. *) PROCEDURE Enqueue (data: ARRAY OF BYTE; VAR deque: Deques; VAR Ok: BOOLEAN); (* Place data in node at the back of the deque. Ok will be set to *) (* FALSE under the following conditions: Deque is full. *) PROCEDURE Push (data: ARRAY OF BYTE; VAR deque: Deques; VAR Ok: BOOLEAN); (* Place data in node at the back of the deque. Ok will be set to *) (* FALSE under the following conditions: Deque is full. *) PROCEDURE Dequeue (VAR data: ARRAY OF BYTE; VAR deque: Deques; VAR Ok: BOOLEAN); (* Place contents from node at the front of the deque into data. *) (* Remove the front node of the deque. Ok is set to FALSE upon the *) (* following conditions: deque is empty, size of data does not equal *) (* size of contents. *) PROCEDURE Pop (VAR data: ARRAY OF BYTE; VAR deque: Deques; VAR Ok: BOOLEAN); (* Place contents from node at the back of the deque into data. *) (* Remove the back node of the deque. Ok is set to FALSE upon the *) (* following conditions: deque is empty, size of data does not equal *) (* size of contents. *) PROCEDURE Serve (VAR data: ARRAY OF BYTE; nthItem: CARDINAL; VAR deque: Deques; VAR Ok: BOOLEAN); (* Place contents from nth node of the deque into data. Remove the *) (* nth node of the deque. Ok is set to FALSE upon the following *) (* conditions: deque is empty, size of data does not equal size of *) (* contents, nthItem is greater than number of nodes in the deque. *) PROCEDURE Front (VAR data: ARRAY OF BYTE; deque: Deques; VAR Ok: BOOLEAN); (* Place contents from node at the front of the deque into data. *) (* Ok is set to FALSE upon the following conditions: deque is empty, *) (* size of data does not equal size of contents. *) PROCEDURE Back (VAR data: ARRAY OF BYTE; deque: Deques; VAR Ok: BOOLEAN); (* Place contents from node at the back of the deque into data. *) (* Ok is set to FALSE upon the following conditions: deque is empty, *) (* size of data does not equal size of contents. *) PROCEDURE Top (VAR data: ARRAY OF BYTE; deque: Deques; VAR Ok: BOOLEAN); (* Place contents from node at the back (top) of the deque into data. *) (* Ok is set to FALSE upon the following conditions: deque is empty, *) (* size of data does not equal size of contents. *) PROCEDURE Retrieve (VAR data: ARRAY OF BYTE; nthItem: CARDINAL; deque: Deques; VAR Ok: BOOLEAN); (* Place contents from nth node of the deque into data. Ok is set to *) (* FALSE upon the following conditions: deque is empty, size of data *) (* does not equal size of contents. nthItem is greater than number *) (* of nodes in the deque. *) END PMPDeque. <-------------------------- cut here ---------------------------------> (*==================================================================== = Program Id: PMPDEQUE = = Programmer: Peter M. Perchansky = = Purpose: Export procedures for dealing deques. = = Date Written: 10-29-89 = ====================================================================*) IMPLEMENTATION MODULE PMPDeque; (*------------- Procedures from JPI's TopSpeed Modula II -------------*) FROM AsmLib IMPORT Compare, Move; FROM SYSTEM IMPORT ADDRESS, BYTE, TSIZE; FROM Storage IMPORT ALLOCATE, Available, DEALLOCATE; (*--------------------------------------------------------------------*) TYPE Deques = POINTER TO DequeNodes; (* opaque type defined *) DequeNodes = RECORD (* node *) contents : ADDRESS; (* contents of node *) size : CARDINAL; (* size of contents *) next : Deques; (* pointer to next node *) previous : Deques; (* pointer to prev node *) END; (*--------------------------------------------------------------------*) (* Utility procedures used internally by PMPDeque. *) (*--------------------------------------------------------------------*) PROCEDURE DeleteNode (VAR node: Deques); (* Deletes specified node from deque *) VAR nodeToDelete : Deques; (* save pointer for deletion *) BEGIN IF node^.next = node THEN (* Delete last node in deque *) DEALLOCATE (node^.contents, node^.size); DEALLOCATE (node, TSIZE (DequeNodes)); node := NIL; ELSE nodeToDelete := node; (* Delete specified node *) node := node^.next; nodeToDelete^.previous^.next := node; node^.previous := nodeToDelete^.previous; DEALLOCATE (nodeToDelete^.contents, nodeToDelete^.size); DEALLOCATE (nodeToDelete, TSIZE (DequeNodes)); END; END DeleteNode; PROCEDURE InsertNodeAtEnd (node: Deques; VAR deque: Deques); (* Inserts specified node at end of deque *) BEGIN IF Empty (deque) THEN (* create front *) node^.next := node; node^.previous := node; deque := node; ELSE (* add to end *) node^.next := deque; node^.previous := deque^.previous; deque^.previous^.next := node; deque^.previous := node; END; END InsertNodeAtEnd; (*--------------------------------------------------------------------*) (* Procedures exported by PMPDeque. *) (*--------------------------------------------------------------------*) PROCEDURE CreateDeque (VAR deque: Deques); (* Creates the deque by making it NIL *) (* This procedure must be called prior to performing operations on *) (* the deque. *) BEGIN deque := NIL; END CreateDeque; PROCEDURE Empty (deque: Deques) : BOOLEAN; (* Return TRUE if the deque is NIL *) BEGIN RETURN deque = NIL; END Empty; PROCEDURE DestroyDeque (VAR deque: Deques); (* Destroys the deque by deallocating deque nodes and contents *) (* DestroyDeque should be called when the deque is no longer needed *) (* in memory. *) BEGIN WHILE NOT Empty (deque) DO DeleteNode (deque); END; END DestroyDeque; PROCEDURE Full (size: CARDINAL): BOOLEAN; (* Return TRUE if there is no Available memory for another deque node *) BEGIN RETURN (NOT (Available (TSIZE (DequeNodes)) AND Available (size))); END Full; PROCEDURE DequeLength (deque : Deques) : CARDINAL; (* Returns the number of nodes in the deque *) VAR current : Deques; (* cursor used to walk the deque *) count : CARDINAL; (* node counter *) BEGIN count := 0; IF NOT Empty (deque) THEN current := deque^.previous; (* start from back of deque *) REPEAT INC (count); current := current^.next; (* and walk forward until *) UNTIL current = deque^.previous; (* we've reached the back *) END; RETURN count; END DequeLength; PROCEDURE DequePos (data: ARRAY OF BYTE; deque: Deques): CARDINAL; (* Returns the number of the node containing the data (if found). *) (* Returns 0 if not found. The front of the deque is treated as node *) (* number one. *) VAR count, (* node counter *) pos : CARDINAL; (* returned from AsmLib.Compare *) current : Deques; (* cursor used to walk the deque *) BEGIN count := 0; IF NOT Empty (deque) THEN current := deque^.previous; (* start from the back of deque *) REPEAT INC (count); current := current^.next; pos := Compare (ADR (data), current^.contents, current^.size); UNTIL (current = deque^.previous) OR (pos = current^.size); END; IF pos = current^.size THEN (* pos = size upon exact match *) RETURN count ELSE RETURN 0 END; END DequePos; PROCEDURE InDeque (data: ARRAY OF BYTE; deque: Deques): BOOLEAN; (* Returns TRUE if data is found in deque. *) BEGIN RETURN (DequePos (data, deque) # 0); END InDeque; PROCEDURE Enqueue (data: ARRAY OF BYTE; VAR deque: Deques; VAR Ok: BOOLEAN); (* Place data in node at the back of the deque. Ok will be set to *) (* FALSE under the following conditions: Deque is full. *) VAR newNode : Deques; (* used to create new node *) dataSize : CARDINAL; (* size of data *) BEGIN dataSize := HIGH (data) + 1; Ok := NOT Full (dataSize); IF Ok THEN ALLOCATE (newNode, TSIZE (DequeNodes)); ALLOCATE (newNode^.contents, dataSize); Move (ADR (data), newNode^.contents, dataSize); newNode^.size := dataSize; InsertNodeAtEnd (newNode, deque); END; END Enqueue; PROCEDURE Push (data: ARRAY OF BYTE; VAR deque: Deques; VAR Ok: BOOLEAN); (* Place data in node at the back of the deque. Ok will be set to *) (* FALSE under the following conditions: Deque is full. *) BEGIN Enqueue (data, deque, Ok); END Push; PROCEDURE Dequeue (VAR data: ARRAY OF BYTE; VAR deque: Deques; VAR Ok: BOOLEAN); (* Place contents from node at the front of the deque into data. *) (* Remove the front node of the deque. Ok is set to FALSE upon the *) (* following conditions: deque is empty, size of data does not equal *) (* size of contents. *) VAR dataSize : CARDINAL; (* size of data *) BEGIN Ok := NOT Empty (deque); IF Ok THEN dataSize := HIGH (data) + 1; Ok := deque^.size = dataSize; (* size of data must equal the size of contents *) IF Ok THEN Move (deque^.contents, ADR (data), deque^.size); DeleteNode (deque); END; END; END Dequeue; PROCEDURE Pop (VAR data: ARRAY OF BYTE; VAR deque: Deques; VAR Ok: BOOLEAN); (* Place contents from node at the back of the deque into data. *) (* Remove the back node of the deque. Ok is set to FALSE upon the *) (* following conditions: deque is empty, size of data does not equal *) (* size of contents. *) VAR dataSize : CARDINAL; (* size of data *) BEGIN Ok := NOT Empty (deque); IF Ok THEN dataSize := HIGH (data) + 1; IF deque^.next = deque THEN (* only one node *) Ok := deque^.size = dataSize; (* size of data must equal size of contents *) IF Ok THEN Move (deque^.contents, ADR (data), deque^.size); DeleteNode (deque); END; ELSE Ok := deque^.previous^.size = dataSize; (* size of data must equal size of contents *) IF Ok THEN Move (deque^.previous^.contents, ADR (data), deque^.previous^.size); DeleteNode (deque^.previous); END; END; END; END Pop; PROCEDURE Serve (VAR data: ARRAY OF BYTE; nthItem: CARDINAL; VAR deque: Deques; VAR Ok: BOOLEAN); (* Place contents from nth node of the deque into data. Remove the *) (* nth node of the deque. Ok is set to FALSE upon the following *) (* conditions: deque is empty, size of data does not equal size of *) (* contents, nthItem is greater than number of nodes in the deque. *) VAR current : Deques; (* used to walk the deque *) dataSize, (* size of data *) numberOfNodes, (* length of deque *) count : CARDINAL; (* node counter *) BEGIN Ok := NOT Empty (deque); IF Ok THEN numberOfNodes := DequeLength (deque); Ok := (nthItem <= numberOfNodes) AND (nthItem > 0); (* nthItem must be less than length of deque & greater than 0 *) IF Ok THEN dataSize := HIGH (data) + 1; count := 0; current := deque^.previous; (* start from back of deque *) REPEAT INC (count); (* walk from back until we *) current := current^.next; (* have the nth Item *) UNTIL count = nthItem; Ok := current^.size = dataSize; (* size of contents must equal size of data *) IF Ok THEN Move (current^.contents, ADR (data), current^.size); IF count = 1 THEN DeleteNode (deque) ELSE DeleteNode (current) END; END; END; END; END Serve; PROCEDURE Front (VAR data: ARRAY OF BYTE; deque: Deques; VAR Ok: BOOLEAN); (* Place contents from node at the front of the deque into data. *) (* Ok is set to FALSE upon the following conditions: deque is empty, *) (* size of data does not equal size of contents. *) VAR dataSize : CARDINAL; (* size of data *) BEGIN Ok := NOT Empty (deque); IF Ok THEN dataSize := HIGH (data) + 1; Ok := deque^.size = dataSize; (* size of data must equal size of contents *) IF Ok THEN Move (deque^.contents, ADR (data), deque^.size); END; END; END Front; PROCEDURE Back (VAR data: ARRAY OF BYTE; deque: Deques; VAR Ok: BOOLEAN); (* Place contents from node at the back of the deque into data. *) (* Ok is set to FALSE upon the following conditions: deque is empty, *) (* size of data does not equal size of contents. *) VAR dataSize : CARDINAL; (* size of data *) BEGIN Ok := NOT Empty (deque); IF Ok THEN dataSize := HIGH (data) + 1; Ok := deque^.previous^.size = dataSize; (* size of data must equal size of contents *) IF Ok THEN Move (deque^.previous^.contents, ADR (data), deque^.previous^.size); END; END; END Back; PROCEDURE Top (VAR data: ARRAY OF BYTE; deque: Deques; VAR Ok: BOOLEAN); (* Place contents from node at the back (top) of the deque into data. *) (* Ok is set to FALSE upon the following conditions: deque is empty, *) (* size of data does not equal size of contents. *) BEGIN Back (data, deque, Ok); END Top; PROCEDURE Retrieve (VAR data: ARRAY OF BYTE; nthItem: CARDINAL; deque: Deques ; VAR Ok: BOOLEAN); (* Place contents from nth node of the deque into data. Ok is set to *) (* FALSE upon the following conditions: deque is empty, size of data *) (* does not equal size of contents. nthItem is greater than number *) (* of nodes in the deque. *) VAR current : Deques; (* used to walk the deque *) dataSize, (* size of data *) numberOfNodes, (* length of deque *) count : CARDINAL; (* node counter *) BEGIN Ok := NOT Empty (deque); IF Ok THEN numberOfNodes := DequeLength (deque); Ok := (nthItem <= numberOfNodes) AND (nthItem > 0); (* nthItem must be less than length of deque & greater than 0 *) IF Ok THEN dataSize := HIGH (data) + 1; count := 0; current := deque^.previous; (* start from back of deque *) REPEAT INC (count); (* walk from back until we *) current := current^.next; (* have the nth Item *) UNTIL count = nthItem; Ok := current^.size = dataSize; (* size of contents must equal size of data *) IF Ok THEN Move (current^.contents, ADR (data), current^.size); END; END; END; END Retrieve; END PMPDeque.