language-Modula2-0.1: examples/Modula-2_Libraries/C.-Lins_Modula-2_Software_Component_Library/Vol1/SETS/SETSUUI.MOD
(*
13.8 SetSUUI Implementation
*)
IMPLEMENTATION MODULE SetSUUI;
(*==========================================================
Version : 1.00 30 Apr 1989 C. Lins
Compiler : TopSpeed Modula-2
Code Size: R- bytes
Component: Monolithic Structures - Set
Sequential Unbounded Unmanaged Iterator
INTRODUCTION
This module implements the unbounded Set abstraction for
generic Items using a linearly ordered list.
REVISION HISTORY
v1.00 30 Apr 1989 C. Lins
Initial implementation for TopSpeed Modula-2. Derived from
SetSUMI module.
(C) Copyright 1989 Charles A. Lins
=============================================================*)
FROM JPIStorage IMPORT
(*--Proc*) Allocate, Deallocate;
FROM Items IMPORT
(*--Type*) Item, CompareProc, AccessProc, LoopAccessProc;
FROM ErrorHandling IMPORT
(*--Type*) HandlerProc,
(*--Proc*) Raise, NullHandler, ExitOnError;
FROM Relations IMPORT
(*--Type*) Relation;
FROM SetEnum IMPORT
(*--Type*) Exceptions, Operations, ComponentID;
FROM TypeManager IMPORT
(*--Cons*) NullType,
(*--Type*) TypeID,
(*--Proc*) CompareOf;
(*--------------------*)
(*
13.8.1 Internal Unbounded Set Representation
*)
TYPE Link = POINTER TO Node;
TYPE Node = RECORD (*-- a set item node *)
item : Item; (*-- the item nodes' data *)
next : Link; (*-- link to next node in list *)
END (*-- Node *);
TYPE UnboundedSet = RECORD (*-- set header *)
dataID : TypeID; (*-- defined data type *)
length : CARDINAL; (*-- current set length, := 0 *)
first : Link; (*-- link to first Item node *)
END (*-- UnboundedSet *);
TYPE Set = POINTER TO UnboundedSet;
(*
13.8.2 Exceptions
*)
VAR setError : Exceptions;
VAR handlers : ARRAY Exceptions OF HandlerProc;
(*-----------------------*)
PROCEDURE SetError () : Exceptions (*-- out *);
BEGIN
RETURN setError;
END SetError;
(*--------------------*)
PROCEDURE GetHandler ( ofError : Exceptions (*-- in *))
: HandlerProc (*-- out *);
BEGIN
RETURN handlers[ofError];
END GetHandler;
(*--------------------*)
PROCEDURE SetHandler ( ofError : Exceptions (*-- in *);
toHandler : HandlerProc (*-- in *));
BEGIN
handlers[ofError] := toHandler;
END SetHandler;
(*--------------------*)
PROCEDURE RaiseErrIn ( theRoutine : Operations (*-- in *);
theError : Exceptions (*-- in *));
BEGIN
setError := theError;
Raise(ComponentID + ModuleID, theRoutine, theError, handlers[theError]);
END RaiseErrIn;
(*--------------------*)
(*
13.8.3 Local Routines
*)
VAR setRoutine : Operations; (*-- Routine calling NewNode *)
PROCEDURE NewNode ( theItem : Item (*-- in *);
theNext : Link (*-- in *))
: Link (*-- out *);
VAR newLink : Link;
BEGIN
Allocate(newLink, SIZE(Node));
IF (newLink = NIL) THEN
RaiseErrIn(setRoutine, overflow);
ELSE
WITH newLink^ DO
item := theItem;
next := theNext;
END (*--with*);
END (*--if*);
RETURN newLink;
END NewNode;
(*--------------------*)
PROCEDURE CopySubset ( fromIndex : Link (*-- in *);
toIndex : Link (*-- in *);
VAR toSet : Set (*-- inout *));
VAR tempNode : Link;
BEGIN
LOOP
IF (fromIndex = NIL) THEN
EXIT (*--loop*);
ELSE
tempNode := NewNode(fromIndex^.item, NIL);
IF (tempNode = NIL) THEN
EXIT (*--loop*);
END (*--if*);
INC(toSet^.length);
IF (toSet^.first = NIL) THEN
toSet^.first := tempNode;
ELSE
toIndex^.next := tempNode;
END (*--if*);
toIndex := tempNode;
fromIndex := fromIndex^.next;
END (*--if*);
END (*--loop*);
END CopySubset;
(*--------------------*)
PROCEDURE Recreate ( theType : TypeID (*-- in *);
VAR theSet : Set (*-- inout *))
: BOOLEAN (*-- out *);
BEGIN
IF (theSet # NIL) THEN
Clear(theSet);
theSet^.dataID := theType;
ELSE
theSet := Create(theType);
END (*--if*);
RETURN (setError = noerr);
END Recreate;
(*--------------------*)
(*
13.8.4 Constructors
*)
PROCEDURE Create ( theType : TypeID (*-- in *))
: Set (*-- out *);
VAR newSet : Set; (*-- new set variable being created *)
BEGIN
setError := noerr;
Allocate(newSet, SIZE(UnboundedSet));
IF (newSet # NIL) THEN
WITH newSet^ DO
dataID := theType;
length := 0;
first := NIL;
END (*--with*);
RETURN newSet;
END (*--if*);
RaiseErrIn(create, overflow);
RETURN NullSet;
END Create;
(*--------------------*)
PROCEDURE Destroy (VAR theSet : Set (*-- inout *));
BEGIN
Clear(theSet);
IF (setError = noerr) THEN
Deallocate(theSet, SIZE(theSet^));
END (*--if*);
END Destroy;
(*--------------------*)
PROCEDURE Clear (VAR theSet : Set (*-- inout *));
VAR theNode : Link; (*-- Set node to be deallocated *)
BEGIN
setError := noerr;
IF (theSet # NIL) THEN
WITH theSet^ DO
WHILE (first # NIL) DO
theNode := first;
first := first^.next;
Deallocate(theNode, SIZE(theNode^));
END (*--while*);
length := 0;
END (*--with*);
ELSE
RaiseErrIn(clear, undefined);
END (*--if*);
END Clear;
(*--------------------*)
PROCEDURE Assign ( theSet : Set (*-- in *);
VAR toSet : Set (*-- inout *));
VAR fromIndex : Link; (*-- Loop index over source set items *)
toIndex : Link; (*-- Loop index over target set items *)
BEGIN
setError := noerr;
IF (theSet # NIL) THEN
IF (toSet = theSet) OR ~Recreate(theSet^.dataID, toSet) THEN
RETURN;
END (*--if*);
WITH theSet^ DO
IF (first = NIL) THEN
RETURN;
END (*--if*);
END (*--with*);
setRoutine := assign;
WITH toSet^ DO
first := NewNode(theSet^.first^.item, NIL);
IF (setError = overflow) THEN
RETURN;
END (*--if*);
toIndex := first;
fromIndex := theSet^.first;
END (*--with*);
WHILE (fromIndex^.next # NIL) DO
fromIndex := fromIndex^.next;
toIndex^.next := NewNode(fromIndex^.item, NIL);
IF (setError = overflow) THEN
RETURN;
END (*--if*);
toIndex := toIndex^.next;
END (*--while*);
toSet^.length := theSet^.length;
ELSE
RaiseErrIn(assign, undefined);
END (*--if*);
END Assign;
(*--------------------*)
PROCEDURE Include ( theItem : Item (*-- in *);
VAR inSet : Set (*-- inout *));
VAR current : Link; (*-- Loop index in search of theItem *)
previous : Link; (*-- Previous node examined *)
newNode : Link; (*-- For new node to add *)
compareItem : CompareProc; (*-- Item comparison routine *)
itemOrder : Relation; (*-- Ordering relation between items *)
BEGIN
setError := noerr;
IF (inSet # NIL) THEN
compareItem := CompareOf(inSet^.dataID);
current := inSet^.first;
previous := NIL;
LOOP
IF (current = NIL) THEN
EXIT (*--loop*);
END (*--if*);
itemOrder := compareItem(current^.item, theItem);
IF (itemOrder = equal) THEN
RETURN;
ELSIF (itemOrder = greater) THEN
EXIT (*--loop*);
END (*--if*);
previous := current;
current := current^.next;
END (*--loop*);
(*-- Insert the new item *)
Allocate(newNode, SIZE(Node));
IF (newNode = NIL) THEN
RaiseErrIn(include, overflow);
ELSE
newNode^.item := theItem;
IF (previous = NIL) THEN
newNode^.next := inSet^.first;
inSet^.first := newNode;
ELSE
newNode^.next := current;
previous^.next := newNode;
END (*--if*);
INC(inSet^.length);
END (*--if*);
ELSE
RaiseErrIn(include, undefined);
END (*--if*);
END Include;
(*--------------------*)
PROCEDURE Exclude ( theItem : Item (*-- in *);
VAR fromSet : Set (*-- inout *));
VAR current : Link; (*-- Loop index over items *)
previous : Link; (*-- Previous node examined *)
compareItem : CompareProc; (*-- Item comparison routine *)
itemOrder : Relation; (*-- Relation between items *)
BEGIN
setError := noerr;
IF (fromSet # NIL) THEN
compareItem := CompareOf(fromSet^.dataID);
current := fromSet^.first;
previous := NIL;
LOOP
IF (current = NIL) THEN
RETURN;
END (*--if*);
itemOrder := compareItem(current^.item, theItem);
IF (itemOrder = equal) THEN
EXIT (*--loop*);
ELSIF (itemOrder = greater) THEN
RETURN;
END (*--if*);
previous := current;
current := current^.next;
END (*--loop*);
(*-- ╥current╙ points to the node to be deleted. *)
IF (previous = NIL) THEN
fromSet^.first := current^.next;
ELSE
previous^.next := current^.next;
END (*--if*);
Deallocate(current, SIZE(current^));
DEC(fromSet^.length);
ELSE
RaiseErrIn(exclude, undefined);
END (*--if*);
END Exclude;
(*--------------------*)
PROCEDURE Union ( left : Set (*-- in *);
right : Set (*-- in *);
VAR toSet : Set (*-- inout *));
VAR leftIndex : Link; (*-- Loop index over left set *)
rightIndex : Link; (*-- Loop index over right set *)
toIndex : Link; (*-- List of target set nodes *)
tempNode : Link; (*-- Temporary node *)
compareItem : CompareProc; (*-- Item comparison routine *)
order : Relation; (*-- Ordering relation between items *)
BEGIN
setError := noerr;
IF (left = NIL) OR (right = NIL) THEN
RaiseErrIn(union, undefined);
RETURN;
ELSIF (left^.dataID # right^.dataID) THEN
RaiseErrIn(union, typeerror);
RETURN;
ELSIF ~Recreate(left^.dataID, toSet) THEN
RETURN;
END (*--if*);
WITH toSet^ DO
compareItem := CompareOf(dataID);
END (*--with*);
setRoutine := union;
leftIndex := left^.first;
rightIndex := right^.first;
WHILE (leftIndex # NIL) & (rightIndex # NIL) DO
order := compareItem(leftIndex^.item, rightIndex^.item);
Allocate(tempNode, SIZE(Node));
IF (tempNode = NIL) THEN
RaiseErrIn(union, overflow);
RETURN;
END (*--if*);
tempNode^.next := NIL;
INC(toSet^.length);
IF (order = less) THEN
tempNode^.item := leftIndex^.item;
leftIndex := leftIndex^.next;
ELSIF (order = equal) THEN
tempNode^.item := leftIndex^.item;
leftIndex := leftIndex^.next;
rightIndex := rightIndex^.next;
ELSE
tempNode^.item := rightIndex^.item;
rightIndex := rightIndex^.next;
END (*--if*);
(*-- Update the linked list *)
IF (toSet^.first = NIL) THEN
toSet^.first := tempNode;
ELSE
toIndex^.next := tempNode;
END (*--if*);
toIndex := tempNode;
END (*--while*);
IF (leftIndex = NIL) THEN
CopySubset(rightIndex, toIndex, toSet);
ELSIF (rightIndex = NIL) THEN
CopySubset(leftIndex, toIndex, toSet);
END (*--if*);
END Union;
(*--------------------*)
PROCEDURE Intersection ( left : Set (*-- in *);
right : Set (*-- in *);
VAR toSet : Set (*-- inout *));
VAR leftIndex : Link; (*-- Loop link over left set *)
rightIndex : Link; (*-- Loop link over right set *)
toIndex : Link; (*-- List of target set nodes *)
compareItem : CompareProc; (*-- Item comparison routine *)
order : Relation; (*-- Ordering relation between items *)
BEGIN
setError := noerr;
IF (left = NIL) OR (right = NIL) THEN
RaiseErrIn(intersection, undefined);
RETURN;
ELSIF (left^.dataID # right^.dataID) THEN
RaiseErrIn(intersection, typeerror);
RETURN;
ELSIF ~Recreate(left^.dataID, toSet) THEN
RETURN;
END (*--if*);
WITH toSet^ DO
compareItem := CompareOf(dataID);
END (*--with*);
setRoutine := intersection;
leftIndex := left^.first;
rightIndex := right^.first;
WHILE (leftIndex # NIL) & (rightIndex # NIL) DO
order := compareItem(leftIndex^.item, rightIndex^.item);
IF (order = equal) THEN
IF (toSet^.first = NIL) THEN
toSet^.first := NewNode(leftIndex^.item, NIL);
toIndex := toSet^.first;
ELSE
toIndex^.next := NewNode(leftIndex^.item, NIL);
toIndex := toIndex^.next;
END (*--if*);
IF (setError = overflow) THEN
RETURN;
END (*--if*);
INC(toSet^.length);
leftIndex := leftIndex^.next;
rightIndex := rightIndex^.next;
ELSIF (order = less) THEN
leftIndex := leftIndex^.next;
ELSE
rightIndex := rightIndex^.next;
END (*--if*);
END (*--while*);
END Intersection;
(*--------------------*)
PROCEDURE Difference ( left : Set (*-- in *);
right : Set (*-- in *);
VAR toSet : Set (*-- inout *));
VAR leftIndex : Link; (*-- Loop index over left set *)
rightIndex : Link; (*-- Loop index over right set *)
toIndex : Link; (*-- List of target set nodes *)
compareItem : CompareProc; (*-- Item comparison routine *)
order : Relation; (*-- Ordering relation between items *)
BEGIN
setError := noerr;
IF (left = NIL) OR (right = NIL) THEN
RaiseErrIn(difference, undefined);
RETURN;
ELSIF (left^.dataID # right^.dataID) THEN
RaiseErrIn(difference, typeerror);
RETURN;
ELSIF ~Recreate(left^.dataID, toSet) THEN
RETURN;
END (*--if*);
WITH toSet^ DO
compareItem := CompareOf(dataID);
END (*--with*);
setRoutine := difference;
leftIndex := left^.first;
rightIndex := right^.first;
WHILE (leftIndex # NIL) & (rightIndex # NIL) DO
order := compareItem(leftIndex^.item, rightIndex^.item);
IF (order = equal) THEN
leftIndex := leftIndex^.next;
rightIndex := rightIndex^.next;
ELSIF (order = less) THEN
IF (toSet^.first = NIL) THEN
toSet^.first := NewNode(leftIndex^.item, NIL);
toIndex := toSet^.first;
ELSE
toIndex^.next := NewNode(leftIndex^.item, NIL);
toIndex := toIndex^.next;
END (*--if*);
IF (setError = overflow) THEN
RETURN;
END (*--if*);
INC(toSet^.length);
leftIndex := leftIndex^.next;
ELSE
rightIndex := rightIndex^.next;
END (*--if*);
END (*--while*);
CopySubset(leftIndex, toIndex, toSet);
END Difference;
(*--------------------*)
PROCEDURE SymDifference ( left : Set (*-- in *);
right : Set (*-- in *);
VAR toSet : Set (*-- inout *));
VAR leftIndex : Link; (*-- Loop index over left set *)
rightIndex : Link; (*-- Loop index over right set *)
toIndex : Link; (*-- List of target set nodes *)
tempNode : Link; (*-- Temporary node *)
compareItem : CompareProc; (*-- Item comparison routine *)
order : Relation; (*-- Ordering relation between items *)
BEGIN
setError := noerr;
IF (left = NIL) OR (right = NIL) THEN
RaiseErrIn(symdifference, undefined);
RETURN;
ELSIF (left^.dataID # right^.dataID) THEN
RaiseErrIn(symdifference, typeerror);
RETURN;
ELSIF ~Recreate(left^.dataID, toSet) THEN
RETURN;
END (*--if*);
WITH toSet^ DO
compareItem := CompareOf(dataID);
END (*--with*);
setRoutine := symdifference;
leftIndex := left^.first;
rightIndex := right^.first;
WHILE (leftIndex # NIL) & (rightIndex # NIL) DO
order := compareItem(leftIndex^.item, rightIndex^.item);
IF (order = equal) THEN
leftIndex := leftIndex^.next;
rightIndex := rightIndex^.next;
ELSE
Allocate(tempNode, SIZE(Node));
IF (tempNode = NIL) THEN
RaiseErrIn(symdifference, overflow);
RETURN;
END (*--if*);
INC(toSet^.length);
IF (order = less) THEN
tempNode^.item := leftIndex^.item;
leftIndex := leftIndex^.next;
ELSE
tempNode^.item := rightIndex^.item;
rightIndex := rightIndex^.next;
END (*--if*);
IF (toSet^.first = NIL) THEN
toSet^.first := tempNode;
ELSE
toIndex^.next := tempNode;
END (*--if*);
toIndex := tempNode;
END (*--if*);
END (*--while*);
IF (leftIndex = NIL) THEN
CopySubset(rightIndex, toIndex, toSet);
ELSIF (rightIndex = NIL) THEN
CopySubset(leftIndex, toIndex, toSet);
END (*--if*);
END SymDifference;
(*--------------------*)
(*
13.8.5 Selectors
*)
PROCEDURE IsDefined ( theSet : Set (*-- in *))
: BOOLEAN (*-- out *);
BEGIN
RETURN (theSet # NIL);
END IsDefined;
(*--------------------*)
PROCEDURE IsEmpty ( theSet : Set (*-- in *))
: BOOLEAN (*-- out *);
BEGIN
setError := noerr;
IF (theSet # NIL) THEN
RETURN (theSet^.length = 0);
END (*--if*);
RaiseErrIn(isempty, undefined);
RETURN TRUE;
END IsEmpty;
(*--------------------*)
PROCEDURE TypeOf ( theSet : Set (*-- in *))
: TypeID (*-- out *);
BEGIN
setError := noerr;
IF (theSet # NIL) THEN
RETURN theSet^.dataID;
END (*--if*);
RaiseErrIn(typeof, undefined);
RETURN NullType;
END TypeOf;
(*--------------------*)
PROCEDURE IsEqual ( left : Set (*-- in *);
right : Set (*-- in *))
: BOOLEAN (*-- out *);
VAR leftIndex : Link; (*-- Loop index over left set items *)
rightIndex: Link; (*-- Loop index over right set items *)
BEGIN
setError := noerr;
IF (left # NIL) & (right # NIL) THEN
IF (left^.dataID = right^.dataID) THEN
IF (left^.length = right^.length) THEN
leftIndex := left^.first;
rightIndex:= right^.first;
WHILE (leftIndex # NIL) DO
IF (leftIndex^.item # rightIndex^.item) THEN
RETURN FALSE;
END (*--if*);
leftIndex := leftIndex^.next;
rightIndex:= rightIndex^.next;
END (*--while*);
RETURN TRUE;
END (*--if*);
ELSE
RaiseErrIn(isequal, typeerror);
END (*--if*);
ELSE
RaiseErrIn(isequal, undefined);
END (*--if*);
RETURN FALSE;
END IsEqual;
(*----------------------------*)
PROCEDURE NumMembers ( theSet : Set (*-- in *))
: CARDINAL (*-- out *);
BEGIN
setError := noerr;
IF (theSet # NIL) THEN
RETURN theSet^.length;
END (*--if*);
RaiseErrIn(nummembers, undefined);
RETURN 0;
END NumMembers;
(*----------------------------*)
PROCEDURE IsAMember ( theItem : Item (*-- in *);
theSet : Set (*-- in *))
: BOOLEAN (*-- out *);
VAR index : Link; (*-- Loop index over items *)
compareItem : CompareProc; (*-- Item comparison routine *)
BEGIN
setError := noerr;
IF (theSet # NIL) THEN
WITH theSet^ DO
compareItem := CompareOf(dataID);
index := first;
END (*--with*);
WHILE (index # NIL) DO
IF (theItem = index^.item) THEN
RETURN TRUE;
ELSIF (compareItem(index^.item, theItem) = greater) THEN
RETURN FALSE;
END (*--if*);
index := index^.next;
END (*--while*);
ELSE
RaiseErrIn(ismember, undefined);
END (*--if*);
RETURN FALSE;
END IsAMember;
(*----------------------------*)
PROCEDURE IsSubset ( left : Set (*-- in *);
right : Set (*-- in *))
: BOOLEAN (*-- out *);
VAR leftIndex : Link; (*-- Loop index over left set *)
rightIndex : Link; (*-- Loop index over right set *)
compareItem : CompareProc; (*-- Item comparison routine *)
order : Relation; (*-- Ordering relation between items *)
BEGIN
setError := noerr;
IF (left = NIL) OR (right = NIL) THEN
RaiseErrIn(issubset, undefined);
RETURN FALSE;
ELSIF (left^.dataID # right^.dataID) THEN
RaiseErrIn(issubset, typeerror);
RETURN FALSE;
END (*--if*);
compareItem:= CompareOf(left^.dataID);
leftIndex := left^.first;
rightIndex := right^.first;
WHILE (leftIndex # NIL) & (rightIndex # NIL) DO
order := compareItem(leftIndex^.item, rightIndex^.item);
IF (order = equal) THEN
leftIndex := leftIndex^.next;
rightIndex := rightIndex^.next;
ELSIF (order = less) THEN
RETURN FALSE;
ELSE
rightIndex := rightIndex^.next;
END (*--if*);
END (*--while*);
RETURN (leftIndex = NIL);
END IsSubset;
(*----------------------------*)
PROCEDURE IsProperSubset( left : Set (*-- in *);
right : Set (*-- in *))
: BOOLEAN (*-- out *);
BEGIN
RETURN IsSubset(left, right) & (left^.length < right^.length);
END IsProperSubset;
(*----------------------------*)
(*
13.8.6 Iterators
*)
PROCEDURE LoopOver ( theSet : Set (*-- in *);
process : LoopAccessProc (*-- in *));
VAR theNode : Link; (*-- Loop index over items *)
BEGIN
setError := noerr;
IF (theSet # NIL) THEN
theNode := theSet^.first;
WHILE (theNode # NIL) & process(theNode^.item) DO
theNode := theNode^.next;
END (*--while*);
ELSE
RaiseErrIn(loopover, undefined);
END (*--if*);
END LoopOver;
(*----------------------------*)
PROCEDURE Traverse ( theSet : Set (*-- in *);
process : AccessProc (*-- in *));
VAR theNode : Link; (*-- Loop index over items *)
BEGIN
setError := noerr;
IF (theSet # NIL) THEN
theNode := theSet^.first;
WHILE (theNode # NIL) DO
process(theNode^.item);
theNode := theNode^.next;
END (*--while*);
ELSE
RaiseErrIn(traverse, undefined);
END (*--if*);
END Traverse;
(*----------------------------*)
(*
13.8.7 Module Initialization
*)
BEGIN
FOR setError := MIN(Exceptions) TO MAX(Exceptions) DO
handlers[setError] := ExitOnError;
END (*--for*);
handlers[noerr] := NullHandler;
setError := noerr;
END SetSUUI.