github.com/haraldrudell/parl@v0.4.176/pqs/aggregating-priority-queue.go (about)

     1  /*
     2  © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pqs
     7  
     8  import (
     9  	"github.com/haraldrudell/parl"
    10  	"github.com/haraldrudell/parl/parli"
    11  	"github.com/haraldrudell/parl/pslices"
    12  	"golang.org/x/exp/constraints"
    13  )
    14  
    15  // AggregatingPriorityQueue implements a priority queue using
    16  // cached priority from aggregators
    17  //   - item type is parl.AggregatePriority that can cache priorities
    18  //   - item identity is the pointer value to each aggregator of type V
    19  //   - the queue is periodically cleared, then regenerated by updating all items
    20  //   - the return queue length is configurable to be 1 or longer
    21  //   - P is the type used for priority, ordered highest first
    22  //   - insertion order is used for equal priorities, order lowest/earliest first
    23  type AggregatingPriorityQueue[V any, P constraints.Ordered] struct {
    24  	// queue is a list of queue nodes ordered by rank
    25  	//	- element is parl.AggregatePriority: pointer to AggregatePriority
    26  	//	- AggregatePriority holds a cached priority value and the aggregator with running totals
    27  	queue parli.Ordered[parl.AggregatePriority[V, P]]
    28  	// m provides O(1) access to priority data-nodes via the value-pointer
    29  	m map[*V]parl.AggregatePriority[V, P]
    30  	// indexGenerator provides IDs for insertion-ordering
    31  	indexGenerator parl.UniqueIDint
    32  	// non-zero for limiting queue length
    33  	maxQueueLength int
    34  }
    35  
    36  var _ AggregatePriority[int, int]
    37  
    38  // NewAggregatingPriorityQueue returns a map of updatable values traversable by rank
    39  func NewAggregatingPriorityQueue[V any, P constraints.Ordered](
    40  	maxQueueLength ...int,
    41  ) (priorityQueue parl.AggregatingPriorityQueue[V, P]) {
    42  	var maxQueueLength0 int
    43  	if len(maxQueueLength) > 0 {
    44  		maxQueueLength0 = maxQueueLength[0]
    45  	}
    46  	if maxQueueLength0 < 1 {
    47  		maxQueueLength0 = 1
    48  	}
    49  	var a *AggregatePriority[V, P]
    50  	return &AggregatingPriorityQueue[V, P]{
    51  		m:              map[*V]parl.AggregatePriority[V, P]{},
    52  		queue:          pslices.NewOrderedAny(a.Cmp),
    53  		maxQueueLength: maxQueueLength0,
    54  	}
    55  }
    56  
    57  // Get retrieves a the value container with running totals associated with the identity valuep
    58  func (a *AggregatingPriorityQueue[V, P]) Get(valuep *V) (aggregator parl.Aggregator[V, P], ok bool) {
    59  	var nodep parl.AggregatePriority[V, P]
    60  	if nodep, ok = a.m[valuep]; ok {
    61  		aggregator = nodep.Aggregator()
    62  	}
    63  	return
    64  }
    65  
    66  // Put stores a new value container associated with valuep
    67  //   - the valuep is assumed to not have a node in the queue
    68  func (a *AggregatingPriorityQueue[V, P]) Put(valuep *V, aggregator parl.Aggregator[V, P]) {
    69  
    70  	// create aggregatePriority with current priority from aggregator
    71  	aggregatePriority := NewAggregatePriority(
    72  		valuep,
    73  		a.indexGenerator.ID(),
    74  		aggregator,
    75  	)
    76  
    77  	// store in map
    78  	a.m[valuep] = aggregatePriority
    79  	a.insert(aggregatePriority)
    80  }
    81  
    82  // Update re-prioritizes a value
    83  func (a *AggregatingPriorityQueue[V, P]) Update(valuep *V) {
    84  
    85  	var aggregatePriority parl.AggregatePriority[V, P]
    86  	var ok bool
    87  	if aggregatePriority, ok = a.m[valuep]; !ok {
    88  		return // value priority does not exist return
    89  	}
    90  
    91  	// update order: that’s what we do here. We keep order
    92  	a.queue.Delete(aggregatePriority)
    93  	var _ = (&AggregatePriority[int, int]{}).Update
    94  	aggregatePriority.Update()
    95  	a.insert(aggregatePriority)
    96  }
    97  
    98  var _ = ((parl.AggregatingPriorityQueue[int, int])(&AggregatingPriorityQueue[int, int]{})).Clear
    99  
   100  // Clear empties the priority queue. The hashmap is left intact.
   101  func (a *AggregatingPriorityQueue[V, P]) Clear() {
   102  	a.queue.Clear()
   103  }
   104  
   105  // List returns the first n or default all values by pirority
   106  func (a *AggregatingPriorityQueue[V, P]) List(n ...int) (aggregatorQueue []parl.AggregatePriority[V, P]) {
   107  	return a.queue.List(n...)
   108  }
   109  
   110  func (a *AggregatingPriorityQueue[V, P]) insert(aggregatePriority parl.AggregatePriority[V, P]) {
   111  
   112  	// enforce max length
   113  	if a.maxQueueLength > 0 && a.queue.Length() == a.maxQueueLength {
   114  		var ap *AggregatePriority[V, P]
   115  		if ap.Cmp(aggregatePriority, a.queue.Element(a.maxQueueLength-1)) >= 0 {
   116  			return // aggregatePriority has too low priority to make the cut
   117  		}
   118  		a.queue.DeleteIndex(a.maxQueueLength - 1)
   119  	}
   120  
   121  	// update order: that’s what we do here. We keep order
   122  	a.queue.Insert(aggregatePriority)
   123  }