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}