home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue39 / Alfresco / PriQueue.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1998-10-07  |  19.0 KB  |  639 lines

  1. {*********************************************************}
  2. {* PriQueue                                              *}
  3. {* Copyright (c) Julian M Bucknall 1998                  *}
  4. {* All rights reserved.                                  *}
  5. {*********************************************************}
  6. {* Priority queues                                       *}
  7. {*********************************************************}
  8.  
  9. {Note: this unit is released as freeware. In other words, you are free
  10.        to use this unit in your own applications, however I retain all
  11.        copyright to the code. JMB}
  12.  
  13. unit PriQueue;
  14.  
  15. interface
  16.  
  17. uses
  18.   SysUtils,
  19.   Classes;
  20.  
  21. type
  22.   TaaItemPriorityCompare = function(const aItem1, aItem2 : pointer) : integer;
  23.     {-Function prototype to take two items and compare their
  24.       priorities: returns < 0 if the first item's priority is less
  25.       than the second's, 0 if they're equal, > 0 otherwise}
  26.  
  27. type
  28.   TaaPriorityQueueA = class
  29.     {-A priority queue that is fast at insertion, slow at retrieval}
  30.     private
  31.       pqCompare : TaaItemPriorityCompare;
  32.       pqList    : TList;
  33.     protected
  34.       function pqGetCount : integer;
  35.     public
  36.       constructor Create(aCompareFn : TaaItemPriorityCompare);
  37.         {-Create the priority queue}
  38.       destructor Destroy; override;
  39.         {-Dispose of the priority queue - items remaining are NOT
  40.           freed}
  41.  
  42.       procedure Add(aItem : pointer);
  43.         {-Add an item to the priority queue}
  44.       function Remove : pointer;
  45.         {-Remove and return the item with the largest priority}
  46.  
  47.       property Count : integer read pqGetCount;
  48.         {-Count of items in the queue}
  49.   end;
  50.  
  51. type
  52.   TaaPriorityQueueB = class
  53.     {-A priority queue that is slow at insertion, fast at retrieval}
  54.     private
  55.       pqCompare : TaaItemPriorityCompare;
  56.       pqList    : TList;
  57.     protected
  58.       function pqGetCount : integer;
  59.     public
  60.       constructor Create(aCompareFn : TaaItemPriorityCompare);
  61.         {-Create the priority queue}
  62.       destructor Destroy; override;
  63.         {-Dispose of the priority queue - items remaining are NOT
  64.           freed}
  65.  
  66.       procedure Add(aItem : pointer);
  67.         {-Add an item to the priority queue}
  68.       function Remove : pointer;
  69.         {-Remove and return the item with the largest priority}
  70.  
  71.       property Count : integer read pqGetCount;
  72.         {-Count of items in the queue}
  73.   end;
  74.  
  75. type
  76.   TaaPriorityQueue = class
  77.     {-A priority queue that uses the heap algorithm}
  78.     private
  79.       pqCompare      : TaaItemPriorityCompare;
  80.       pqExternalList : boolean;
  81.       pqList         : TList;
  82.     protected
  83.       function pqGetCount : integer;
  84.  
  85.       procedure pqBubbleUp(aFromInx : integer; aItem : pointer);
  86.       procedure pqMakeIntoHeap;
  87.       procedure pqTrickleDown(aFromInx : integer; aItem : pointer);
  88.     public
  89.       constructor Create(aCompareFn : TaaItemPriorityCompare);
  90.         {-Create the priority queue}
  91.       constructor CreateWithList(aCompareFn : TaaItemPriorityCompare;
  92.                                  aList      : TList);
  93.         {-Create the priority queue with an existing list}
  94.       destructor Destroy; override;
  95.         {-Dispose of the priority queue - items remaining are NOT
  96.           freed}
  97.  
  98.       procedure Add(aItem : pointer);
  99.         {-Add an item to the priority queue}
  100.       function Remove : pointer;
  101.         {-Remove and return the item with the largest priority}
  102.  
  103.       property Count : integer read pqGetCount;
  104.         {-Count of items in the queue}
  105.  
  106.  
  107.       property List : TList read pqList;
  108.   end;
  109.  
  110. type
  111.   TaaPQHandle = pointer;
  112.  
  113.   TaaPriorityQueueEx = class
  114.     {-A priority queue that uses the heap algorithm and that allows
  115.       deletion and reprioritisation of arbitrary items}
  116.     private
  117.       pqCompare : TaaItemPriorityCompare;
  118.       pqHandles : pointer;
  119.       pqList    : TList;
  120.     protected
  121.       function pqGetCount : integer;
  122.  
  123.       procedure pqBubbleUp(aFromInx : integer; aHandle : pointer);
  124.       procedure pqTrickleDown(aFromInx : integer; aHandle : pointer);
  125.  
  126.       {$IFOPT D+}
  127.       procedure VerifyIndirection;
  128.       {$ENDIF}
  129.  
  130.     public
  131.       constructor Create(aCompareFn : TaaItemPriorityCompare);
  132.         {-Create the priority queue}
  133.       destructor Destroy; override;
  134.         {-Dispose of the priority queue - items remaining are NOT
  135.           freed}
  136.  
  137.       function Add(aItem : pointer) : TaaPQHandle;
  138.         {-Add an item to the priority queue; return handle}
  139.       procedure Delete(var aHandle : TaaPQHandle);
  140.         {-Delete an item referenced by its handle from the priority
  141.           queue; the handle is set to nil on return}
  142.       function Remove : pointer;
  143.         {-Remove and return the item with the largest priority}
  144.       procedure Replace(aHandle : TaaPQHandle; aItem : pointer);
  145.         {-Replace the item referenced by the handle in the priority
  146.           queue}
  147.  
  148.       property Count : integer read pqGetCount;
  149.         {-Count of items in the queue}
  150.  
  151.       property List : TList read pqList;
  152.   end;
  153.  
  154. implementation
  155.  
  156. {===TaaPriorityQueueA================================================}
  157. constructor TaaPriorityQueueA.Create(aCompareFn : TaaItemPriorityCompare);
  158. begin
  159.   inherited Create;
  160.   pqCompare := aCompareFn;
  161.   pqList := TList.Create;
  162. end;
  163. {--------}
  164. destructor TaaPriorityQueueA.Destroy;
  165. begin
  166.   pqList.Free;
  167.   inherited Destroy;
  168. end;
  169. {--------}
  170. procedure TaaPriorityQueueA.Add(aItem : pointer);
  171. begin
  172.   pqList.Add(aItem);
  173. end;
  174. {--------}
  175. function TaaPriorityQueueA.pqGetCount : integer;
  176. begin
  177.   Result := pqList.Count;
  178. end;
  179. {--------}
  180. function TaaPriorityQueueA.Remove : pointer;
  181. var
  182.   Inx     : integer;
  183.   PQCount : integer;
  184.   MaxInx  : integer;
  185.   MaxItem : pointer;
  186. begin
  187.   PQCount := Count;
  188.   if (PQCount = 0) then
  189.     Result := nil
  190.   else if (PQCount = 1) then begin
  191.     Result := pqList[0];
  192.     pqList.Clear;
  193.   end
  194.   else begin
  195.     MaxItem := pqList[0];
  196.     MaxInx := 0;
  197.     for Inx := 1 to pred(PQCount) do
  198.       if (pqCompare(pqList[Inx], MaxItem) > 0) then begin
  199.         MaxItem := pqList[Inx];
  200.         MaxInx := Inx;
  201.       end;
  202.     Result := MaxItem;
  203.     pqList.Delete(MaxInx);
  204.   end;
  205. end;
  206. {====================================================================}
  207.  
  208.  
  209. {===TaaPriorityQueueB================================================}
  210. constructor TaaPriorityQueueB.Create(aCompareFn : TaaItemPriorityCompare);
  211. begin
  212.   inherited Create;
  213.   pqCompare := aCompareFn;
  214.   pqList := TList.Create;
  215. end;
  216. {--------}
  217. destructor TaaPriorityQueueB.Destroy;
  218. begin
  219.   pqList.Free;
  220.   inherited Destroy;
  221. end;
  222. {--------}
  223. procedure TaaPriorityQueueB.Add(aItem : pointer);
  224. var
  225.   Inx : integer;
  226. begin
  227.   {increment the number of items in the list}
  228.   pqList.Count := pqList.Count + 1;
  229.   {find where to put our new item}
  230.   Inx := pqList.Count - 2;
  231.   while (Inx >= 0) and
  232.         (pqCompare(pqList[Inx], aItem) > 0) do begin
  233.     pqList[Inx+1] := pqList[Inx];
  234.     dec(Inx);
  235.   end;
  236.   {put it there}
  237.   pqList[Inx+1] := aItem;
  238. end;
  239. {--------}
  240. function TaaPriorityQueueB.pqGetCount : integer;
  241. begin
  242.   Result := pqList.Count;
  243. end;
  244. {--------}
  245. function TaaPriorityQueueB.Remove : pointer;
  246. begin
  247.   Result := pqList.Last;
  248.   pqList.Count := pqList.Count - 1;
  249. end;
  250. {====================================================================}
  251.  
  252.  
  253. {===TaaPriorityQueue=================================================}
  254. constructor TaaPriorityQueue.Create(aCompareFn : TaaItemPriorityCompare);
  255. begin
  256.   inherited Create;
  257.   pqCompare := aCompareFn;
  258.   pqList := TList.Create;
  259. end;
  260. {--------}
  261. constructor TaaPriorityQueue.CreateWithList(aCompareFn : TaaItemPriorityCompare;
  262.                                             aList      : TList);
  263. begin
  264.   inherited Create;
  265.   pqCompare := aCompareFn;
  266.   pqList := aList;
  267.   pqExternalList := true;
  268.   pqMakeIntoHeap;
  269. end;
  270. {--------}
  271. destructor TaaPriorityQueue.Destroy;
  272. begin
  273.   if not pqExternalList then
  274.     pqList.Free;
  275.   inherited Destroy;
  276. end;
  277. {--------}
  278. procedure TaaPriorityQueue.Add(aItem : pointer);
  279. begin
  280.   {add extra space at the end of the queue}
  281.   pqList.Count := pqList.Count + 1;
  282.   {now bubble it up as far as it will go}
  283.   pqBubbleUp(pred(pqList.Count), aItem);
  284. end;
  285. {--------}
  286. procedure TaaPriorityQueue.pqBubbleUp(aFromInx : integer; aItem : pointer);
  287. var
  288.   ParentInx : integer;
  289. begin
  290.   {while the item under consideration is larger than its parent, swap
  291.    it with its parent and continue from its new position}
  292.   {Note: the parent for the child at index N is at (N-1) div 2}
  293.   ParentInx := (aFromInx - 1) div 2;
  294.   {while our item has a parent, and it's greater than the parent...}
  295.   while (aFromInx > 0) and
  296.         (pqCompare(aItem, pqList[ParentInx]) > 0) do begin
  297.     {move our parent down the tree}
  298.     pqList[aFromInx] := pqList[ParentInx];
  299.     aFromInx := ParentInx;
  300.     ParentInx := (aFromInx - 1) div 2;
  301.   end;
  302.   {store our item in the correct place}
  303.   pqList[aFromInx] := aItem;
  304. end;
  305. {--------}
  306. function TaaPriorityQueue.pqGetCount : integer;
  307. begin
  308.   Result := pqList.Count;
  309. end;
  310. {--------}
  311. procedure TaaPriorityQueue.pqMakeIntoHeap;
  312. var
  313.   Inx : integer;
  314. begin
  315.   {starting from the lowest, rightmost parent, trickle down and then
  316.    continue with the rest of the parents from right to left, bottom to
  317.    top. The rightmost parent is the parent of the last item. This is
  318.    ((count-1)-1) div 2}
  319.   for Inx := ((pqList.Count - 2) div 2) downto 0 do 
  320.     pqTrickleDown(Inx, pqList[Inx]);
  321. end;
  322. {--------}
  323. procedure TaaPriorityQueue.pqTrickleDown(aFromInx : integer; aItem : pointer);
  324. var
  325.   ChildInx  : integer;
  326.   ListCount : integer;
  327. begin
  328.   {while the item under consideration is smaller than one of its
  329.    children, swap it with the larger child and continue from its new
  330.    position}
  331.   {Note: the children for the parent at index N are at (2N+1) and
  332.          2N+2}
  333.   ListCount := pqList.Count;
  334.   {calculate the left child index}
  335.   ChildInx := succ(aFromInx * 2);
  336.   {while there is at least a left child...}
  337.   while (ChildInx < ListCount) do begin
  338.     {if there is a right child, calculate the index of the larger
  339.      child}
  340.     if (succ(ChildInx) < ListCount) and
  341.        (pqCompare(pqList[ChildInx], pqList[succ(ChildInx)]) < 0) then
  342.       inc(ChildInx);
  343.     {if our item is greater or equal to the larger child, we're done}
  344.     if (pqCompare(aItem, pqList[ChildInx]) >= 0) then
  345.       Break;
  346.     {otherwise move the larger child up the tree, and move our item
  347.      down the tree and repeat}
  348.     pqList[aFromInx] := pqList[ChildInx];
  349.     aFromInx := ChildInx;
  350.     ChildInx := succ(aFromInx * 2);
  351.   end;
  352.   {store our item in the correct place}
  353.   pqList[aFromInx] := aItem;
  354. end;
  355. {--------}
  356. function TaaPriorityQueue.Remove : pointer;
  357. begin
  358.   {return the item at the root}
  359.   Result := pqList[0];
  360.   {replace the root with the child at the lowest, rightmost position,
  361.    and shrink the list}
  362.   pqList[0] := pqList.Last;
  363.   pqList.Count := pqList.Count - 1;
  364.   {now trickle down the root item as far as it will go}
  365.   if (pqList.Count > 0) then
  366.     pqTrickleDown(0, pqList[0]);
  367. end;
  368. {====================================================================}
  369.  
  370.  
  371. {===Linked list helper routines======================================}
  372. type
  373.   PllNode = ^TllNode;
  374.   TllNode = packed record
  375.     lliNext : PllNode;
  376.     lliPrev : PllNode;
  377.     lliItem : pointer;
  378.     lliInx  : integer;
  379.   end;
  380. {--------}
  381. function CreateLinkedList : PllNode;
  382. begin
  383.   Result := AllocMem(sizeof(TllNode));
  384.   Result^.lliNext := AllocMem(sizeof(TllNode));
  385.   Result^.lliNext^.lliPrev := Result;
  386. end;
  387. {--------}
  388. procedure DestroyLinkedList(aLinkedList : PllNode);
  389. var
  390.   Temp : PllNode;
  391. begin
  392.   while (aLinkedList <> nil) do begin
  393.     Temp := aLinkedList;
  394.     aLinkedList := aLinkedList^.lliNext;
  395.     FreeMem(Temp, sizeof(TllNode));
  396.   end;
  397. end;
  398. {--------}
  399. function AddLinkedListNode(aLinkedList : PllNode; aItem : pointer) : PllNode;
  400. begin
  401.   Result := AllocMem(sizeof(TllNode));
  402.   Result^.lliNext := aLinkedList^.lliNext;
  403.   Result^.lliPrev := aLinkedList;
  404.   aLinkedList^.lliNext^.lliPrev := Result;
  405.   aLinkedList^.lliNext := Result;
  406.   Result^.lliItem := aItem;
  407. end;
  408. {--------}
  409. procedure DeleteLinkedListNode(aLinkedList : PllNode; aNode : PllNode);
  410. begin
  411.   aNode^.lliPrev^.lliNext := aNode^.lliNext;
  412.   aNode^.lliNext^.lliPrev := aNode^.lliPrev;
  413.   FreeMem(aNode, sizeof(TllNode));
  414. end;
  415. {====================================================================}
  416.  
  417.  
  418. {===TaaPriorityQueueEx===============================================}
  419. constructor TaaPriorityQueueEx.Create(aCompareFn : TaaItemPriorityCompare);
  420. begin
  421.   inherited Create;
  422.   pqCompare := aCompareFn;
  423.   pqList := TList.Create;
  424.   pqHandles := CreateLinkedList;
  425. end;
  426. {--------}
  427. destructor TaaPriorityQueueEx.Destroy;
  428. begin
  429.   pqList.Free;
  430.   DestroyLinkedList(pqHandles);
  431.   inherited Destroy;
  432. end;
  433. {--------}
  434. function TaaPriorityQueueEx.Add(aItem : pointer) : TaaPQHandle;
  435. var
  436.   Handle : PllNode;
  437. begin
  438.   {add extra space at the end of the queue}
  439.   pqList.Count := pqList.Count + 1;
  440.   {create a new node for the linked list}
  441.   Handle := AddLinkedListNode(pqHandles, aItem);
  442.   {now bubble it up as far as it will go}
  443.   if (pqList.Count = 1) then begin
  444.     pqList[0] := Handle;
  445.     Handle^.lliInx := 0;
  446.   end
  447.   else
  448.     pqBubbleUp(pred(pqList.Count), Handle);
  449.   {return the handle}
  450.   Result := Handle;
  451.   {$IFOPT D+}
  452.   VerifyIndirection;
  453.   {$ENDIF}
  454. end;
  455. {--------}
  456. procedure TaaPriorityQueueEx.Delete(var aHandle : TaaPQHandle);
  457. var
  458.   Handle    : PllNode absolute aHandle;
  459.   NewHandle : PllNode;
  460.   HeapInx   : integer;
  461.   ParentInx    : integer;
  462.   ParentHandle : PllNode;
  463. begin
  464.   {delete the handle}
  465.   HeapInx := Handle^.lliInx;
  466.   DeleteLinkedListNode(pqHandles, Handle);
  467.   Handle := nil;
  468.   {check to see whether we deleted the last item, if so just shrink
  469.    the heap - the heap property will still apply}
  470.   if (HeapInx = pred(pqList.Count)) then
  471.     pqList.Count := pqList.Count - 1
  472.   else begin
  473.     {replace the heap element with the child at the lowest, rightmost
  474.      position, and shrink the list}
  475.     NewHandle := pqList.Last;
  476.     pqList[HeapInx] := NewHandle;
  477.     NewHandle^.lliInx := HeapInx;
  478.     pqList.Count := pqList.Count - 1;
  479.     {check to see whether we can bubble up}
  480.     if (HeapInx > 0) then begin
  481.       ParentInx := (HeapInx - 1) div 2;
  482.       ParentHandle := PllNode(pqList[ParentInx]);
  483.       if (pqCompare(NewHandle^.lliItem, ParentHandle^.lliItem) > 0) then begin
  484.         pqBubbleUp(HeapInx, NewHandle);
  485.         {$IFOPT D+}
  486.         VerifyIndirection;
  487.         {$ENDIF}
  488.         Exit;
  489.       end;
  490.     end;
  491.     {otherwise trickle down}
  492.     if (pqList.Count > 0) then
  493.       pqTrickleDown(HeapInx, pqList[HeapInx]);
  494.   end;
  495.   {$IFOPT D+}
  496.   VerifyIndirection;
  497.   {$ENDIF}
  498. end;
  499. {--------}
  500. procedure TaaPriorityQueueEx.pqBubbleUp(aFromInx : integer; aHandle : pointer);
  501. var
  502.   ParentInx    : integer;
  503.   ParentHandle : PllNode;
  504.   Handle       : PllNode absolute aHandle;
  505. begin
  506.   {while the handle under consideration is larger than its parent,
  507.    swap it with its parent and continue from its new position}
  508.   {Note: the parent for the child at index N is at (N-1) div 2}
  509.   if (aFromInx > 0) then begin
  510.     ParentInx := (aFromInx - 1) div 2;
  511.     ParentHandle := PllNode(pqList[ParentInx]);
  512.     {while our item has a parent, and it's greater than the parent...}
  513.     while (aFromInx > 0) and
  514.           (pqCompare(Handle^.lliItem, ParentHandle^.lliItem) > 0) do begin
  515.       {move our parent down the tree}
  516.       pqList[aFromInx] := ParentHandle;
  517.       ParentHandle^.lliInx := aFromInx;
  518.       aFromInx := ParentInx;
  519.       ParentInx := (aFromInx - 1) div 2;
  520.       ParentHandle := PllNode(pqList[ParentInx]);
  521.     end;
  522.   end;
  523.   {store our item in the correct place}
  524.   pqList[aFromInx] := Handle;
  525.   Handle^.lliInx := aFromInx;
  526. end;
  527. {--------}
  528. function TaaPriorityQueueEx.pqGetCount : integer;
  529. begin
  530.   Result := pqList.Count;
  531. end;
  532. {--------}
  533. procedure TaaPriorityQueueEx.pqTrickleDown(aFromInx : integer; aHandle : pointer);
  534. var
  535.   ListCount   : integer;
  536.   ChildInx    : integer;
  537.   ChildHandle : PllNode;
  538.   Handle      : PllNode absolute aHandle;
  539. begin
  540.   {while the item under consideration is smaller than one of its
  541.    children, swap it with the larger child and continue from its new
  542.    position}
  543.   {Note: the children for the parent at index N are at (2N+1) and
  544.          2N+2}
  545.   ListCount := pqList.Count;
  546.   {calculate the left child index}
  547.   ChildInx := succ(aFromInx * 2);
  548.   {while there is at least a left child...}
  549.   while (ChildInx < ListCount) do begin
  550.     {if there is a right child, calculate the index of the larger
  551.      child}
  552.     if (succ(ChildInx) < ListCount) and
  553.        (pqCompare(PllNode(pqList[ChildInx])^.lliItem,
  554.                   PllNode(pqList[succ(ChildInx)])^.lliItem) < 0) then
  555.       inc(ChildInx);
  556.     {if our item is greater or equal to the larger child, we're done}
  557.     ChildHandle := PllNode(pqList[ChildInx]);
  558.     if (pqCompare(Handle^.lliItem, ChildHandle^.lliItem) >= 0) then
  559.       Break;
  560.     {otherwise move the larger child up the tree, and move our item
  561.      down the tree and repeat}
  562.     pqList[aFromInx] := ChildHandle;
  563.     ChildHandle^.lliInx := aFromInx;
  564.     aFromInx := ChildInx;
  565.     ChildInx := succ(aFromInx * 2);
  566.   end;
  567.   {store our item in the correct place}
  568.   pqList[aFromInx] := Handle;
  569.   Handle^.lliInx := aFromInx;
  570. end;
  571. {--------}
  572. function TaaPriorityQueueEx.Remove : pointer;
  573. var
  574.   Handle : PllNode;
  575. begin
  576.   {return the item at the root}
  577.   Handle := pqList[0];
  578.   Result := Handle^.lliItem;
  579.   DeleteLinkedListNode(pqHandles, Handle);
  580.   {replace the root with the child at the lowest, rightmost position,
  581.    and shrink the list}
  582.   Handle := pqList.Last;
  583.   pqList[0] := Handle;
  584.   Handle^.lliInx := 0;
  585.   pqList.Count := pqList.Count - 1;
  586.   {now trickle down the root item as far as it will go}
  587.   if (pqList.Count > 0) then
  588.     pqTrickleDown(0, Handle);
  589.   {$IFOPT D+}
  590.   VerifyIndirection;
  591.   {$ENDIF}
  592. end;
  593. {--------}
  594. procedure TaaPriorityQueueEx.Replace(aHandle : TaaPQHandle; aItem : pointer);
  595. var
  596.   Handle : PllNode absolute aHandle;
  597.   ParentInx    : integer;
  598.   ParentHandle : PllNode;
  599. begin
  600.   {first, replace the item}
  601.   Handle^.lliItem := aItem;
  602.   {check to see whether we can bubble up}
  603.   if (Handle^.lliInx > 0) then begin
  604.     ParentInx := (Handle^.lliInx - 1) div 2;
  605.     ParentHandle := PllNode(pqList[ParentInx]);
  606.     if (pqCompare(Handle^.lliItem, ParentHandle^.lliItem) > 0) then begin
  607.       pqBubbleUp(Handle^.lliInx, Handle);
  608.       {$IFOPT D+}
  609.       VerifyIndirection;
  610.       {$ENDIF}
  611.       Exit;
  612.     end;
  613.   end;
  614.   {otherwise trickle down}
  615.   pqTrickleDown(Handle^.lliInx, Handle);
  616.   {$IFOPT D+}
  617.   VerifyIndirection;
  618.   {$ENDIF}
  619. end;
  620. {--------}
  621. {$IFOPT D+}
  622. procedure TaaPriorityQueueEx.VerifyIndirection;
  623. var
  624.   i : integer;
  625.   Handle : PllNode;
  626. begin
  627.   for i := 0 to pred(pqList.Count) do begin
  628.     Handle := PllNode(pqList[i]);
  629.     if (Handle^.lliInx <> i) then begin
  630.       writeln('ERROR: Handle at ', i, ' doesn''t point to it');
  631.       readln;
  632.     end;
  633.   end;
  634. end;
  635. {$ENDIF}
  636. {====================================================================}
  637.  
  638. end.
  639.