Skip to main content

d_ary_heap/
lib.rs

1//! # d-ary Heap Priority Queue
2//!
3//! Cross-language implementation of d-ary heap (d-heap) priority queue with O(1) item lookup.
4//! This implementation provides API parity with C++, Zig, and TypeScript versions.
5//!
6//! ## Features
7//!
8//! - **Configurable arity (d)**: Number of children per node (d ≥ 1)
9//! - **Min/Max flexibility**: Supports both min-heap and max-heap behavior via comparators
10//! - **O(1) item lookup**: Internal hash map enables efficient priority updates
11//! - **Efficient operations**: O(log_d n) insert, O(d·log_d n) pop
12//! - **Cross-language API**: Unified method names and behavior across implementations
13//!
14//! ## Cross-Language Consistency
15//!
16//! This Rust implementation maintains API compatibility with:
17//! - **C++**: `TOOLS::PriorityQueue<T>` in `Cpp/PriorityQueue.h`
18//! - **Zig**: `DHeap(T)` in `zig/src/d_heap.zig`
19//! - **TypeScript**: `PriorityQueue<T>` in `TypeScript/src/PriorityQueue.ts`
20//!
21//! All implementations share identical time complexities and method semantics.
22
23use std::collections::HashMap;
24use std::fmt::{Display, Formatter, Result as FmtResult};
25use std::hash::Hash;
26
27/// Error types for d-ary heap operations.
28///
29/// **Cross-language equivalents**:
30/// - Go: `ErrEmptyQueue`, `ErrItemNotFound`
31/// - Zig: `error.DepthMustBePositive`, `error.ItemNotFound`, `error.IndexOutOfBounds`
32/// - TypeScript: Throws Error with messages
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum Error {
35    /// Arity (d) must be >= 1.
36    InvalidArity,
37    /// Item not found in the priority queue.
38    ItemNotFound,
39    /// Index is out of bounds.
40    IndexOutOfBounds,
41    /// Operation requires a non-empty queue.
42    EmptyQueue,
43}
44
45impl Display for Error {
46    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
47        match self {
48            Error::InvalidArity => write!(f, "Heap arity (d) must be >= 1"),
49            Error::ItemNotFound => write!(f, "Item not found"),
50            Error::IndexOutOfBounds => write!(f, "Index out of bounds"),
51            Error::EmptyQueue => write!(f, "Operation called on empty priority queue"),
52        }
53    }
54}
55
56impl std::error::Error for Error {}
57
58/// Type alias for position indices, providing cross-language consistency.
59///
60/// **Cross-language equivalents**:
61/// - C++: `TOOLS::PriorityQueue<T>::Position`
62/// - Zig: `DHeap.Position`
63/// - TypeScript: `Position` type alias
64pub type Position = usize;
65
66/// Trait defining priority comparison for heap ordering.
67///
68/// Implement this trait to define custom priority ordering.
69/// Returns `true` if `a` has higher priority than `b`.
70///
71/// **Cross-language equivalents**:
72/// - C++: `std::less<T>` / `std::greater<T>`
73/// - Zig: `Comparator(T)`
74/// - TypeScript: `Comparator<T>` function
75///
76/// # Examples
77///
78/// ```rust
79/// use d_ary_heap::PriorityCompare;
80///
81/// struct MyComparator;
82/// impl PriorityCompare<i32> for MyComparator {
83///     fn higher_priority(&self, a: &i32, b: &i32) -> bool {
84///         a < b // Min-heap: lower values have higher priority
85///     }
86/// }
87/// ```
88pub trait PriorityCompare<T> {
89    /// Returns `true` if `a` should come before `b` in the heap (has higher priority).
90    fn higher_priority(&self, a: &T, b: &T) -> bool;
91}
92
93/// d-ary heap priority queue with O(1) item lookup.
94///
95/// **Type Parameters**:
96/// - `T`: Item type (must implement `Eq + Hash + Clone`)
97/// - `C`: Comparator implementing `PriorityCompare<T>`
98///
99/// **Cross-language equivalents**:
100/// - C++: `TOOLS::PriorityQueue<T, THash, TComparisonPredicate, TEqual>`
101/// - Zig: `DHeap(T, HashContext(T), Comparator(T))`
102/// - TypeScript: `PriorityQueue<T, K>`
103///
104/// # Examples
105///
106/// ```rust
107/// use d_ary_heap::{PriorityQueue, MinBy};
108///
109/// // Create min-heap with arity 3
110/// let mut heap = PriorityQueue::new(3, MinBy(|x: &i32| *x)).unwrap();
111/// heap.insert(5);
112/// heap.insert(3);
113/// heap.insert(7);
114///
115/// assert_eq!(heap.front(), &3);
116/// assert_eq!(heap.len(), 3);
117/// ```
118///
119/// **Time Complexities** (n = number of items, d = arity):
120/// - `new()`: O(1)
121/// - `insert()`: O(log_d n)
122/// - `front()`/`peek()`: O(1)
123/// - `pop()`: O(d · log_d n)
124/// - `increase_priority()`: O(log_d n)
125/// - `decrease_priority()`: O(d · log_d n)
126/// - `contains()`: O(1)
127/// - `len()`/`is_empty()`/`d()`: O(1)
128#[derive(Debug)]
129pub struct PriorityQueue<T, C>
130where
131    T: Eq + Hash + Clone,
132{
133    container: Vec<T>,
134    positions: HashMap<T, Position>,
135    comparator: C,
136    depth: usize,
137}
138
139impl<T, C> PriorityQueue<T, C>
140where
141    T: Eq + Hash + Clone,
142    C: PriorityCompare<T>,
143{
144    /// Creates a new empty d-ary heap with specified arity and comparator.
145    ///
146    /// # Arguments
147    ///
148    /// * `d` - Arity (number of children per node). Must be ≥ 1.
149    /// * `comparator` - Defines priority order (min-heap or max-heap)
150    ///
151    /// # Errors
152    ///
153    /// Returns `Error::InvalidArity` if `d == 0`.
154    ///
155    /// # Examples
156    ///
157    /// ```rust
158    /// use d_ary_heap::{PriorityQueue, MinBy, MaxBy, Error};
159    ///
160    /// // Binary heap (d=2) with min-heap ordering
161    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
162    ///
163    /// // Quaternary heap (d=4) with max-heap ordering
164    /// let mut heap = PriorityQueue::new(4, MaxBy(|x: &i32| *x)).unwrap();
165    ///
166    /// // Invalid arity returns error
167    /// assert!(PriorityQueue::new(0, MinBy(|x: &i32| *x)).is_err());
168    /// ```
169    ///
170    /// **Cross-language equivalents**:
171    /// - C++: `PriorityQueue<T>(d)`
172    /// - Zig: `DHeap.init(d, comparator, allocator)` (returns `!T`)
173    /// - TypeScript: `new PriorityQueue({d, comparator, keyExtractor})` (throws)
174    /// - Go: `New(d, comparator)` (returns `*T, error`)
175    pub fn new(d: usize, comparator: C) -> Result<Self, Error> {
176        if d == 0 {
177            return Err(Error::InvalidArity);
178        }
179        Ok(Self { container: Vec::new(), positions: HashMap::new(), comparator, depth: d })
180    }
181
182    /// Creates a new d-ary heap with specified arity, inserting the first item.
183    ///
184    /// # Arguments
185    ///
186    /// * `d` - Arity (number of children per node). Must be ≥ 1.
187    /// * `comparator` - Defines priority order
188    /// * `t` - First item to insert
189    ///
190    /// # Errors
191    ///
192    /// Returns `Error::InvalidArity` if `d == 0`.
193    ///
194    /// # Examples
195    ///
196    /// ```rust
197    /// use d_ary_heap::{PriorityQueue, MinBy};
198    ///
199    /// let mut heap = PriorityQueue::with_first(3, MinBy(|x: &i32| *x), 42).unwrap();
200    /// assert_eq!(heap.front(), &42);
201    /// ```
202    ///
203    /// **Cross-language equivalents**:
204    /// - C++: `PriorityQueue(d, t)`
205    /// - Zig: Not directly available (use `init` + `insert`)
206    /// - TypeScript: `PriorityQueue.withFirst(options, item)`
207    /// - Go: `WithFirst(d, comparator, item)` (returns `*T, error`)
208    pub fn with_first(d: usize, comparator: C, t: T) -> Result<Self, Error> {
209        if d == 0 {
210            return Err(Error::InvalidArity);
211        }
212        let mut container = Vec::with_capacity(1);
213        container.push(t.clone());
214        let mut positions = HashMap::with_capacity(1);
215        positions.insert(t, 0);
216        Ok(Self { container, positions, comparator, depth: d })
217    }
218
219    /// Returns the arity (number of children per node) of this heap.
220    ///
221    /// **Time Complexity**: O(1)
222    ///
223    /// # Examples
224    ///
225    /// ```rust
226    /// use d_ary_heap::{PriorityQueue, MinBy};
227    ///
228    /// let heap = PriorityQueue::new(4, MinBy(|x: &i32| *x)).unwrap();
229    /// assert_eq!(heap.d(), 4);
230    /// ```
231    ///
232    /// **Cross-language equivalents**:
233    /// - C++: `d()`
234    /// - Zig: `d()`
235    /// - TypeScript: `d()`
236    #[inline]
237    pub fn d(&self) -> usize { self.depth }
238
239    /// Returns the number of items in the heap.
240    ///
241    /// **Time Complexity**: O(1)
242    ///
243    /// # Examples
244    ///
245    /// ```rust
246    /// use d_ary_heap::{PriorityQueue, MinBy};
247    ///
248    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
249    /// assert_eq!(heap.len(), 0);
250    /// heap.insert(5);
251    /// assert_eq!(heap.len(), 1);
252    /// ```
253    ///
254    /// **Cross-language equivalents**:
255    /// - C++: `len()`
256    /// - Zig: `len()`
257    /// - TypeScript: `len()`
258    #[inline]
259    pub fn len(&self) -> usize { self.container.len() }
260
261    /// Returns `true` if the heap is empty.
262    ///
263    /// **Time Complexity**: O(1)
264    ///
265    /// # Examples
266    ///
267    /// ```rust
268    /// use d_ary_heap::{PriorityQueue, MinBy};
269    ///
270    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
271    /// assert!(heap.is_empty());
272    /// heap.insert(5);
273    /// assert!(!heap.is_empty());
274    /// ```
275    ///
276    /// **Cross-language equivalents**:
277    /// - C++: `is_empty()`
278    /// - Zig: `isEmpty()`
279    /// - TypeScript: `isEmpty()`
280    #[inline]
281    pub fn is_empty(&self) -> bool { self.container.is_empty() }
282
283    /// Checks if an item exists in the heap by identity (O(1) lookup).
284    ///
285    /// **Time Complexity**: O(1)
286    ///
287    /// # Examples
288    ///
289    /// ```rust
290    /// use d_ary_heap::{PriorityQueue, MinBy};
291    ///
292    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
293    /// heap.insert(5);
294    /// assert!(heap.contains(&5));
295    /// assert!(!heap.contains(&10));
296    /// ```
297    ///
298    /// **Cross-language equivalents**:
299    /// - C++: `contains(item)`
300    /// - Zig: `contains(item)`
301    /// - TypeScript: `contains(item)`
302    #[inline]
303    pub fn contains(&self, item: &T) -> bool { self.positions.contains_key(item) }
304
305    /// Returns the position (index) of an item in the heap, or `None` if not found.
306    ///
307    /// **Time Complexity**: O(1)
308    ///
309    /// # Examples
310    ///
311    /// ```rust
312    /// use d_ary_heap::{PriorityQueue, MinBy};
313    ///
314    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
315    /// heap.insert(5);
316    /// heap.insert(3);
317    ///
318    /// // Root item (highest priority) is at position 0
319    /// assert_eq!(heap.get_position(&3), Some(0));
320    /// assert!(heap.get_position(&5).is_some());
321    /// assert_eq!(heap.get_position(&99), None);
322    /// ```
323    ///
324    /// **Cross-language equivalents**:
325    /// - C++: `get_position(item)`
326    /// - Zig: `getPosition(item)`
327    /// - TypeScript: `getPosition(item)`
328    /// - Go: `GetPosition(item)`
329    #[inline]
330    pub fn get_position(&self, item: &T) -> Option<Position> {
331        self.positions.get(item).copied()
332    }
333
334    /// Clears all items from the heap, optionally changing the arity.
335    ///
336    /// **Time Complexity**: O(1)
337    ///
338    /// # Errors
339    ///
340    /// Returns `Error::InvalidArity` if `d` is `Some(0)`.
341    ///
342    /// # Examples
343    ///
344    /// ```rust
345    /// use d_ary_heap::{PriorityQueue, MinBy};
346    ///
347    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
348    /// heap.insert(5);
349    /// heap.insert(3);
350    ///
351    /// heap.clear(None).unwrap();
352    /// assert!(heap.is_empty());
353    /// assert_eq!(heap.d(), 2); // Arity preserved
354    ///
355    /// heap.clear(Some(4)).unwrap(); // Change arity to 4
356    /// assert_eq!(heap.d(), 4);
357    ///
358    /// // Invalid arity returns error
359    /// assert!(heap.clear(Some(0)).is_err());
360    /// ```
361    ///
362    /// **Cross-language equivalents**:
363    /// - C++: `clear(opt_d)`
364    /// - Zig: `clear(new_depth?)` (returns `!void`)
365    /// - TypeScript: `clear(newD?)` (throws on invalid)
366    /// - Go: `Clear(d)` (returns `error`)
367    pub fn clear(&mut self, d: Option<usize>) -> Result<(), Error> {
368        if let Some(new_d) = d {
369            if new_d == 0 {
370                return Err(Error::InvalidArity);
371            }
372            self.depth = new_d;
373        }
374        self.container.clear();
375        self.positions.clear();
376        Ok(())
377    }
378
379    /// Returns a reference to the highest-priority item.
380    ///
381    /// **Time Complexity**: O(1)
382    ///
383    /// # Panics
384    ///
385    /// Panics if the heap is empty.
386    ///
387    /// # Examples
388    ///
389    /// ```rust
390    /// use d_ary_heap::{PriorityQueue, MinBy};
391    ///
392    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
393    /// heap.insert(5);
394    /// heap.insert(3);
395    ///
396    /// assert_eq!(heap.front(), &3);
397    /// ```
398    ///
399    /// **Cross-language equivalents**:
400    /// - C++: `front()` (UB if empty)
401    /// - Zig: `front()` (returns `null` if empty)
402    /// - TypeScript: `front()` (throws if empty)
403    ///
404    /// **Safe alternative**: Use `peek()` instead.
405    pub fn front(&self) -> &T {
406        self.container.first().expect("front() called on empty priority queue")
407    }
408
409    /// Safe alternative to `front()` that returns `None` if empty.
410    ///
411    /// **Time Complexity**: O(1)
412    ///
413    /// # Examples
414    ///
415    /// ```rust
416    /// use d_ary_heap::{PriorityQueue, MinBy};
417    ///
418    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
419    /// assert_eq!(heap.peek(), None);
420    ///
421    /// heap.insert(5);
422    /// assert_eq!(heap.peek(), Some(&5));
423    /// ```
424    ///
425    /// **Cross-language equivalents**:
426    /// - C++: `peek()`
427    /// - Zig: `front()` / `peek()`
428    /// - TypeScript: `peek()`
429    /// - Go: `Peek()`
430    pub fn peek(&self) -> Option<&T> { self.container.first() }
431
432    /// Inserts an item into the heap according to its priority.
433    ///
434    /// **Time Complexity**: O(log_d n)
435    ///
436    /// # Examples
437    ///
438    /// ```rust
439    /// use d_ary_heap::{PriorityQueue, MinBy};
440    ///
441    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
442    /// heap.insert(5);
443    /// heap.insert(3);
444    /// heap.insert(7);
445    ///
446    /// assert_eq!(heap.front(), &3);
447    /// ```
448    ///
449    /// **Cross-language equivalents**:
450    /// - C++: `insert(item)`
451    /// - Zig: `insert(item)`
452    /// - TypeScript: `insert(item)`
453    pub fn insert(&mut self, t: T) {
454        self.container.push(t.clone());
455        let i = self.container.len() - 1;
456        self.positions.insert(t, i);
457        self.move_up(i);
458    }
459
460    /// Increases priority of item at specified index (moves up if needed).
461    ///
462    /// **Time Complexity**: O(log_d n)
463    ///
464    /// # Errors
465    ///
466    /// Returns `Error::IndexOutOfBounds` if index is out of bounds.
467    ///
468    /// # Examples
469    ///
470    /// ```rust
471    /// use d_ary_heap::{PriorityQueue, MinBy, Error};
472    ///
473    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
474    /// heap.insert(10);
475    /// heap.insert(5);
476    ///
477    /// // Increase priority of item at index 1
478    /// heap.increase_priority_by_index(1).unwrap();
479    ///
480    /// // Error on out of bounds
481    /// assert_eq!(heap.increase_priority_by_index(99), Err(Error::IndexOutOfBounds));
482    /// ```
483    ///
484    /// **Cross-language equivalents**:
485    /// - C++: `increase_priority(position)`
486    /// - Zig: `increasePriorityByIndex(index)` (returns `!void`)
487    /// - TypeScript: `increasePriorityByIndex(index)` (throws)
488    /// - Go: `IncreasePriorityByIndex(index)` (returns `error`)
489    pub fn increase_priority_by_index(&mut self, i: usize) -> Result<(), Error> {
490        if i >= self.container.len() {
491            return Err(Error::IndexOutOfBounds);
492        }
493        self.move_up(i);
494        Ok(())
495    }
496
497    /// Decreases priority of item at specified index (moves down if needed).
498    ///
499    /// **Time Complexity**: O(d · log_d n)
500    ///
501    /// # Errors
502    ///
503    /// Returns `Error::IndexOutOfBounds` if index is out of bounds.
504    ///
505    /// # Examples
506    ///
507    /// ```rust
508    /// use d_ary_heap::{PriorityQueue, MinBy, Error};
509    ///
510    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
511    /// heap.insert(10);
512    /// heap.insert(5);
513    ///
514    /// // Decrease priority of item at index 0 (root)
515    /// heap.decrease_priority_by_index(0).unwrap();
516    ///
517    /// // Error on out of bounds
518    /// assert_eq!(heap.decrease_priority_by_index(99), Err(Error::IndexOutOfBounds));
519    /// ```
520    ///
521    /// **Cross-language equivalents**:
522    /// - C++: `decrease_priority_by_index(index)`
523    /// - Zig: `decreasePriorityByIndex(index)` (returns `!void`)
524    /// - TypeScript: `decreasePriorityByIndex(index)` (throws)
525    /// - Go: `DecreasePriorityByIndex(index)` (returns `error`)
526    pub fn decrease_priority_by_index(&mut self, i: usize) -> Result<(), Error> {
527        if i >= self.container.len() {
528            return Err(Error::IndexOutOfBounds);
529        }
530        self.move_down(i);
531        Ok(())
532    }
533
534    /// Updates priority of item at specified index (moves in correct direction).
535    ///
536    /// Use this when you don't know whether the item's priority increased or decreased.
537    /// It will check both directions to maintain heap property.
538    ///
539    /// **Time Complexity**: O((d+1) · log_d n) worst case
540    ///
541    /// # Errors
542    ///
543    /// Returns `Error::IndexOutOfBounds` if index is out of bounds.
544    ///
545    /// # Examples
546    ///
547    /// ```rust
548    /// use d_ary_heap::{PriorityQueue, MinBy, Error};
549    ///
550    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
551    /// heap.insert(10);
552    /// heap.insert(5);
553    ///
554    /// // Update priority at index - direction is determined automatically
555    /// heap.update_priority_by_index(0).unwrap();
556    ///
557    /// // Error on out of bounds
558    /// assert_eq!(heap.update_priority_by_index(99), Err(Error::IndexOutOfBounds));
559    /// ```
560    ///
561    /// **Cross-language equivalents**:
562    /// - C++: `update_priority_by_index(index)`
563    /// - Zig: Not available
564    /// - TypeScript: Not available
565    /// - Go: Not available
566    pub fn update_priority_by_index(&mut self, i: usize) -> Result<(), Error> {
567        if i >= self.container.len() {
568            return Err(Error::IndexOutOfBounds);
569        }
570        self.move_up(i);
571        self.move_down(i);
572        Ok(())
573    }
574
575    /// Increases priority of existing item (moves toward root if needed).
576    ///
577    /// **Time Complexity**: O(log_d n)
578    ///
579    /// # Errors
580    ///
581    /// Returns `Error::ItemNotFound` if item is not in the heap.
582    ///
583    /// # Examples
584    ///
585    /// ```rust
586    /// use d_ary_heap::{PriorityQueue, MinBy, Error};
587    ///
588    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
589    /// heap.insert(10);
590    /// heap.insert(5);
591    ///
592    /// // The heap maintains proper ordering
593    /// assert_eq!(heap.front(), &5);
594    /// assert!(heap.contains(&10));
595    ///
596    /// // Error on non-existent item
597    /// assert_eq!(heap.increase_priority(&99), Err(Error::ItemNotFound));
598    /// ```
599    ///
600    /// **Note**: For min-heap, "increase priority" means decreasing the priority value.
601    /// For max-heap, "increase priority" means increasing the priority value.
602    ///
603    /// **Cross-language equivalents**:
604    /// - C++: `increase_priority(item)`
605    /// - Zig: `increasePriority(item)` (returns `!void`)
606    /// - TypeScript: `increasePriority(item)` (throws)
607    /// - Go: `IncreasePriority(item)` (returns `error`)
608    pub fn increase_priority(&mut self, updated_item: &T) -> Result<(), Error> {
609        let &i = self.positions
610            .get(updated_item)
611            .ok_or(Error::ItemNotFound)?;
612
613        // Update positions: remove old key and insert the new (updated) item.
614        // Since Hash/Eq are based on identity (not priority), updated_item can be used
615        // directly to remove the old entry — no need to clone the old item.
616        self.positions.remove(updated_item);
617        self.positions.insert(updated_item.clone(), i);
618        self.container[i] = updated_item.clone();
619
620        // Move up after priority increase
621        self.move_up(i);
622        Ok(())
623    }
624
625    /// Decreases priority of existing item (moves toward leaves if needed).
626    ///
627    /// **Important**: Only call this when you know the item's priority has decreased
628    /// (become less important). For min-heap, this means the value increased.
629    /// For max-heap, this means the value decreased.
630    /// If unsure of the direction, use `update_priority()` instead.
631    ///
632    /// **Time Complexity**: O(d · log_d n)
633    ///
634    /// # Errors
635    ///
636    /// Returns `Error::ItemNotFound` if item is not in the heap.
637    ///
638    /// # Examples
639    ///
640    /// ```rust
641    /// use d_ary_heap::{PriorityQueue, MinBy, Error};
642    ///
643    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
644    /// heap.insert(5);
645    /// heap.insert(10);
646    ///
647    /// // The heap maintains proper ordering
648    /// assert_eq!(heap.front(), &5);
649    /// assert!(heap.contains(&10));
650    ///
651    /// // Error on non-existent item
652    /// assert_eq!(heap.decrease_priority(&99), Err(Error::ItemNotFound));
653    /// ```
654    ///
655    /// **Note**: For min-heap, "decrease priority" means increasing the priority value.
656    /// For max-heap, "decrease priority" means decreasing the priority value.
657    ///
658    /// **Cross-language equivalents**:
659    /// - C++: `decrease_priority(item)`
660    /// - Zig: `decreasePriority(item)` (returns `!void`)
661    /// - TypeScript: `decreasePriority(item)` (throws)
662    /// - Go: `DecreasePriority(item)` (returns `error`)
663    pub fn decrease_priority(&mut self, updated_item: &T) -> Result<(), Error> {
664        let &i = self.positions
665            .get(updated_item)
666            .ok_or(Error::ItemNotFound)?;
667
668        // Update positions: remove old key and insert the new (updated) item.
669        // Since Hash/Eq are based on identity (not priority), updated_item can be used
670        // directly to remove the old entry — no need to clone the old item.
671        self.positions.remove(updated_item);
672        self.positions.insert(updated_item.clone(), i);
673        self.container[i] = updated_item.clone();
674
675        // Move down after priority decrease (item became less important)
676        self.move_down(i);
677        Ok(())
678    }
679
680    /// Updates priority of existing item, moving it in the correct direction.
681    ///
682    /// Use this when you don't know whether the item's priority increased or decreased.
683    /// It will check both directions to maintain heap property.
684    ///
685    /// **Time Complexity**: O((d+1) · log_d n) worst case
686    ///
687    /// # Errors
688    ///
689    /// Returns `Error::ItemNotFound` if item is not in the heap.
690    ///
691    /// # Examples
692    ///
693    /// ```rust
694    /// use d_ary_heap::{PriorityQueue, MinBy, Error};
695    ///
696    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
697    /// heap.insert(5);
698    /// heap.insert(10);
699    ///
700    /// // Update priority - direction is determined automatically
701    /// heap.update_priority(&3).unwrap_or(()); // Would need matching item by identity
702    ///
703    /// // Error on non-existent item
704    /// assert_eq!(heap.update_priority(&99), Err(Error::ItemNotFound));
705    /// ```
706    ///
707    /// **Cross-language equivalents**:
708    /// - C++: `update_priority(item)` / `try_update_priority(item)`
709    /// - Zig: `updatePriority(item)` (returns `!void`)
710    /// - TypeScript: `updatePriority(item)` (throws)
711    /// - Go: `UpdatePriority(item)` (returns `error`)
712    pub fn update_priority(&mut self, updated_item: &T) -> Result<(), Error> {
713        let &i = self.positions
714            .get(updated_item)
715            .ok_or(Error::ItemNotFound)?;
716
717        // Update positions: remove old key and insert the new (updated) item.
718        self.positions.remove(updated_item);
719        self.positions.insert(updated_item.clone(), i);
720        self.container[i] = updated_item.clone();
721
722        // Check both directions since we don't know if priority increased or decreased
723        self.move_up(i);
724        self.move_down(i);
725        Ok(())
726    }
727
728    /// Removes and returns the highest-priority item from the heap.
729    ///
730    /// Returns `None` if the heap is empty.
731    ///
732    /// **Time Complexity**: O(d · log_d n)
733    ///
734    /// # Examples
735    ///
736    /// ```rust
737    /// use d_ary_heap::{PriorityQueue, MinBy};
738    ///
739    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
740    /// heap.insert(5);
741    /// heap.insert(3);
742    /// heap.insert(7);
743    ///
744    /// assert_eq!(heap.pop(), Some(3));
745    /// assert_eq!(heap.pop(), Some(5));
746    /// assert_eq!(heap.pop(), Some(7));
747    /// assert_eq!(heap.pop(), None);
748    /// ```
749    ///
750    /// **Cross-language equivalents**:
751    /// - C++: `pop()`
752    /// - Zig: `pop()` (returns `?T`)
753    /// - TypeScript: `pop()` (returns `T | undefined`)
754    /// - Go: `Pop()` (returns `T, bool`)
755    pub fn pop(&mut self) -> Option<T> {
756        if self.container.is_empty() { return None; }
757        let last = self.container.len() - 1;
758        self.swap(0, last);
759        let removed = self.container.pop().unwrap();
760        self.positions.remove(&removed);
761        if !self.container.is_empty() {
762            self.move_down(0);
763        }
764        Some(removed)
765    }
766
767    /// Returns a copy of the heap contents as a Vec.
768    ///
769    /// The root element (highest priority) is at index 0. The internal heap
770    /// structure is preserved—this is NOT a sorted array.
771    ///
772    /// **Time Complexity**: O(n)
773    ///
774    /// # Examples
775    ///
776    /// ```rust
777    /// use d_ary_heap::{PriorityQueue, MinBy};
778    ///
779    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
780    /// heap.insert(5);
781    /// heap.insert(3);
782    /// heap.insert(7);
783    ///
784    /// let arr = heap.to_array();
785    /// assert_eq!(arr.len(), 3);
786    /// assert_eq!(arr[0], 3); // Root is highest priority (min value)
787    /// ```
788    ///
789    /// **Cross-language equivalents**:
790    /// - C++: `to_array()`
791    /// - Zig: `toArray()`
792    /// - TypeScript: `toArray()`
793    /// - Go: `ToArray()`
794    pub fn to_array(&self) -> Vec<T> {
795        self.container.clone()
796    }
797
798    /// Inserts multiple items into the heap using Floyd's heapify algorithm.
799    ///
800    /// This is more efficient than inserting items one at a time when adding
801    /// many items at once: O(n) vs O(n log n).
802    ///
803    /// **Time Complexity**: O(n) where n is the number of items being inserted
804    ///
805    /// # Examples
806    ///
807    /// ```rust
808    /// use d_ary_heap::{PriorityQueue, MinBy};
809    ///
810    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
811    /// heap.insert_many(vec![5, 3, 7, 1, 9]);
812    ///
813    /// assert_eq!(heap.len(), 5);
814    /// assert_eq!(heap.front(), &1);
815    /// ```
816    ///
817    /// **Cross-language equivalents**:
818    /// - C++: `insert_many(items)`
819    /// - Zig: `insertMany(items)`
820    /// - TypeScript: `insertMany(items)`
821    /// - Go: `InsertMany(items)`
822    pub fn insert_many(&mut self, items: impl IntoIterator<Item = T>) {
823        let items: Vec<T> = items.into_iter().collect();
824        if items.is_empty() {
825            return;
826        }
827
828        // Add all items to container and positions
829        let start_idx = self.container.len();
830        for (i, item) in items.into_iter().enumerate() {
831            self.positions.insert(item.clone(), start_idx + i);
832            self.container.push(item);
833        }
834
835        // Floyd's heapify: sift down from the last non-leaf to the root
836        // This achieves O(n) instead of O(n log n) for individual inserts
837        if self.container.len() > 1 {
838            let last_non_leaf = (self.container.len() - 2) / self.depth;
839            for i in (0..=last_non_leaf).rev() {
840                self.move_down(i);
841            }
842        }
843    }
844
845    /// Removes and returns multiple highest-priority items from the heap.
846    ///
847    /// Returns up to `count` items in priority order (highest priority first).
848    /// If the heap has fewer items than requested, returns all available items.
849    ///
850    /// **Time Complexity**: O(count · d · log_d n)
851    ///
852    /// # Examples
853    ///
854    /// ```rust
855    /// use d_ary_heap::{PriorityQueue, MinBy};
856    ///
857    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
858    /// heap.insert_many(vec![5, 3, 7, 1, 9]);
859    ///
860    /// let items = heap.pop_many(3);
861    /// assert_eq!(items, vec![1, 3, 5]);
862    /// assert_eq!(heap.len(), 2);
863    ///
864    /// // Requesting more than available returns all remaining
865    /// let remaining = heap.pop_many(10);
866    /// assert_eq!(remaining, vec![7, 9]);
867    /// assert!(heap.is_empty());
868    /// ```
869    ///
870    /// **Cross-language equivalents**:
871    /// - C++: `pop_many(count)`
872    /// - Zig: `popMany(count)`
873    /// - TypeScript: `popMany(count)`
874    /// - Go: `PopMany(count)`
875    pub fn pop_many(&mut self, count: usize) -> Vec<T> {
876        let actual_count = count.min(self.container.len());
877        let mut result = Vec::with_capacity(actual_count);
878        for _ in 0..actual_count {
879            if let Some(item) = self.pop() {
880                result.push(item);
881            }
882        }
883        result
884    }
885
886    #[inline]
887    fn parent(&self, i: usize) -> usize {
888        assert!(i > 0 && self.depth > 0);
889        (i - 1) / self.depth
890    }
891
892    fn best_child_position(&self, i: usize) -> usize {
893        let n = self.container.len();
894        let left = i * self.depth + 1;
895        if left >= n { return left; }
896        let right = ((i + 1) * self.depth).min(n - 1);
897        let mut best = left;
898        for p in (left + 1)..=right {
899            if self.comparator.higher_priority(&self.container[p], &self.container[best]) {
900                best = p;
901            }
902        }
903        best
904    }
905
906    fn swap(&mut self, i: usize, j: usize) {
907        if i == j { return; }
908        self.container.swap(i, j);
909        let ti = self.container[i].clone();
910        let tj = self.container[j].clone();
911        self.positions.insert(ti, i);
912        self.positions.insert(tj, j);
913    }
914
915    fn move_up(&mut self, mut i: usize) {
916        while i > 0 {
917            let p = self.parent(i);
918            if self.comparator.higher_priority(&self.container[i], &self.container[p]) {
919                self.swap(i, p);
920                i = p;
921            } else {
922                break;
923            }
924        }
925    }
926
927    fn move_down(&mut self, mut i: usize) {
928        let n = self.container.len();
929        loop {
930            let first_child = i * self.depth + 1;
931            if first_child >= n { break; }
932            let best = self.best_child_position(i);
933            if self.comparator.higher_priority(&self.container[best], &self.container[i]) {
934                self.swap(i, best);
935                i = best;
936            } else {
937                break;
938            }
939        }
940    }
941}
942
943/// Display implementation for PriorityQueue.
944///
945/// Renders the queue contents in array layout: `{item1, item2, ...}`.
946///
947/// **Cross-language equivalents**:
948/// - C++: `put(std::ostream&)`
949/// - Zig: `toString()`
950/// - TypeScript: `toString()`
951///
952/// # Examples
953///
954/// ```rust
955/// use d_ary_heap::{PriorityQueue, MinBy};
956///
957/// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
958/// heap.insert(5);
959/// heap.insert(3);
960///
961/// // Uses Display trait
962/// println!("{}", heap); // Output: {3, 5}
963/// ```
964impl<T, C> Display for PriorityQueue<T, C>
965where
966    T: Eq + Hash + Clone + Display,
967{
968    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
969        write!(f, "{{")?;
970        for (idx, item) in self.container.iter().enumerate() {
971            if idx > 0 { write!(f, ", ")?; }
972            write!(f, "{}", item)?;
973        }
974        write!(f, "}}")
975    }
976}
977
978/// Additional methods available when T implements Display.
979impl<T, C> PriorityQueue<T, C>
980where
981    T: Eq + Hash + Clone + Display,
982{
983    /// Produces a string representation of the heap contents.
984    ///
985    /// **Time Complexity**: O(n)
986    ///
987    /// This method provides explicit string conversion for API parity with
988    /// C++, Zig, and TypeScript implementations.
989    ///
990    /// # Examples
991    ///
992    /// ```rust
993    /// use d_ary_heap::{PriorityQueue, MinBy};
994    ///
995    /// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
996    /// heap.insert(5);
997    /// heap.insert(3);
998    ///
999    /// assert_eq!(heap.to_string(), "{3, 5}");
1000    /// ```
1001    ///
1002    /// **Cross-language equivalents**:
1003    /// - C++: `to_string()`
1004    /// - Zig: `toString()` / `to_string()`
1005    /// - TypeScript: `toString()` / `to_string()`
1006    #[inline]
1007    pub fn to_string(&self) -> String {
1008        format!("{}", self)
1009    }
1010}
1011
1012/// Convenience comparator for min-heap behavior.
1013///
1014/// Creates a min-heap where items with smaller key values have higher priority.
1015///
1016/// **Cross-language equivalents**:
1017/// - C++: `std::less<T>`
1018/// - Zig: `MinBy` comparator
1019/// - TypeScript: `minBy` helper
1020///
1021/// # Examples
1022///
1023/// ```rust
1024/// use d_ary_heap::{PriorityQueue, MinBy};
1025///
1026/// // Min-heap by integer value
1027/// let mut heap = PriorityQueue::new(2, MinBy(|x: &i32| *x)).unwrap();
1028/// heap.insert(5);
1029/// heap.insert(3);
1030/// assert_eq!(heap.front(), &3);
1031///
1032/// // Min-heap by struct field
1033/// #[derive(PartialEq, Eq, Hash, Clone)]
1034/// struct Task { priority: i32 }
1035/// let mut heap = PriorityQueue::new(3, MinBy(|t: &Task| t.priority)).unwrap();
1036/// ```
1037pub struct MinBy<F>(pub F);
1038impl<T, F, K> PriorityCompare<T> for MinBy<F>
1039where
1040    F: Fn(&T) -> K,
1041    K: Ord,
1042{
1043    #[inline]
1044    fn higher_priority(&self, a: &T, b: &T) -> bool { (self.0)(a) < (self.0)(b) }
1045}
1046
1047/// Convenience comparator for max-heap behavior.
1048///
1049/// Creates a max-heap where items with larger key values have higher priority.
1050///
1051/// **Cross-language equivalents**:
1052/// - C++: `std::greater<T>`
1053/// - Zig: `MaxBy` comparator
1054/// - TypeScript: `maxBy` helper
1055///
1056/// # Examples
1057///
1058/// ```rust
1059/// use d_ary_heap::{PriorityQueue, MaxBy};
1060///
1061/// // Max-heap by integer value
1062/// let mut heap = PriorityQueue::new(2, MaxBy(|x: &i32| *x)).unwrap();
1063/// heap.insert(5);
1064/// heap.insert(3);
1065/// assert_eq!(heap.front(), &5);
1066///
1067/// // Max-heap by struct field
1068/// #[derive(PartialEq, Eq, Hash, Clone)]
1069/// struct Task { priority: i32 }
1070/// let mut heap = PriorityQueue::new(3, MaxBy(|t: &Task| t.priority)).unwrap();
1071/// ```
1072pub struct MaxBy<F>(pub F);
1073impl<T, F, K> PriorityCompare<T> for MaxBy<F>
1074where
1075    F: Fn(&T) -> K,
1076    K: Ord,
1077{
1078    #[inline]
1079    fn higher_priority(&self, a: &T, b: &T) -> bool { (self.0)(a) > (self.0)(b) }
1080}