############################################################################
#
#       File:     pqueue.icn
#
#       Subject:  Procedures for manipulating priority queues
#
#       Authors:  William S. Evans and Gregg M. Townsend
#
#       Date:     May 3, 2001
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#       These procedures manipulate priority queues.
#
#	pq(L)		returns a priority queue containing the elements
#			in L.  L is a list (or table or set) of pqelem
#			records, each containing a data and priority field.
#			If L is &null, pq() returns an empty priority queue.
#
#	pqget(Q)	returns and removes the highest priority element
#			from Q.  Q is a priority queue returned by pq().
#
#	pqput(Q, e)	adds element e (a pqelem record) to Q.
#
#	pqgen(Q)	generates the elements in Q in priority order.
#
#	pqelem(d, p)	constructs a record with data d and priority p.
#
############################################################################
#
#	Priority queues are implemented as heaps.  Heaps are
#	implemented as lists in the usual fashion.
#
############################################################################

record pqelem (
   data,		# element's data
   priority		# element's priority
   )

procedure pq(L)				#: create priority queue
   local Q, i, e

   /L := list()
   Q := list()
   every e := !L do
      put(Q, pqelem(e.data, numeric(e.priority) | runerr(102, e.priority)))
   every i := *Q / 2 to 1 by -1 do
      pq__down(Q, i)
   return Q
end

procedure pqget(Q)			#: remove first priority queue element
   local e

   e := get(Q) | fail
   push(Q, pull(Q))
   pq__down(Q, 1)
   return e
end

procedure pqgen(Q)			#: generate priority queue elements
   local q, e

   q := copy(Q)
   while e := copy(pqget(q)) do
      suspend e
end

procedure pqput(Q, e)			#: insert priority queue element
   put(Q, pqelem(e.data, numeric(e.priority) | runerr(102, e.priority)))
   pq__up(Q, *Q)
   return Q
end

# Procedures named with a "pq__" prefix are not
# intended for access outside this file.

procedure pq__down(Q, i)
   local left, right, largest

   left := i * 2
   right := left + 1

   if Q[left].priority > Q[i].priority then largest := left
   else largest := i
   if Q[right].priority > Q[largest].priority then largest := right
   if largest ~= i then {
      Q[i] :=: Q[largest]
      pq__down(Q, largest)
      }
   return
end

procedure pq__up(Q, i)
   local parent

   parent := i / 2
   if Q[i].priority > Q[parent].priority then {
      Q[i] :=: Q[parent]
      pq__up(Q, parent)
      }
   return
end
