Fundamentals of computer science II

One of the remaining limitations of the ListsWithCursors module is that there is a preferred direction of movement through the list; it's much easier to start at the beginning of the list and advance to the end than to start at the end and move backwards to the beginning. There are a fair number of cases (including the implementation of bignums -- natural numbers of unbounded size) in which the possibility of traversing the list in either direction is essential. To accommodate this need, I propose the bidirectional list data type (with cursor).

The values of this type are the same as those of lists with cursors, but in addition to the repertoire of operations described in the handout on lists with cursors, the following operations, all involving the cursor:

cursor-to-end
Input: operand, a bidirectional list.
Outputs: none.
Preconditions: none.
Postconditions: If the length of operand is 0, its cursor is the null cursor; otherwise, its cursor is marking its last position.

retreat-cursor
Input: operand, a bidirectional list.
Outputs: none.
Precondition: The cursor of operand is not null.
Postconditions: If the cursor of operand at input marks the first position in operand, then it is a null cursor at output. Otherwise, the cursor of operand at output marks the position preceding the position that it marked at input.

find-previous
Inputs: sought and operand, where operand is a bidirectional list and sought is a value of its element type.
Outputs: none.
Preconditions: none.
Postconditions: Let p be 0 if the cursor of operand was null at input; otherwise, let p be the position it marked. Either the cursor of operand at output is null and sought is not in any position less than p, or sought is the element at the position q marked by the cursor of operand at output, q is less than p, and sought is not in any position less than p and greater than q.

find-previous-by-test
Inputs: operand, a list, and test, an operation that takes one input, a value of the element type of operand, and yields one output, a Boolean.
Outputs: none.
Preconditions: none.
Postconditions: Let p be 0 if the cursor of operand was null at input; otherwise, let p be the position it marked. Either (1) the cursor of operand at output is null and applying test to the element in any position less than p yields false, or (2) applying test to the element at the position q marked by the cursor of operand at output yields true, q is less than p, and applying test to the element in any of the positions less than p and greater than q yields false.

A BidirectionalLists module in HP Pascal

The most natural implementation of bidirectional lists uses a doubly-linked structure in which each component of the list contains both an ``aft'' pointer to the component that follows it and a ``fore'' pointer to the component that precedes it. The fore pointer of the first component of the list, like the aft pointer of the last one, is nil.

As in the ListsWithCursors module, it is convenient to provide a header node for each bidirectional list, containing pointers to the first and last elements of the list, an indication of its size, and the list's cursor.

One nice feature of doubly-linked lists is that it eliminates the annoying special case from the implementation of the insert-at-cursor and delete-at-cursor operations; the addition of the fore pointer gives us a way of finding the previous component in constant time.

{ This module defines an interface for a bidirectional list data type that
  includes a cursor in each list and implements it for HP 9000 Series 700
  workstations under HP-UX 9.x, using HP Pascal.

  Programmer: John Stone, Grinnell College.
  Original version: November 1-5, 1996
}

$heap_dispose on$

module BidirectionalLists;

$search 'elements.o'$
import Elements;

export

  type
    BidirectionalList = ^BidirectionalListRecord;       { an opaque type }

  { The MakeEmptyBidirectionalList function creates and returns a newly
    allocated, empty, bidirectional list. }

  function MakeEmptyBidirectionalList: BidirectionalList;

  { The PrependToBidirectionalList procedure takes a bidirectional list and
    prepends an element to it. }

  procedure PrependToBidirectionalList (Prefix: Element;
    var Base: BidirectionalList); 

  { The FirstOfBidirectionalList function returns the first element of a
    non-empty bidirectional list. }

  function FirstOfBidirectionalList (Operand: BidirectionalList): Element;

  { The DeleteFirstOfBidirectionalList function takes a non-empty
    bidirectional list and removes its first element. }

  procedure DeleteFirstOfBidirectionalList (var Operand: BidirectionalList);

  { The EmptyBidirectionalList function determines whether a given
    bidirectional list is empty. }

  function EmptyBidirectionalList (Operand: BidirectionalList): Boolean;

  { The LengthOfBidirectionalList function returns the length of a given
    bidirectional list. }

  function LengthOfBidirectionalList (Operand: BidirectionalList): Integer;

  { The AppendToBidirectionalList procedure takes a bidirectional list and
    appends an element to it. } 

  procedure AppendToBidirectionalList (var Base: BidirectionalList;
    Postfix: Element);

  { The LastOfBidirectionalList function returns the last element of a
    non-empty bidirectional list. } 

  function LastOfBidirectionalList (Operand: BidirectionalList): Element;

  { The DeleteLastOfBidirectionalList procedure takes a non-empty
    bidirectional list and removes its last element. }

  procedure DeleteLastOfBidirectionalList (var Operand: BidirectionalList);

  { The CopyBidirectionalList function constructs and returns a copy of a
    given bidirectional list, in newly allocated storage. }

  function CopyBidirectionalList (Operand: BidirectionalList):
    BidirectionalList;

  { The CursorToStartOfBidirectionalList procedure adjusts a bidirectional
    list's cursor so that it marks the first position (or is null, if the
    list is empty). } 

  procedure CursorToStartOfBidirectionalList (var Operand: BidirectionalList);

  { The CursorToEndOfBidirectionalList procedure adjusts a bidirectional
    list's cursor so that it marks the last position (or is null, if the
    list is empty). } 

  procedure CursorToEndOfBidirectionalList (var Operand: BidirectionalList);

  { The AdvanceCursorAlongBidirectionalList procedure moves a bidirectional
    list's cursor, assumed not to be null, to the next position on the list
    (or makes it null, if there are no more positions. }

  procedure AdvanceCursorAlongBidirectionalList (var Operand:
    BidirectionalList);

  { The RetreatCursorAlongBidirectionalList procedure moves a bidirectional
    list's cursor, assumed not to be null, to the previous position on the
    list (or makes it null, if there are no previous positions. }

  procedure RetreatCursorAlongBidirectionalList (var Operand:
    BidirectionalList);

  { The NullCursorInBidirectionalList function determines whether a given
    bidirectional list's cursor is null. }

  function NullCursorInBidirectionalList (Operand: BidirectionalList):
    Boolean; 

  { The ConcatenateBidirectionalList procedure takes two bidirectional
    lists and adds the elements of the second operand at the end of the
    first operand. } 

  procedure ConcatenateBidirectionalList (var LeftOperand:
    BidirectionalList; RightOperand: BidirectionalList); 

  { The RecoverByPositionFromBidirectionalList function returns the value
    occupying a specified position in a bidirectional list. }

  function RecoverByPositionFromBidirectionalList (Position: Integer;
    Ls:  BidirectionalList): Element;

  { The ElementAtCursorInBidirectionalList function returns the element
    occupying the position marked by the cursor in a given bidirectional
    list. } 

  function ElementAtCursorInBidirectionalList (Operand: BidirectionalList):
    Element; 

  { The AssignAtPositionInBidirectionalList procedure replaces the value
    occupying a specified position in a bidirectional list with a new
    value. } 

  procedure AssignAtPositionInBidirectionalList (Position: Integer;
    var Ls: BidirectionalList; NewValue: Element);

  { The AssignAtCursorInBidirectionalList procedure replaces the element
    occupying the position marked by the cursor in a given bidirectional
    list with a new value. } 

  procedure AssignAtCursorInBidirectionalList (var Ls: BidirectionalList;
    NewValue: Element);

  { The ElementOfBidirectionalList function determines whether a given
    value is an element of a given bidirectional list. }

  function ElementOfBidirectionalList (Candidate: Element;
    Ls: BidirectionalList): Boolean;

  { The LocateInBidirectionalList procedure determines whether a given
    value is an element of a given bidirectional list and, if so, returns
    the least position at which it occurs. } 

  procedure LocateInBidirectionalList (Sought: Element;
    Ls:BidirectionalList; var Found: Boolean; var Position: Integer); 

  { The FindNextInBidirectionalList procedure advances a given
    bidirectional list's cursor to the next position occupied by a
    specified value, leaving it null if there is no such position. }

  procedure FindNextInBidirectionalList (Sought: Element;
    var Ls: BidirectionalList);

  { The FindPreviousInBidirectionalList procedure moves a given
    bidirectional list's cursor to the nearest lower-numbered position
    occupied by a specified value, leaving it null if there is no such
    position. } 

  procedure FindPreviousInBidirectionalList (Sought: Element;
    var Ls: BidirectionalList);

  { The SublistOfBidirectionalList function takes a bidirectional list and
    prunes off all of it but a section bounded by the positions indicated
    by Start and Finish. }  

  procedure SublistOfBidirectionalList (var Ls: BidirectionalList;
    Start, Finish: Integer);

  { The ReverseBidirectionalList procedure reverses the order of the
    elements in a given bidirectional list. }

  procedure ReverseBidirectionalList (var Operand: BidirectionalList);

  { The InsertAtPositionInBidirectionalList procedure inserts an element at
    a specified position in a bidirectional list. }

  procedure InsertAtPositionInBidirectionalList (var Ls: BidirectionalList;
    Position: Integer; Elm: Element);

  { The InsertAtCursorInBidirectionalList procedure inserts a new element
    into a bidirectional.list at the position marked by the list's cursor,
    or at the end if the list's cursor is null. }

  procedure InsertAtCursorInBidirectionalList (var Ls: BidirectionalList;
    Elm: Element); 

  { The DeleteAtPositionInBidirectionalList procedure removes the element
    at a specified position in a bidirectional list. }

  procedure DeleteAtPositionInBidirectionalList (var Ls: BidirectionalList;
    Position: Integer);

  { The DeleteAtCursorInBidirectionalList procedure deletes and discards
    the element at the position in a given bidirectional list that is
    marked by the list's cursor. } 

  procedure DeleteAtCursorInBidirectionalList (var Ls: BidirectionalList);

  { The DeleteValueFromBidirectionalList procedure strips all occurrences
    of a specified value out of a bidirectional list. }

  procedure DeleteValueFromBidirectionalList (var Ls: BidirectionalList;
     Delend: Element);

  { The ReplaceInBidirectionalList procedure replaces every occurrence of a
    specified value in a bidirectional list with a new value. }

  procedure ReplaceInBidirectionalList (var Ls: BidirectionalList;
    Displacer, Displaced: Element);

  { The FillBidirectionalList function constructs and returns a
    bidirectional list consisting of a specified number of copies of a
    given element. } 

  function FillBidirectionalList (Length: Integer; Filler: Element):
    BidirectionalList; 

  { The GenerateBidirectionalList function constructs a bidirectional list
    of a specified length by applying a given function to the positive
    integers in ascending order until that length is reached. }

  function GenerateBidirectionalList (function Generator (N: Integer): Element;
    Length: Integer): BidirectionalList;

  { The TransformBidirectionalList procedure applies a transformation to
    every element of a bidirectional list. }

  procedure TransformBidirectionalList (var Ls: BidirectionalList;
    function Transformer (E: Element): Element);

  { The ApplyAlongBidirectionalList procedure applies a given procedure to
    each successive element of a given bidirectional list. }

  procedure ApplyAlongBidirectionalList (Ls: BidirectionalList;
    procedure Applicand (E: Element)); 

  { The EveryElementOfBidirectionalList function determines whether every
    element of a given bidirectional list satisfies a given predicate. }

  function EveryElementOfBidirectionalList (Ls: BidirectionalList;
    function Test (E: Element): Boolean): Boolean;

  { The SomeElementOfBidirectionalList function determines whether at least
    one element of a given bidirectional list satisfies a given predicate. }

  function SomeElementOfBidirectionalList (Ls: BidirectionalList;
    function Test (E: Element): Boolean): Boolean;

  { The RecoverByTestFromBidirectionalList procedure determines whether any
    of the elements of a given bidirectional list satisfies a given
    predicate and, if so, returns the one whose position is the least. }

  procedure RecoverByTestFromBidirectionalList (Ls: BidirectionalList;
    function Test (E: Element): Boolean; var Found: Boolean;
    var Sought: Element);

  { The LocateByTestInBidirectionalList procedure determines whether any of
    the elements of a given bidirectional list satisfies a given predicate
    and, if so, returns the least position occupied by such an element. }

  procedure LocateByTestInBidirectionalList (Ls: BidirectionalList;
    function Test (E: Element): Boolean; var Found: Boolean;
    var Position: Integer);

  { The FindNextByTestInBidirectionalList procedure advances a given
    bidirectional list's cursor to the next position occupied by an element
    that satisfies a given predicate, leaving it null if there is no such
    position. } 

  procedure FindNextByTestInBidirectionalList (var Operand: BidirectionalList;
    function Test (E: Element): Boolean);

  { The FindPreviousByTestInBidirectionalList procedure moves a given
    bidirectional list's cursor to the nearest lower-numbered  position
    occupied by an element that satisfies a given predicate, leaving it
    null if there is no such position. } 

  procedure FindPreviousByTestInBidirectionalList (var Operand:
    BidirectionalList; function Test (E: Element): Boolean);

  { The FilterBidirectionalList procedure strips out of a given
    bidirectional list any elements that do not satisfy a given
    predicate. } 

  procedure FilterBidirectionalList (var Ls: BidirectionalList;
    function Test (E: Element): Boolean); 

  { The DeallocateBidirectionalList procedure recycles the storage
    associated with a given bidirectional list. }

  procedure DeallocateBidirectionalList (var Operand: BidirectionalList);

implement

import StdErr;

  const
    FirstExceptionCode = 1;

    IncorrectlyConstructedBidirectionalListException = 1;
    UninitializedBidirectionalListException = 2;
    FirstOfBidirectionalListException = 3;
    DeleteFirstOfBidirectionalListException = 4;
    LastOfBidirectionalListException = 5;
    DeleteLastOfBidirectionalListException = 6;
    NullCursorException = 7;
    RecoverByPositionFromBidirectionalListException = 8;
    AssignAtPositionInBidirectionalListException = 9;
    SublistOfBidirectionalListException = 10;
    InsertAtPositionInBidirectionalListException = 11;
    DeleteAtPositionInBidirectionalListException = 12;
    FillBidirectionalListException = 13;
    GenerateBidirectionalListException = 14;
    ExceptionException = 15;

    LastExceptionCode = 15;

    Debug = True;
      { True during debugging, False (for greater speed) when the
        module is released }

  type
    Link = ^BidirectionalListComponent;
    BidirectionalListComponent = record
                      Datum: Element;
                      Fore, Aft: Link
                    end;
    BidirectionalListRecord = record
                   Size: Integer;
                   Head, Tail: Link;
                   Cursor: Link
                 end;

  procedure BidirectionalListExceptionHandler (ExceptionCode: Integer);
  begin
    if (ExceptionCode < FirstExceptionCode) or
                        (LastExceptionCode < ExceptionCode) then
      ExceptionCode := ExceptionException;
    WriteLn (StdErr, 'Exception #', ExceptionCode : 1,
             ' in module BidirectionalLists:');
    case ExceptionCode of
    IncorrectlyConstructedBidirectionalListException:
      WriteLn (StdErr, 'An operation attempted to return an incorrectly ',
               'constructed bidirectional list.');
    UninitializedBidirectionalListException:
      WriteLn (Stderr, 'An operation was applied to an uninitialized ',
               'list. ');
    FirstOfBidirectionalListException:
      WriteLn (StdErr, 'The empty list was passed to the ',
               'FirstOfBidirectionalList function.');
    DeleteFirstOfBidirectionalListException:
      WriteLn (StdErr, 'The empty list was passed to the ',
               'DeleteFirstOfBidirectionalList function.');
    LastOfBidirectionalListException:
      WriteLn (StdErr, 'The empty list was passed to the ',
               'LastOfBidirectionalList function.');
    DeleteLastOfBidirectionalListException:
      WriteLn (StdErr, 'The empty list was passed to the ',
               'DeleteLastOfBidirectionalList function.');
    NullCursorException:
      WriteLn (StdErr, 'A list with a null cursor was passed to a ',
               'procedure or function that required a non-null cursor.');
    RecoverByPositionFromBidirectionalListException:
      WriteLn (StdErr, 'An invalid index was passed to the ',
               'RecoverByPositionFromBidirectionalList function.');
    AssignAtPositionInBidirectionalListException:
      WriteLn (StdErr, 'An invalid index was passed to the ',
               'AssignAtPositionFromBidirectionalList function.');
    SublistOfBidirectionalListException:
      WriteLn (StdErr, 'An invalid index was passed to the ',
               'SublistOfBidirectionalListfunction.');
    InsertAtPositionInBidirectionalListException:
      WriteLn (StdErr, 'An invalid index was passed to the ',
               'InsertAtPositionInBidirectionalList function.');
    DeleteAtPositionInBidirectionalListException:
      WriteLn (StdErr, 'An invalid index was passed to the ',
               'DeleteAtPositionInBidirectionalList function.');
    FillBidirectionalListException:
      WriteLn (StdErr, 'A negative Length argument was passed to the ',
               'FillBidirectionalList function.');
    GenerateBidirectionalListException:
      WriteLn (StdErr, 'A negative Length argument was passed to the ',
               'GenerateBidirectionalList function.');
    ExceptionException:
      WriteLn (StdErr, 'The BidirectionalListExceptionHandler procedure ',
               'received an unknown exception code.')
    end
  end;

  function ValidBidirectionalList (Ls: BidirectionalList): Boolean;
  var
    Tally: Integer;
      { counts the number of components actually in the doubly-linked
        list }
    Traverser: Link;
      { points to successive components of the doubly-linked list }
    CursorFound: Boolean;
      { indicates whether the cursor has a suitable value (either nil, or
        a pointer to one of the components of the doubly-linked list }
    OKSoFar: Boolean;
      { indicates whether the internal links of the doubly-linked list are
        all reciprocal (i.e., whether A's aft pointer points to B if and
        only if B's fore pointer points to A) }
  begin
    if Ls = nil then
      ValidBidirectionalList := False
    else if Ls^.Size = 0 then
      ValidBidirectionalList := (Ls^.Head = nil) and (Ls^.Tail = nil)
                                and (Ls^.Cursor = nil)
    else if (Ls^.Head = nil) or (Ls^.Tail = nil) then
      ValidBidirectionalList := False
    else if (Ls^.Head^.Fore <> nil) or (Ls^.Tail^.Aft <> nil) then
      ValidBidirectionalList := False
    else begin
      Tally := 1;
      Traverser := Ls^.Head;
      CursorFound := (Ls^.Cursor = nil) or (Ls^.Cursor = Ls^.Tail);
      OKSoFar := True;
      while OKSoFar and (Traverser^.Aft <> nil) do begin
        if Traverser^.Aft^.Fore <> Traverser then
          OKSoFar := False;
        Tally := Tally + 1;
        if Traverser = Ls^.Cursor then
          CursorFound := True;
        Traverser := Traverser^.Aft
      end;
      ValidBidirectionalList := OKSoFar and (Traverser = Ls^.Tail) and
                                (Tally = Ls^.Size) and CursorFound
    end
  end;

  function MakeEmptyBidirectionalList: BidirectionalList;
  var
    Result: BidirectionalList;
      { the empty bidirectional list, as it is being constructed }
  begin
    New (Result);
    Result^.Size := 0;
    Result^.Head := nil;
    Result^.Tail := nil;
    Result^.Cursor := nil;
    if Debug then
      Assert (ValidBidirectionalList (Result),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler);
    MakeEmptyBidirectionalList := Result
  end;
  
  procedure PrependToBidirectionalList (Prefix: Element;
    var Base: BidirectionalList);
  var
    OneMore: Link;
      { a pointer to a component to be inserted into a doubly-linked list }
  begin
    Assert (Base <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    New (OneMore);
    OneMore^.Datum := Prefix;
    OneMore^.Fore := nil;
    OneMore^.Aft := Base^.Head;
    if Base^.Head = nil then
      Base^.Tail := OneMore
    else
      Base^.Head^.Fore := OneMore;
    Base^.Head := OneMore;
    Base^.Cursor := nil;
    Base^.Size := Base^.Size + 1;
    if Debug then
      Assert (ValidBidirectionalList (Base),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler)
  end;

  function FirstOfBidirectionalList (Operand: BidirectionalList): Element;
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    Assert (Operand^.Head <> nil, FirstOfBidirectionalListException,
            BidirectionalListExceptionHandler);
    FirstOfBidirectionalList := Operand^.Head^.Datum
  end;

  procedure DeleteFirstOfBidirectionalList (var Operand: BidirectionalList);
  var
    OneLess: Link;
      { a pointer to a component to be removed from a doubly-linked list }
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    Assert (Operand^.Head <> nil, DeleteFirstOfBidirectionalListException,
            BidirectionalListExceptionHandler);
    OneLess := Operand^.Head;
    Operand^.Head := OneLess^.Aft;
    if OneLess^.Aft = nil then
      Operand^.Tail := nil
    else
      OneLess^.Aft^.Fore := nil;
    Operand^.Cursor := nil;
    Operand^.Size := Operand^.Size - 1;
    Dispose (OneLess);
    if Debug then
      Assert (ValidBidirectionalList (Operand),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler);
  end;

  function EmptyBidirectionalList (Operand: BidirectionalList): Boolean;
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    EmptyBidirectionalList := (Operand^.Size = 0)
  end;

  function LengthOfBidirectionalList (Operand: BidirectionalList): Integer;
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    LengthOfBidirectionalList := Operand^.Size
  end;

  procedure AppendToBidirectionalList (var Base: BidirectionalList;
    Postfix: Element);
  var
    OneMore: Link;
      { a pointer to a component to be inserted into a doubly-linked list }
  begin
    Assert (Base <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    New (OneMore);
    OneMore^.Datum := Postfix;
    OneMore^.Fore := Base^.Tail;
    OneMore^.Aft := nil;
    if Base^.Tail = nil then
      Base^.Head := OneMore
    else
      Base^.Tail^.Aft := OneMore;
    Base^.Tail := OneMore;
    Base^.Cursor := nil;
    Base^.Size := Base^.Size + 1;
    if Debug then
      Assert (ValidBidirectionalList (Base),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler);
  end;

  function LastOfBidirectionalList (Operand: BidirectionalList): Element;
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    Assert (Operand^.Tail <> nil, LastOfBidirectionalListException,
            BidirectionalListExceptionHandler);
    LastOfBidirectionalList := Operand^.Tail^.Datum
  end;

  procedure DeleteLastOfBidirectionalList (var Operand: BidirectionalList);
  var
    OneLess: Link;
      { a pointer to a component to be removed from a doubly-linked list }
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    Assert (Operand^.Tail <> nil, DeleteLastOfBidirectionalListException,
            BidirectionalListExceptionHandler);
    OneLess := Operand^.Tail;
    if OneLess^.Fore = nil then
      Operand^.Head := nil
    else
      OneLess^.Fore^.Aft := nil;
    Operand^.Tail := OneLess^.Fore;
    Operand^.Cursor := nil;
    Operand^.Size := Operand^.Size - 1;
    Dispose (OneLess);
    if Debug then
      Assert (ValidBidirectionalList (Operand),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler);
  end;

  function CopyBidirectionalList (Operand: BidirectionalList):
    BidirectionalList;
  var
    Result: BidirectionalList;
      { the duplicate bidirectional list, as it is being constructed }
    ResultTraverser, OperandTraverser: Link;
      { pointers to successive components of the duplicate and original
        doubly-linked lists, respectively }
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    New (Result);
    if Operand^.Size = 0 then begin
      Result^.Head := nil;
      Result^.Tail := nil
    end
    else begin
      New (ResultTraverser);
      Result^.Head := ResultTraverser;
      ResultTraverser^.Fore := nil;
      OperandTraverser := Operand^.Head;
      ResultTraverser^.Datum := OperandTraverser^.Datum;
      while OperandTraverser^.Aft <> nil do begin
        New (ResultTraverser^.Aft);
        ResultTraverser^.Aft^.Fore := ResultTraverser;
        ResultTraverser := ResultTraverser^.Aft;
        OperandTraverser := OperandTraverser^.Aft;
        ResultTraverser^.Datum := OperandTraverser^.Datum
      end;
      ResultTraverser^.Aft := nil;
      Result^.Tail := ResultTraverser
    end;
    Result^.Size := Operand^.Size;
    Result^.Cursor := nil;
    if Debug then
      Assert (ValidBidirectionalList (Result),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler);
    CopyBidirectionalList := Result
  end;

  procedure CursorToStartOfBidirectionalList (var Operand: BidirectionalList);
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    Operand^.Cursor := Operand^.Head
  end;

  procedure CursorToEndOfBidirectionalList (var Operand: BidirectionalList);
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    Operand^.Cursor := Operand^.Tail
  end;

  procedure AdvanceCursorAlongBidirectionalList (var Operand:
    BidirectionalList);
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    Assert (Operand^.Cursor <> nil, NullCursorException,
            BidirectionalListExceptionHandler);
    Operand^.Cursor := Operand^.Cursor^.Aft
  end;

  procedure RetreatCursorAlongBidirectionalList (var Operand:
    BidirectionalList);
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    Assert (Operand^.Cursor <> nil, NullCursorException,
            BidirectionalListExceptionHandler);
    Operand^.Cursor := Operand^.Cursor^.Fore
  end;

  function NullCursorInBidirectionalList (Operand: BidirectionalList):
    Boolean;
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    NullCursorInBidirectionalList := (Operand^.Cursor = nil)
  end;

  procedure ConcatenateBidirectionalList (var LeftOperand:
    BidirectionalList; RightOperand: BidirectionalList);
  var
    ResultTraverser, OperandTraverser: Link;
      { pointers to successive components of the doubly-linked lists in the
        right operand and in the extended left operand, respectively }
  begin 
    Assert ((LeftOperand <> nil) and (RightOperand <> nil),
            UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    if LeftOperand^.Size = 0 then begin
      DeallocateBidirectionalList (LeftOperand);
      LeftOperand := CopyBidirectionalList (RightOperand)
    end
    else if RightOperand^.Size = 0 then
      LeftOperand^.Cursor := nil
    else begin
      New (ResultTraverser);
      LeftOperand^.Tail^.Aft := ResultTraverser;
      ResultTraverser^.Fore := LeftOperand^.Tail;
      OperandTraverser := RightOperand^.Head;
      ResultTraverser^.Datum := OperandTraverser^.Datum;
      while OperandTraverser^.Aft <> nil do begin
        New (ResultTraverser^.Aft);
        ResultTraverser^.Aft^.Fore := ResultTraverser;
        ResultTraverser := ResultTraverser^.Aft;
        OperandTraverser := OperandTraverser^.Aft;
        ResultTraverser^.Datum := OperandTraverser^.Datum
      end;
      ResultTraverser^.Aft := nil;
      LeftOperand^.Tail := ResultTraverser;
      LeftOperand^.Size := LeftOperand^.Size + RightOperand^.Size;
      LeftOperand^.Cursor := nil
    end;
    if Debug then
      Assert (ValidBidirectionalList (LeftOperand),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler);
  end;

  function RecoverByPositionFromBidirectionalList (Position: Integer;
    Ls: BidirectionalList): Element;
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Assert ((1 <= Position) and (Position <= Ls^.Size),
            RecoverByPositionFromBidirectionalListException,
            BidirectionalListExceptionHandler);
    Traverser := Ls^.Head;
    while 1 < Position do begin
      Traverser := Traverser^.Aft;
      Position := Position - 1
    end;
    RecoverByPositionFromBidirectionalList := Traverser^.Datum
  end;

  function ElementAtCursorInBidirectionalList (Operand: BidirectionalList):
    Element;
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    Assert (Operand^.Cursor <> nil, NullCursorException,
            BidirectionalListExceptionHandler);
    ElementAtCursorInBidirectionalList := Operand^.Cursor^.Datum
  end;

  procedure AssignAtPositionInBidirectionalList (Position: Integer;
    var Ls: BidirectionalList; NewValue: Element);
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Assert ((1 <= Position) and (Position <= Ls^.Size),
            RecoverByPositionFromBidirectionalListException,
            BidirectionalListExceptionHandler);
    Traverser := Ls^.Head;
    while 1 < Position do begin
      Traverser := Traverser^.Aft;
      Position := Position - 1
    end;
    Traverser^.Datum := NewValue
  end;

  procedure AssignAtCursorInBidirectionalList (var Ls: BidirectionalList;
    NewValue: Element);
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Assert (Ls^.Cursor <> nil, NullCursorException,
            BidirectionalListExceptionHandler); 
    Ls^.Cursor^.Datum := NewValue
  end;

  function ElementOfBidirectionalList (Candidate: Element;
    Ls: BidirectionalList): Boolean; 
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
    Found: Boolean;
      { indicates whether the element sought has yet been encountered }
  begin 
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Found := False;
    Traverser := Ls^.Head;
    while not Found and (Traverser <> nil) do
      if Traverser^.Datum = Candidate then
        Found := True
      else
        Traverser := Traverser^.Aft;
    ElementOfBidirectionalList := Found
  end;   

  procedure LocateInBidirectionalList (Sought: Element;
    Ls: BidirectionalList; var Found: Boolean; var Position: Integer); 
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Position := 0;
    Found := False;
    Traverser := Ls^.Head;
    while not Found and (Traverser <> nil) do begin
      Position := Position + 1;
      if Traverser^.Datum = Sought then
        Found := True
      else
        Traverser := Traverser^.Aft
    end
  end;

  procedure FindNextInBidirectionalList (Sought: Element;
    var Ls: BidirectionalList); 
  label
    99;  { Exit when the element is found or the end of the list
           is reached. }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    if Ls^.Cursor = nil then
      Ls^.Cursor := Ls^.Head
    else
      Ls^.Cursor := Ls^.Cursor^.Aft;
    while True do
      if Ls^.Cursor = nil then
        goto 99
      else if EqualElement (Ls^.Cursor^.Datum, Sought) then
        goto 99
      else
        Ls^.Cursor := Ls^.Cursor^.Aft;
  99:
  end;  

  procedure FindPreviousInBidirectionalList (Sought: Element;
    var Ls: BidirectionalList); 
  label
    99;  { Exit when the element is found or the start of the list
           is reached. }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    if Ls^.Cursor = nil then
      Ls^.Cursor := Ls^.Tail
    else
      Ls^.Cursor := Ls^.Cursor^.Fore;
    while True do
      if Ls^.Cursor = nil then
        goto 99
      else if EqualElement (Ls^.Cursor^.Datum, Sought) then
        goto 99
      else
        Ls^.Cursor := Ls^.Cursor^.Fore;
  99:
  end;  

  procedure SublistOfBidirectionalList (var Ls: BidirectionalList;
    Start, Finish: Integer);
  var
    Traverser, Trailer: Link;
      { pointers to successive components of the doubly-linked list;
        Trailer is consistently one component behind Traverser }
    Position: Integer;
      { counts off positions in the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Assert ((1 <= Start) and (Start <= Ls^.Size) and
            (1 <= Finish) and (Finish <= Ls^.Size),
            SublistofBidirectionalListException, 
            BidirectionalListExceptionHandler);
    if Finish < Start then begin
      Traverser := Ls^.Head;
      while Traverser <> nil do begin
        Trailer := Traverser;
        Traverser := Traverser^.Aft;
        Dispose (Trailer)
      end;
      Ls^.Head := nil;
      Ls^.Tail := nil;
      Ls^.Cursor := nil;
      Ls^.Size := 0
    end
    else begin
      Traverser := Ls^.Head;
      for Position := 1 to Start - 1 do begin
        Trailer := Traverser;
        Traverser := Traverser^.Aft;
        Dispose (Trailer)
      end;
      Ls^.Head := Traverser;
      Traverser^.Fore := nil;
      Traverser := Ls^.Tail;
      for Position := Ls^.Size downto Finish + 1 do begin
        Trailer := Traverser;
        Traverser := Traverser^.Fore;
        Dispose (Trailer)
      end;
      Ls^.Tail := Traverser;
      Traverser^.Aft := nil;
      Ls^.Cursor := nil;
      Ls^.Size := Finish - Start + 1
    end;
    if Debug then
      Assert (ValidBidirectionalList (Ls),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler)
  end;

  procedure ReverseBidirectionalList (var Operand: BidirectionalList);
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
    Temp: Link;
      { temporary storage for a pointer as its direction is reversed }
  begin 
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    if 1 < Operand^.Size then begin
      Traverser := Operand^.Head;
      while Traverser <> nil do begin
        Temp := Traverser^.Aft;
        Traverser^.Aft := Traverser^.Fore;
        Traverser^.Fore := Temp;
        Traverser := Traverser^.Fore
      end;
      Temp := Operand^.Head;
      Operand^.Head := Operand^.Tail;
      Operand^.Tail := Temp;
    end;
    Operand^.Cursor := nil;
    if Debug then
      Assert (ValidBidirectionalList (Operand),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler)
  end;

  procedure InsertAtPositionInBidirectionalList (var Ls: BidirectionalList;
    Position: Integer; Elm: Element);
  var
    OneMore: Link;
      { a pointer to a component to be inserted into a doubly-linked list }
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Assert ((1 <= Position) and (Position <= Ls^.Size + 1),
             InsertAtPositionInBidirectionalListException,
             BidirectionalListExceptionHandler);
    Traverser := Ls^.Head;
    while 1 < Position do begin
      Traverser := Traverser^.Aft;
      Position := Position - 1
    end;
    New (OneMore);
    OneMore^.Datum := Elm;
    if Traverser = nil then
      OneMore^.Fore := Ls^.Tail
    else
      OneMore^.Fore := Traverser^.Fore;
    OneMore^.Aft := Traverser;
    if Traverser = Ls^.Head then
      Ls^.Head := OneMore
    else if Traverser = nil then
      Ls^.Tail^.Aft := OneMore
    else
      Traverser^.Fore^.Aft := OneMore;
    if Traverser = nil then
      Ls^.Tail := OneMore
    else
      Traverser^.Fore := OneMore;
    Ls^.Cursor := nil;
    Ls^.Size := Ls^.Size + 1;
    if Debug then
      Assert (ValidBidirectionalList (Ls),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler)
  end;

  procedure InsertAtCursorInBidirectionalList (var Ls: BidirectionalList;
    Elm: Element);
  var
    OneMore: Link;
      { a pointer to a component to be inserted into a doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    New (OneMore);
    OneMore^.Datum := Elm;
    if Ls^.Cursor = nil then
      OneMore^.Fore := Ls^.Tail
    else
      OneMore^.Fore := Ls^.Cursor^.Fore;
    OneMore^.Aft := Ls^.Cursor;
    if Ls^.Cursor = Ls^.Head then
      Ls^.Head := OneMore
    else if Ls^.Cursor = nil then
      Ls^.Tail^.Aft := OneMore
    else
      Ls^.Cursor^.Fore^.Aft := OneMore;
    if Ls^.Cursor = nil then
      Ls^.Tail := OneMore
    else
      Ls^.Cursor^.Fore := OneMore;
    Ls^.Size := Ls^.Size + 1;
    if Debug then
      Assert (ValidBidirectionalList (Ls),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler)
  end;

  procedure DeleteAtPositionInBidirectionalList (var Ls: BidirectionalList;
    Position: Integer);
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Assert ((1 <= Position) and (Position <= Ls^.Size),
            DeleteAtPositionInBidirectionalListException,
            BidirectionalListExceptionHandler);
    Traverser := Ls^.Head;
    while 1 < Position do begin
      Traverser := Traverser^.Aft;
      Position := Position - 1
    end;
    if Traverser^.Fore = nil then
      Ls^.Head := Traverser^.Aft
    else
      Traverser^.Fore^.Aft := Traverser^.Aft;
    if Traverser^.Aft = nil then
      Ls^.Tail := Traverser^.Fore
    else
      Traverser^.Aft^.Fore := Traverser^.Fore;
    Dispose (Traverser);
    Ls^.Cursor := nil;
    Ls^.Size := Ls^.Size - 1;
    if Debug then
      Assert (ValidBidirectionalList (Ls),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler)
  end;

  procedure DeleteAtCursorInBidirectionalList (var Ls: BidirectionalList);
  var
    OneLess: Link;
      { a pointer to a component to be removed from a doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Assert (Ls^.Cursor <> nil, NullCursorException,
            BidirectionalListExceptionHandler);
    OneLess := Ls^.Cursor;
    Ls^.Cursor := OneLess^.Aft;
    if OneLess^.Fore = nil then
      Ls^.Head := OneLess^.Aft
    else
      OneLess^.Fore^.Aft := OneLess^.Aft;
    if OneLess^.Aft = nil then
      Ls^.Tail := OneLess^.Fore
    else
      OneLess^.Aft^.Fore := OneLess^.Fore;
    Dispose (OneLess);
    Ls^.Size := Ls^.Size - 1;
    if Debug then
      Assert (ValidBidirectionalList (Ls),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler)
  end;

  procedure DeleteValueFromBidirectionalList (var Ls: BidirectionalList;
    Delend: Element);
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
    OneLess: Link;
      { a pointer to a component to be removed from a doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
      BidirectionalListExceptionHandler); 
    Traverser := Ls^.Head;
    while Traverser <> nil do
      if Traverser^.Datum = Delend then begin
        OneLess := Traverser;
        Traverser := Traverser^.Aft;
        if OneLess^.Fore = nil then
          Ls^.Head := OneLess^.Aft
        else
          OneLess^.Fore^.Aft := OneLess^.Aft;
        if OneLess^.Aft = nil then
          Ls^.Tail := OneLess^.Fore
        else
          OneLess^.Aft^.Fore := OneLess^.Fore;
        Dispose (OneLess);
        Ls^.Size := Ls^.Size - 1
      end
      else
        Traverser := Traverser^.Aft;
    Ls^.Cursor := nil;
    if Debug then
      Assert (ValidBidirectionalList (Ls),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler)
  end;

  procedure ReplaceInBidirectionalList (var Ls: BidirectionalList;
    Displacer, Displaced: Element);
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Traverser := Ls^.Head;
    while Traverser <> nil do begin
      if Traverser^.Datum = Displaced then
        Traverser^.Datum := Displacer;
      Traverser := Traverser^.Aft
    end
  end;

  function FillBidirectionalList (Length: Integer; Filler: Element):
    BidirectionalList;
  var
    Filled: BidirectionalList;
      { the new bidirectional list, as it is constructed }
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
    Position: Integer;
      { the number of components so far constructed }
  begin
    Assert (0 <= Length, FillBidirectionalListException,
            BidirectionalListExceptionHandler); 
    New (Filled);
    if Length = 0 then begin
      Filled^.Head := nil;
      Filled^.Tail := nil
    end
    else begin
      New (Filled^.Head);
      Traverser := Filled^.Head;
      Traverser^.Fore := nil;
      Traverser^.Datum := Filler;
      Position := 1;
      while Position < Length do begin
        New (Traverser^.Aft);
        Traverser^.Aft^.Fore := Traverser;
        Traverser := Traverser^.Aft;
        Traverser^.Datum := Filler;
        Position := Position + 1
      end;
      Traverser^.Aft := nil;
      Filled^.Tail := Traverser
    end;
    Filled^.Cursor := nil;
    Filled^.Size := Length;
    if Debug then
      Assert (ValidBidirectionalList (Filled),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler);
    FillBidirectionalList := Filled
  end;

  function GenerateBidirectionalList (function Generator (N: Integer):
    Element; Length: Integer): BidirectionalList;
  var
    Generated: BidirectionalList;
      { the new bidirectional list, as it is constructed }
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
    Position: Integer;
      { the number of components so far constructed }
  begin
    Assert (0 <= Length, GenerateBidirectionalListException,
            BidirectionalListExceptionHandler); 
    New (Generated);
    if Length = 0 then begin
      Generated^.Head := nil;
      Generated^.Tail := nil
    end
    else begin
      New (Generated^.Head);
      Traverser := Generated^.Head;
      Traverser^.Fore := nil;
      Position := 1;
      Traverser^.Datum := Generator (Position);
      while Position < Length do begin
        New (Traverser^.Aft);
        Traverser^.Aft^.Fore := Traverser;
        Traverser := Traverser^.Aft;
        Position := Position + 1;
        Traverser^.Datum := Generator (Position)
      end;
      Traverser^.Aft := nil;
      Generated^.Tail := Traverser
    end;
    Generated^.Cursor := nil;
    Generated^.Size := Length;
    if Debug then
      Assert (ValidBidirectionalList (Generated),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler);
    GenerateBidirectionalList := Generated
  end;

  procedure TransformBidirectionalList (var Ls: BidirectionalList;
    function Transformer (E: Element): Element);
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Traverser := Ls^.Head;
    while Traverser <> nil do begin
      Traverser^.Datum := Transformer (Traverser^.Datum);
      Traverser := Traverser^.Aft
    end
  end;

  procedure ApplyAlongBidirectionalList (Ls: BidirectionalList;
    procedure Applicand (E: Element));
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Traverser := Ls^.Head;
    while Traverser <> nil do begin
      Applicand (Traverser^.Datum);
      Traverser := Traverser^.Aft
    end
  end;

  function EveryElementOfBidirectionalList (Ls: BidirectionalList;
    function Test (E: Element): Boolean): Boolean;
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
    TrueSoFar: Boolean;
      { indicates whether all of the elements so far encountered have
        passed the test }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    TrueSoFar := True;
    Traverser := Ls^.Head;
    while TrueSoFar and (Traverser <> nil) do
      if Test (Traverser^.Datum) then
        Traverser := Traverser^.Aft
      else
        TrueSoFar := False;
    EveryElementOfBidirectionalList := TrueSoFar
  end;

  function SomeElementOfBidirectionalList (Ls: BidirectionalList;
    function Test (E: Element): Boolean): Boolean; 
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
    FalseSoFar: Boolean;
      { indicates whether all of the elements so far encountered have
        failed the test }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    FalseSoFar := True;
    Traverser := Ls^.Head;
    while FalseSoFar and (Traverser <> nil) do
      if Test (Traverser^.Datum) then
        FalseSoFar := False
      else
        Traverser := Traverser^.Aft;
    SomeElementOfBidirectionalList := not FalseSoFar
  end;

  procedure RecoverByTestFromBidirectionalList (Ls: BidirectionalList;
    function Test (E: Element): Boolean; var Found: Boolean;
    var Sought: Element);
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Found := False;
    Traverser := Ls^.Head;
    while not Found and (Traverser <> nil) do
      if Test (Traverser^.Datum) then begin
        Found := True;
        Sought := Traverser^.Datum
      end
      else
        Traverser := Traverser^.Aft
  end;

  procedure LocateByTestInBidirectionalList (Ls: BidirectionalList;
    function Test (E: Element): Boolean; var Found: Boolean;
    var Position: Integer);
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Found := False;
    Position := 1;
    Traverser := Ls^.Head;
    while not Found and (Traverser <> nil) do
      if Test (Traverser^.Datum) then
        Found := True
      else begin
        Traverser := Traverser^.Aft;
        Position := Position + 1
      end
  end;

  procedure FindNextByTestInBidirectionalList (var Operand: BidirectionalList;
    function Test (E: Element): Boolean);
  label
    99;  { Exit when the test succeeds or the end of the list is reached. }
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    if Operand^.Cursor = nil then
      Operand^.Cursor := Operand^.Head
    else
      Operand^.Cursor := Operand^.Cursor^.Aft;
    while True do
      if Operand^.Cursor = nil then
        goto 99
      else if Test (Operand^.Cursor^.Datum) then
        goto 99
      else
        Operand^.Cursor := Operand^.Cursor^.Aft;
  99:
  end;  

  procedure FindPreviousByTestInBidirectionalList (var Operand:
    BidirectionalList; function Test (E: Element): Boolean);
  label
    99;  { Exit when the test succeeds or the end of the list is reached. }
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    if Operand^.Cursor = nil then
      Operand^.Cursor := Operand^.Tail
    else
      Operand^.Cursor := Operand^.Cursor^.Fore;
    while True do
      if Operand^.Cursor = nil then
        goto 99
      else if Test (Operand^.Cursor^.Datum) then
        goto 99
      else
        Operand^.Cursor := Operand^.Cursor^.Fore;
  99:
  end;  

  procedure FilterBidirectionalList (var Ls: BidirectionalList;
    function Test (E: Element): Boolean);
  var
    Traverser: Link;
      { a pointer to successive components of the doubly-linked list }
    OneLess: Link;
      { a pointer to a component to be removed from a doubly-linked list }
  begin
    Assert (Ls <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler); 
    Traverser := Ls^.Head;
    while Traverser <> nil do
      if Test (Traverser^.Datum) then
        Traverser := Traverser^.Aft
      else begin
        OneLess := Traverser;
        Traverser := Traverser^.Aft;
        if OneLess^.Fore = nil then
          Ls^.Head := OneLess^.Aft
        else
          OneLess^.Fore^.Aft := OneLess^.Aft;
        if OneLess^.Aft = nil then
          Ls^.Tail := OneLess^.Fore
        else
          OneLess^.Aft^.Fore := OneLess^.Fore;
        Dispose (OneLess);
        Ls^.Size := Ls^.Size - 1
      end;
    Ls^.Cursor := nil;
    if Debug then
      Assert (ValidBidirectionalList (Ls),
              IncorrectlyConstructedBidirectionalListException,
              BidirectionalListExceptionHandler)
  end;

  procedure DeallocateBidirectionalList (var Operand: BidirectionalList);
  var
    Traverser, Trailer: Link;
      { pointers to successive components of the doubly-linked list;
        Trailer is consistently one component behind Traverser }
  begin
    Assert (Operand <> nil, UninitializedBidirectionalListException,
            BidirectionalListExceptionHandler);
    Traverser := Operand^.Head;
    while Traverser <> nil do begin
      Trailer := Traverser;
      Traverser := Traverser^.Aft;
      Dispose (Trailer)
    end;
    Dispose (Operand)
  end;

end.

created August 23, 1996
last revised November 7, 1996

John David Stone (stone@math.grin.edu)