* Bug in code for Priority Queue
@ 1995-01-03 23:35 Tucker Taft
1995-01-05 12:23 ` Jardine Barrington-Cook
0 siblings, 1 reply; 2+ messages in thread
From: Tucker Taft @ 1995-01-03 23:35 UTC (permalink / raw)
Brian Hanson of Cray (brh@cray.com) kindly pointed out a bug
in the code for a priority queue I posted several weeks ago,
which arose during my effort to create a stand-alone
priority queue package from a version that was embedded
in something else. So much for reuse by transcription ;-).
In any case, here is a description of the bug and the fix...
In the procedure Remove_Top_Prio, the local variable Parent_Limit
is declared:
Parent_Limit : Heap_Index; -- WRONG
and then initialized:
Parent_Limit := Num_In_Heap / 2;
Since Num_In_Heap might be 1, Parent_Limit might be 0.
Hence, the subtype of Parent_Limit should be Heap_Count instead of
Heap_Index; that is:
Parent_Limit : Heap_Count; -- RIGHT
Sorry for any inconvenience.
-Tucker Taft stt@inmet.com
P.S. Here is the complete, repaired code, if you are interested:
-----------------------------------------------
-- Copyright (C) 1995 Intermetrics, Inc.
-- May be copied if accompanied by this notice.
package Priority_Queue is
-- Provide a priority queue; greater priority means more urgent.
Min_Prio : constant := 0;
Max_Prio : constant := 100;
type Priority is range Min_Prio .. Max_Prio;
type Queue_Entry_Record;
type Queue_Entry is access Queue_Entry_Record;
type Queue_Entry_Record is
record
Prio : Priority;
Next : Queue_Entry;
Other_Info : Integer; -- Or whatever
end record;
procedure Add_Entry(Ent : Queue_Entry);
-- Add Ent to priority queue
procedure Remove_Top_Prio(Ent : out Queue_Entry);
-- Remove and return an entry with the highest priority
-- Ent set to null if queue is empty.
function Top_Prio return Queue_Entry;
-- Return pointer to entry with highest priority (leave in queue)
-- Returns null if queue is empty.
function Is_Empty return Boolean;
-- Return True if queue is empty
end Priority_Queue;
package body Priority_Queue is
-- Implement a priority queue; greater priority means more urgent.
-- Internal representation is a binary heap,
-- plus an array of FIFO queues.
type FIFO_Queue_Header is
record
First, Last : Queue_Entry := null;
end record;
FIFO_Queues : array(Priority) of FIFO_Queue_Header;
-- Array of singly-linked FIFO queues
Num_Prio : constant := Max_Prio - Min_Prio + 1;
type Heap_Count is range 0 .. Num_Prio + 1;
-- One extra so remove may assume have either 2 or 0 children
subtype Heap_Index is Heap_Count range 1 .. Heap_Count'Last;
Heap : array(Heap_Index) of Priority := (Heap_Index => Min_Prio);
-- Heap of priority values, indicating highest priority
-- with non-empty set of entries;
-- Preinitialized to Min_Prio to simplify removal
-- Binary heap condition is: Heap(N) > Max(Heap(2*N), Heap(2*N+1))
Num_In_Heap : Heap_Count := 0; -- Count of number in heap
procedure Add_Entry(Ent : Queue_Entry) is
-- Add Ent to priority queue
-- Algorithm:
-- Add new entry to its FIFO queue. If its FIFO queue was empty,
-- add its priority to end of heap, and then "sift" it up
-- until binary heap condition is restored.
Parent, Child : Heap_Index;
Prio_To_Add : constant Priority := Ent.Prio;
FIFO_Queue : FIFO_Queue_Header renames FIFO_Queues(Prio_To_Add);
begin
Ent.Next := null;
if FIFO_Queue.Last /= null then
-- There are already elements for this priority;
-- Just add to end of its FIFO queue and return.
FIFO_Queue.Last.Next := Ent;
return;
end if;
-- This is a new priority, add to binary heap
Num_In_Heap := Num_In_Heap + 1;
-- Start at end of heap
Child := Num_In_Heap;
-- Loop until reach top of heap, or parent greater than new prio
while Child > 1 loop
Parent := Child / 2; -- Find slot which should be greater
exit when Heap(Parent) >= Prio_To_Add;
-- All done if parent now greater
-- Parent smaller, move it down to child's slot
Heap(Child) := Heap(Parent);
-- Advance up to parent slot and iterate
Child := Parent;
end loop;
-- ASSERT: Prio_To_Add < Heap(Child/2) or reached top-of-heap
Heap(Child) := Prio_To_Add;
-- Now initialize FIFO queue header to point to new entry
FIFO_Queue.First := Ent;
FIFO_Queue.Last := Ent;
end Add_Entry;
procedure Remove_Top_Prio(Ent : out Queue_Entry) is
-- Remove and return an entry with the highest priority
-- Ent set to null if FIFO queue is empty.
-- Algorithm:
-- Retrieve highest priority in heap. Remove first element
-- from corresponding FIFO queue. If FIFO queue now empty,
-- remove corresponding priority from heap, and "sift"
-- priority at bottom of heap down from top of heap,
-- until binary heap condition is restored.
Top_Prio : Priority;
Prio_To_Sift : Priority;
Parent, Child : Heap_Index;
Parent_Limit : Heap_Count; -- Might be zero
begin
if Num_In_Heap = 0 then
-- Priority queue is empty
Ent := null;
return;
end if;
Top_Prio := Heap(Heap'First); -- Get highest priority
-- Remove first element from appropriate FIFO queue
Ent := FIFO_Queues(Top_Prio).First;
FIFO_Queues(Top_Prio).First := Ent.Next;
if Ent.Next /= null then
-- There are more in same FIFO queue, just return now
return;
end if;
-- This is only entry in this FIFO queue, set Last to null
-- and remove its priority from binary heap
FIFO_Queues(Top_Prio).Last := null;
-- Get priority from bottom of heap to sift down
Prio_To_Sift := Heap(Num_In_Heap);
-- Replace it with min-prio to allow us to ignore
-- special case when parent has exactly one child.
Heap(Num_In_Heap) := Min_Prio;
-- Shorten the heap
Num_In_Heap := Num_In_Heap - 1;
if Num_In_Heap = 0 then
-- All done if heap now empty
return;
end if;
-- Start at top of heap
Parent := 1;
-- Calculate limit on parent (to avoid overflow on
-- child calculation).
Parent_Limit := Num_In_Heap / 2;
-- Loop until reach bottom of heap,
-- or prio-to-sift > both children
while Parent <= Parent_Limit loop
-- Determine larger "child"
Child := Parent * 2;
if Heap(Child + 1) > Heap(Child) then
-- 2*N+1 child is larger than 2*N child
Child := Child + 1;
end if;
exit when Prio_To_Sift >= Heap(Child);
-- All done when found slot for prio-to-sift
-- Move larger child up, and iterate
Heap(Parent) := Heap(Child);
Parent := Child;
end loop;
-- ASSERT: Prio_To_Sift > both children (2*Parent, 2*Parent+1)
-- or slot has no children
-- Put prio in its new slot
Heap(Parent) := Prio_To_Sift;
end Remove_Top_Prio;
function Top_Prio return Queue_Entry is
-- Return pointer to entry with highest priority (leave in queue)
-- Returns null if queue is empty.
begin
if Num_In_Heap = 0 then
return null;
else
return FIFO_Queues(Heap(Heap'First)).First;
end if;
end Top_Prio;
function Is_Empty return Boolean is
-- Return true if queue is empty
begin
return Num_In_Heap = 0;
end Is_Empty;
end Priority_Queue;
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: Bug in code for Priority Queue
1995-01-03 23:35 Bug in code for Priority Queue Tucker Taft
@ 1995-01-05 12:23 ` Jardine Barrington-Cook
0 siblings, 0 replies; 2+ messages in thread
From: Jardine Barrington-Cook @ 1995-01-05 12:23 UTC (permalink / raw)
In article <D1us76.Gp@inmet.camb.inmet.com> stt@dsd.camb.inmet.com (Tucker Taft) writes:
>Newsgroups: comp.lang.ada
>Path: logica.co.uk!pipex!howland.reston.ans.net!gatech!bloom-beacon.mit.edu!uhog.mit.edu!news.mathworks.com!noc.near.net!inmet!dsd!stt
>From: stt@dsd.camb.inmet.com (Tucker Taft)
>Subject: Bug in code for Priority Queue
>Message-ID: <D1us76.Gp@inmet.camb.inmet.com>
>Sender: news@inmet.camb.inmet.com
>Organization: Intermetrics, Inc.
>Date: Tue, 3 Jan 1995 23:35:29 GMT
>Lines: 246
>Brian Hanson of Cray (brh@cray.com) kindly pointed out a bug
>in the code for a priority queue I posted several weeks ago,
>which arose during my effort to create a stand-alone
>priority queue package from a version that was embedded
>in something else. So much for reuse by transcription ;-).
>In any case, here is a description of the bug and the fix...
[.. real code cut out...]
There is a logical problem with the use of priority queues for warning
messages, which I would like to publicise. I have seen this problem twice, and
once was in an aircraft audio alarm system.
When putting an element of priority "n" onto a prioritorised warning queue, it
is essential to search the lower priority queues for the same warning at a
lower severity - if you do not do this the resulting warnings to the operator
can be very misleading.
In a UK Television broadcast of the 1983 election results this caused
two succesive headline messages to appear in the form:
"4th recount at Molesly North" followed by
"3rd recount at Molesly North".
In the aircraft audio warning case the results were of the form:
"Warning: aircraft below 30 meters"
"Warning: aircraft below 40 meters"
(the aircraft was still descending, and below 30 meters)
I for one would be very happy never to see this problem again.
Jardine Barrington-Cook ! "...See worlds on worlds compose one universe
Space Division - Logica ! observe how system into system runs..."
...personal opinions, !
subject to change... ! A. Pope - from "An Essay on Man", 1773
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~1995-01-05 12:23 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1995-01-03 23:35 Bug in code for Priority Queue Tucker Taft
1995-01-05 12:23 ` Jardine Barrington-Cook
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox