github.com/haraldrudell/parl@v0.4.176/pqs/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  // Ranking is a pointer-identity-to-value map of updatable values traversable by rank.
     7  // Ranking implements [parl.Ranking][V comparable, R constraints.Ordered].
     8  package pqs
     9  
    10  import (
    11  	"github.com/haraldrudell/parl"
    12  	"github.com/haraldrudell/parl/parli"
    13  	"github.com/haraldrudell/parl/perrors"
    14  	"github.com/haraldrudell/parl/pslices"
    15  	"golang.org/x/exp/constraints"
    16  )
    17  
    18  // PriorityQueue is a pointer-identity-to-value map of updatable values traversable by rank.
    19  // PriorityQueue implements [parl.PriorityQueue][V comparable, R constraints.Ordered].
    20  //   - V is a value reference composite type that is comparable, ie. not slice map function.
    21  //     Preferrably, V is interface or pointer to struct type.
    22  //   - R is an ordered type such as int floating-point string, used to rank the V values
    23  //   - values are added or updated using AddOrUpdate method distinguished by
    24  //     (computer science) identity
    25  //   - if the same comparable value V is added again, that value is re-ranked
    26  //   - rank R is computed from a value V using the ranker function.
    27  //     The ranker function may be examining field values of a struct
    28  //   - values can have the same rank. If they do, equal rank is provided in insertion order
    29  type PriorityQueue[V any, P constraints.Ordered] struct {
    30  	// priorityFunc is the function computing priority for a value-pointer
    31  	priorityFunc func(value *V) (priority P)
    32  	// queue is a list of queue nodes ordered by descending priority
    33  	queue parli.Ordered[*AssignedPriority[V, P]]
    34  	// m is a map providing O(1) access to ranking nodes by value-pointer
    35  	m map[*V]*AssignedPriority[V, P]
    36  }
    37  
    38  // NewPriorityQueue returns a map of updatable values traversable by rank
    39  func NewPriorityQueue[V any, P constraints.Ordered](
    40  	priorityFunc func(value *V) (priority P),
    41  ) (priorityQueue parl.PriorityQueue[V, P]) {
    42  	if priorityFunc == nil {
    43  		perrors.NewPF("ranker cannot be nil")
    44  	}
    45  	r := PriorityQueue[V, P]{
    46  		priorityFunc: priorityFunc,
    47  		m:            map[*V]*AssignedPriority[V, P]{},
    48  	}
    49  	r.queue = pslices.NewOrderedAny(r.comparatorFunc)
    50  	return &r
    51  }
    52  
    53  // AddOrUpdate adds a new value to the ranking or updates the ranking of a value
    54  // that has changed.
    55  func (pq *PriorityQueue[V, P]) AddOrUpdate(valuep *V) {
    56  
    57  	// obtain updated priority
    58  	priority := pq.priorityFunc(valuep)
    59  
    60  	var assignedPriority *AssignedPriority[V, P]
    61  	var ok bool
    62  	if assignedPriority, ok = pq.m[valuep]; !ok {
    63  
    64  		// new value case
    65  		assignedPriority = NewAssignedPriority(priority, pq.queue.Length(), valuep)
    66  		pq.m[valuep] = assignedPriority
    67  	} else {
    68  
    69  		// node update case
    70  		pq.queue.Delete(assignedPriority)
    71  		assignedPriority.SetPriority(priority)
    72  	}
    73  
    74  	// update order: that’s what we do here. We keep order
    75  	pq.queue.Insert(assignedPriority)
    76  }
    77  
    78  // List returns the first n or default all values by rank
    79  func (pq *PriorityQueue[V, P]) List(n ...int) (valueQueue []*V) {
    80  
    81  	// get number of items n0
    82  	var n0 int
    83  	length := pq.queue.Length()
    84  	if len(n) > 0 {
    85  		n0 = n[0]
    86  	}
    87  	if n0 < 1 || n0 > length {
    88  		n0 = length
    89  	}
    90  
    91  	// build list
    92  	valueQueue = make([]*V, n0)
    93  	assignedPriorityQueue := pq.queue.List()
    94  	for i := 0; i < n0; i++ {
    95  		valueQueue[i] = assignedPriorityQueue[i].Value
    96  	}
    97  	return
    98  }
    99  
   100  // comparatorFunc is provided to pslices.NewOrderedAny based on AssignedPriority.Cmp
   101  func (pq *PriorityQueue[V, P]) comparatorFunc(a, b *AssignedPriority[V, P]) (result int) {
   102  	return a.Cmp(b)
   103  }