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 }