Files
lazarus-ccr/components/systools/source/general/run/stpqueue.pas

743 lines
18 KiB
ObjectPascal
Raw Normal View History

// Upgraded to Delphi 2009: Sebastian Zierer
(* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is TurboPower SysTools
*
* The Initial Developer of the Original Code is
* TurboPower Software
*
* Portions created by the Initial Developer are Copyright (C) 1996-2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** *)
{*********************************************************}
{* SysTools: StPQueue.pas 4.04 *}
{*********************************************************}
{* SysTools: Priority Queue Classes *}
{*********************************************************}
{$IFDEF FPC}
{$mode DELPHI}
{$ENDIF}
//{$I StDefine.inc}
{Notes:
Based on the double-ended heap (deap) described in Horowitz and Sahni,
Data Structures and Algorithms in C.
The deap was first reported in:
Svante Carlsson, "The Deap - a double-ended heap to implement double-
ended priority queues", Information Processing Letters, 26,
pp. 33-36, 1987.
A deap is a complete binary tree. The root node holds no data. Its
left subtree is a min heap. Its right subtree is a max heap. If the right
subtree is not empty, let i be any node in the left subtree. Let j be
the node at the corresponding position in the right subtree. If such a
j does not exist, let j be the node in the right subtree at the position
corresponding to i's parent. The deap has the property that the data in
node i is less than or equal to the data in node j.
Insertion is an O(log2(n)) operation. Deletion of the min or max node
is also an O(log2(n)) operation.
Data elements in the deap are pointers, which can point to any record
structure or class, or can contain any data type of 4 bytes or less.
The deap needs an ordering relationship, so it is essential to assign
to the Compare property inherited from the TStContainer class.
STPQUEUE uses the DisposeData procedure of TStContainer to determine
how to free elements in the collection. By default, it does nothing.
In 16-bit programs the deap is limited to 16380 elements. In 32-bit
programs the limit is set by memory usage or performance.
}
unit StPQueue;
interface
uses
{$IFNDEF FPC}
Windows,
{$ENDIF}
SysUtils, Classes,
StConst, StBase;
type
{first actual element is at index 2}
{.Z+}
TStPQData = array[2..(StMaxBlockSize div SizeOf(Pointer))+1] of Pointer;
PStPQData = ^TStPQData;
{.Z-}
TStPQueue = class(TStContainer)
{.Z+}
protected {private}
pqData : PStPQData; {data - the complete binary tree}
pqCapacity : Integer; {max elements currently possible}
pqDelta : Integer; {delta elements to grow when needed}
procedure ForEachPointer(Action : TIteratePointerFunc; OtherData : Pointer);
override;
function StoresPointers : Boolean;
override;
procedure Expand(Need : Integer);
procedure InsertMin(I : Integer; Data : Pointer);
procedure InsertMax(I : Integer; Data : Pointer);
procedure ModifiedInsert(I : Integer; Data : Pointer);
{.Z-}
public
constructor Create(InitCapacity, Delta : Integer);
virtual;
{-Initialize an empty PQueue of given capacity. If it overflows
grow the PQueue by Delta elements}
destructor Destroy;
override;
{-Free a PQueue}
procedure LoadFromStream(S : TStream);
override;
{-Create a PQueue and its data from a stream}
procedure StoreToStream(S : TStream);
override;
{-Write a PQueue and its data to a stream}
procedure Clear;
override;
{-Remove all data from container but leave it instantiated and
with its current capacity}
procedure Insert(Data : Pointer);
{-Add a new node}
function DeleteMin : Pointer;
{-Remove the minimum node and return its Pointer}
function DeleteMax : Pointer;
{-Remove the maximum node and return its Pointer}
procedure Assign(Source : TPersistent);
override;
{-Assign another container's contents to this one. Only SysTools
containers that store pointers are allowed.}
procedure Join(Q : TStPQueue);
{-Add PQueue Q into this one and dispose Q}
function Iterate(Action : TIteratePointerFunc;
OtherData : Pointer) : Pointer;
{-Call Action for all the nodes or until Action returns false. Note
that the nodes are visited in no particular order.}
function Test : Boolean;
{-Determine whether deap properties are currently valid (for debugging)}
end;
{.Z+}
TStPQueueClass = class of TStPQueue;
{.Z-}
implementation
{$IFDEF ThreadSafe}
var
ClassCritSect : TRTLCriticalSection;
{$ENDIF}
type
TStoreInfo = record
Wtr : TWriter;
SDP : TStoreDataProc;
end;
function AssignData(Container : TStContainer;
Data, OtherData : Pointer) : Boolean; far;
begin
TStPQueue(OtherData).Insert(Data);
AssignData := True;
end;
function DestroyNode(Container : TStContainer;
Data, OtherData : Pointer) : Boolean; far;
begin
if Assigned(Data) then
Container.DoDisposeData(Data);
DestroyNode := True;
end;
procedure EnterClassCS;
begin
{$IFDEF ThreadSafe}
EnterCriticalSection(ClassCritSect);
{$ENDIF}
end;
function JoinData(Container : TStContainer;
Data, OtherData : Pointer) : Boolean; far;
begin
TStPQueue(OtherData).Insert(Data);
JoinData := True;
end;
procedure LeaveClassCS;
begin
{$IFDEF ThreadSafe}
LeaveCriticalSection(ClassCritSect);
{$ENDIF}
end;
function log2(I : Integer) : Integer;
{-Return the Integer below log2(I)}
begin
Result := 0;
while (I > 1) do begin
Inc(Result);
I := I shr 1;
end;
end;
function StoreNode(Container : TStContainer;
Data, OtherData : Pointer) : Boolean; far;
begin
StoreNode := True;
with TStoreInfo(OtherData^) do
SDP(Wtr, Data);
end;
procedure TStPQueue.Assign(Source : TPersistent);
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
if not AssignPointers(Source, AssignData) then
inherited Assign(Source);
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
procedure TStPQueue.Clear;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
if conNodeProt = 0 then
ForEachPointer(StPQueue.DestroyNode, nil);
FCount := 0;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
constructor TStPQueue.Create(InitCapacity, Delta : Integer);
begin
if (InitCapacity < 2) or (Delta < 1) then
RaiseContainerError(stscBadSize);
FCount := 0;
{ensure that Expand creates initial capacity InitCapacity}
pqCapacity := -Delta;
pqDelta := Delta;
pqData := nil;
CreateContainer(TStNode, 0);
Expand(InitCapacity);
end;
function TStPQueue.DeleteMin : Pointer;
var
I, j, n : Integer;
Temp : Pointer;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
if (FCount < 1) then begin
{deap is empty}
DeleteMin := nil;
exit;
end;
{return min element}
DeleteMin := pqData^[2];
{save last element and reset (helps debugging)}
Temp := pqData^[FCount+1];
pqData^[FCount+1] := nil;
{decrement count, n is index of new last element}
n := FCount;
dec(FCount);
if (FCount > 0) then begin
{move empty min-root down to an appropriate leaf}
I := 2;
while (I shl 1 <= n) do begin
{find child with smaller key}
j := I shl 1;
if (j+1 <= n) then
if (DoCompare(pqData^[j], pqData^[j+1]) > 0) then
Inc(j);
pqData^[I] := pqData^[j];
I := j;
end;
{insert the old last element at the given leaf position}
ModifiedInsert(I, Temp);
end;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
function TStPQueue.DeleteMax : Pointer;
var
I, j, n : Integer;
Temp : Pointer;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
if (FCount < 1) then begin
{deap is empty}
DeleteMax := nil;
exit;
end;
{return max element}
if (FCount = 1) then
DeleteMax := pqData^[2]
else
DeleteMax := pqData^[3];
{save last element and reset (helps debugging)}
Temp := pqData^[FCount+1];
pqData^[FCount+1] := nil;
{decrement count, n is index of new last element}
n := FCount;
dec(FCount);
if (FCount > 0) then begin
{move empty max-root down to an appropriate leaf}
I := 3;
while (I shl 1 <= n) do begin
{find child with larger key}
j := I shl 1;
if (j+1 <= n) then
if (DoCompare(pqData^[j], pqData^[j+1]) < 0) then
Inc(j);
pqData^[I] := pqData^[j];
I := j;
end;
{insert the old last element at the given leaf position}
ModifiedInsert(I, Temp);
end;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
destructor TStPQueue.Destroy;
begin
if (pqData <> nil) then begin
Clear;
FreeMem(pqData, pqCapacity*SizeOf(Pointer));
end;
IncNodeProtection;
inherited Destroy;
end;
procedure TStPQueue.Expand(Need : Integer);
var
NewCapacity : Integer;
Size : LongInt;
NewData : PStPQData;
begin
if Need > pqCapacity then begin
{determine new capacity}
NewCapacity := pqCapacity+pqDelta;
if (NewCapacity < Need) then
NewCapacity := Need;
{make sure it's feasible to allocate it}
Size := LongInt(NewCapacity)*SizeOf(Pointer);
{if Size > MaxBlockSize then}
{RaiseContainerError(stscBadSize);}
{allocate new data}
GetMem(NewData, Size);
{copy old data to it and free old data}
if (pqData <> nil) then begin
move(pqData^, NewData^, pqCapacity*SizeOf(Pointer));
FreeMem(pqData, pqCapacity*SizeOf(Pointer));
end;
{update instance variables}
pqData := NewData;
pqCapacity := NewCapacity;
end;
end;
procedure TStPQueue.ForEachPointer(Action : TIteratePointerFunc; OtherData : Pointer);
var
I : Integer;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
{first element is 2, last is FCount+1}
for I := 2 to FCount+1 do
if not Action(Self, pqData^[I], OtherData) then
Exit;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
procedure TStPQueue.Insert(Data : Pointer);
var
I, n, p : Integer;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
{adding an element, make sure there's space}
Inc(FCount);
Expand(FCount);
if (FCount = 1) then
{insert into empty deap}
pqData^[2] := Data
else begin
{n is the actual array index}
n := FCount+1;
{determine whether n is in the min or max subtree}
p := n;
while (p > 3) do
p := p shr 1;
if (p = 2) then begin
{n is a position on the min side}
{I is its partner on the max side}
I := (n+(1 shl (log2(n)-1))) shr 1;
if (DoCompare(Data, pqData^[I]) > 0) then begin
pqData^[n] := pqData^[I];
InsertMax(I, Data);
end else
InsertMin(n, Data);
end else begin
{n is a position on the max side}
{I is its partner on the min side}
I := n-(1 shl (log2(n)-1));
if (DoCompare(Data, pqData^[I]) < 0) then begin
pqData^[n] := pqData^[I];
InsertMin(I, Data);
end else
InsertMax(n, Data);
end;
end;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
procedure TStPQueue.InsertMin(I : Integer; Data : Pointer);
{-Insert into min-heap rooted at node 2}
var
j : Integer;
begin
while (I > 2) and (DoCompare(Data, pqData^[I shr 1]) < 0) do begin
j := I shr 1;
pqData^[I] := pqData^[j];
I := j;
end;
pqData^[I] := Data;
end;
procedure TStPQueue.InsertMax(I : Integer; Data : Pointer);
{-Insert into max-heap rooted at node 3}
var
j : Integer;
begin
while (I > 3) and (DoCompare(Data, pqData^[I shr 1]) > 0) do begin
j := I shr 1;
pqData^[I] := pqData^[j];
I := j;
end;
pqData^[I] := Data;
end;
function TStPQueue.Iterate(Action : TIteratePointerFunc;
OtherData : Pointer) : Pointer;
var
I : Integer;
begin
Iterate := nil;
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
{first element is 2, last is FCount+1}
for I := 2 to FCount+1 do
if not Action(Self, pqData^[I], OtherData) then begin
Iterate := pqData^[I];
Exit;
end;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
procedure TStPQueue.Join(Q : TStPQueue);
begin
{$IFDEF ThreadSafe}
EnterClassCS;
EnterCS;
Q.EnterCS;
try
{$ENDIF}
if (not Assigned(Q)) then
RaiseContainerError(stscBadType);
Q.ForEachPointer(JoinData, Self);
Q.IncNodeProtection;
Q.Free;
{$IFDEF ThreadSafe}
finally
Q.LeaveCS;
LeaveCS;
LeaveClassCS;
end;
{$ENDIF}
end;
procedure TStPQueue.LoadFromStream(S : TStream);
var
Data : Pointer;
Reader : TReader;
StreamedClass : TPersistentClass;
StreamedClassName : string;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
Clear;
Reader := TReader.Create(S, 1024);
try
with Reader do begin
StreamedClassName := ReadString;
StreamedClass := GetClass(StreamedClassName);
if (StreamedClass = nil) then
RaiseContainerErrorFmt(stscUnknownClass, [StreamedClassName]);
if (not IsOrInheritsFrom(StreamedClass, Self.ClassType)) or
(not IsOrInheritsFrom(TStPQueue, StreamedClass)) then
RaiseContainerError(stscWrongClass);
ReadListBegin;
while not EndOfList do begin
Data := DoLoadData(Reader);
Insert(Data);
end;
ReadListEnd;
end;
finally
Reader.Free;
end;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
procedure TStPQueue.ModifiedInsert(I : Integer; Data : Pointer);
{-Special insert after a delete. I is the actual array index where
insertion of Data occurs. Tree does not grow.}
var
p, j : Integer;
begin
if (I > 1) then begin
{determine whether I is in the min or max subtree}
p := I;
while (p > 3) do
p := p shr 1;
if (p = 2) then begin
{I is a position on the min side}
{j is its partner on the max side}
j := I+(1 shl (log2(I)-1));
if (j > FCount+1) then
j := j shr 1;
if (j < 3) then
{empty max heap}
pqData^[I] := Data
else if (DoCompare(Data, pqData^[j]) > 0) then begin
pqData^[I] := pqData^[j];
InsertMax(j, Data);
end else
InsertMin(I, Data);
end else begin
{I is a position on the max side}
{j is its partner on the min side}
j := I-(1 shl (log2(I)-1));
{check its children too to preserve deap property}
if (j shl 1 <= FCount+1) then begin
j := j shl 1;
if (j+1 <= FCount+1) then
if (DoCompare(pqData^[j], pqData^[j+1]) < 0) then
Inc(j);
end;
if (DoCompare(Data, pqData^[j]) < 0) then begin
pqData^[I] := pqData^[j];
InsertMin(j, Data);
end else
InsertMax(I, Data);
end;
end;
end;
function TStPQueue.StoresPointers : Boolean;
begin
StoresPointers := True;
end;
procedure TStPQueue.StoreToStream(S : TStream);
var
Writer : TWriter;
StoreInfo : TStoreInfo;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
Writer := TWriter.Create(S, 1024);
try
with Writer do begin
WriteString(Self.ClassName);
WriteListBegin;
StoreInfo.Wtr := Writer;
StoreInfo.SDP := StoreData;
Iterate(StoreNode, @StoreInfo);
WriteListEnd;
end;
finally
Writer.Free;
end;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
function TStPQueue.Test : Boolean;
var
I, i2, j, n, p : Integer;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
Test := True;
if (FCount = 0) then
exit;
n := FCount+1;
{start with each leaf node}
for I := (1 shl log2(n)) to n do begin
p := I;
while (p > 3) do
p := p shr 1;
if (p = 2) then begin
{I is a position on the min side}
{test min-heap condition}
i2 := I;
while (i2 shr 1 >= 2) do begin
j := i2 shr 1;
if (DoCompare(pqData^[j], pqData^[i2]) > 0) then begin
Test := false;
{writeln('min: j=', j, ' i2=', i2,
' d[j]=', Integer(pqData^[j]), ' d[i2]=', Integer(pqData^[i2]));}
exit;
end;
i2 := j;
end;
{test deap condition}
if n >= 3 then begin
j := I+(1 shl (log2(I)-1));
if (j > n) then
j := j shr 1;
if (DoCompare(pqData^[I], pqData^[j]) > 0) then begin
Test := false;
{writeln('deap: j=', j, ' I=', I,
' d[j]=', Integer(pqData^[j]), ' d[I]=', Integer(pqData^[I]));}
exit;
end;
end;
end else begin
{I is a position on the max side}
{test max-heap condition}
i2 := I;
while (i2 shr 1 >= 3) do begin
j := i2 shr 1;
if (DoCompare(pqData^[j], pqData^[i2]) < 0) then begin
Test := false;
{writeln('max: j=', j, ' i2=', i2,
' d[j]=', Integer(pqData^[j]), ' d[i2]=', Integer(pqData^[i2]));}
exit;
end;
i2 := j;
end;
end;
end;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
{$IFDEF ThreadSafe}
initialization
Windows.InitializeCriticalSection(ClassCritSect);
finalization
Windows.DeleteCriticalSection(ClassCritSect);
{$ENDIF}
end.