philiprehberger-priority_queue 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 96eac91320e11af568cae20213fb6ea3bc6a16562ec2f8359539f69ce8d551f1
4
+ data.tar.gz: 9757f9de2dae44612d2fbc90abe916dbfa21b74657b8e19ca708ba43f5b90e39
5
+ SHA512:
6
+ metadata.gz: b8043b9740dacab51fe67334cc21d06614eb7c0351181fd0ff8558f4cc10cda92cbfe74b68db3c40bdc104916cb2f9df26151065e7d022e684a389d1f608365b
7
+ data.tar.gz: 00a4ebadb39f16879e25b81fae93f307a00337791271a5ca9b393726a24c32d5d41cb12181a16e5a84fd08d1c2a11e54980de39322162d23d5f14b2c57c5060c
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to this gem will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this gem adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-03-21
11
+
12
+ ### Added
13
+ - Initial release
14
+ - Binary heap `Queue` with min-heap (default) and max-heap modes
15
+ - `push(item, priority:)` and `pop` with O(log n) performance
16
+ - `peek` to view the highest-priority item without removal
17
+ - `change_priority(item, new_priority)` to update priorities in-place
18
+ - `to_a` for sorted array extraction
19
+ - `merge(other)` to combine two priority queues
20
+ - Custom comparator support via block
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 philiprehberger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # philiprehberger-priority_queue
2
+
3
+ [![Tests](https://github.com/philiprehberger/rb-priority-queue/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/rb-priority-queue/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/philiprehberger-priority_queue.svg)](https://rubygems.org/gems/philiprehberger-priority_queue)
5
+ [![License](https://img.shields.io/github/license/philiprehberger/rb-priority-queue)](LICENSE)
6
+
7
+ Binary heap priority queue with min/max modes and custom comparators
8
+
9
+ ## Requirements
10
+
11
+ - Ruby >= 3.1
12
+
13
+ ## Installation
14
+
15
+ Add to your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'philiprehberger-priority_queue'
19
+ ```
20
+
21
+ Or install directly:
22
+
23
+ ```bash
24
+ gem install philiprehberger-priority_queue
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```ruby
30
+ require 'philiprehberger/priority_queue'
31
+
32
+ pq = Philiprehberger::PriorityQueue::Queue.new
33
+ pq.push('low', priority: 1)
34
+ pq.push('high', priority: 10)
35
+ pq.push('mid', priority: 5)
36
+
37
+ pq.pop # => "low"
38
+ pq.pop # => "mid"
39
+ pq.pop # => "high"
40
+ ```
41
+
42
+ ### Max-Heap
43
+
44
+ ```ruby
45
+ pq = Philiprehberger::PriorityQueue::Queue.new(mode: :max)
46
+ pq.push('low', priority: 1)
47
+ pq.push('high', priority: 10)
48
+ pq.pop # => "high"
49
+ ```
50
+
51
+ ### Custom Comparator
52
+
53
+ ```ruby
54
+ pq = Philiprehberger::PriorityQueue::Queue.new { |a, b| a[:priority] <=> b[:priority] }
55
+ pq.push('task', priority: 5)
56
+ ```
57
+
58
+ ### Updating Priorities
59
+
60
+ ```ruby
61
+ pq = Philiprehberger::PriorityQueue::Queue.new
62
+ pq.push('task_a', priority: 10)
63
+ pq.push('task_b', priority: 5)
64
+ pq.change_priority('task_a', 1)
65
+ pq.pop # => "task_a" (now has lowest priority)
66
+ ```
67
+
68
+ ### Merging Queues
69
+
70
+ ```ruby
71
+ pq1 = Philiprehberger::PriorityQueue::Queue.new
72
+ pq1.push('a', priority: 1)
73
+
74
+ pq2 = Philiprehberger::PriorityQueue::Queue.new
75
+ pq2.push('b', priority: 2)
76
+
77
+ pq1.merge(pq2)
78
+ pq1.size # => 2
79
+ ```
80
+
81
+ ## API
82
+
83
+ ### `Philiprehberger::PriorityQueue::Queue`
84
+
85
+ | Method | Description |
86
+ |--------|-------------|
87
+ | `.new(mode: :min)` | Create a min-heap (default) or max-heap |
88
+ | `.new { \|a, b\| ... }` | Create with a custom comparator block |
89
+ | `#push(item, priority:)` | Add an item with a priority |
90
+ | `#pop` | Remove and return the highest-priority item |
91
+ | `#peek` | View the highest-priority item without removing it |
92
+ | `#size` | Number of items in the queue |
93
+ | `#empty?` | Whether the queue is empty |
94
+ | `#change_priority(item, new_priority)` | Update an item's priority |
95
+ | `#to_a` | Return all items in priority order |
96
+ | `#merge(other)` | Merge another queue into this one |
97
+
98
+ ## Development
99
+
100
+ ```bash
101
+ bundle install
102
+ bundle exec rspec # Run tests
103
+ bundle exec rubocop # Check code style
104
+ ```
105
+
106
+ ## License
107
+
108
+ MIT
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Philiprehberger
4
+ module PriorityQueue
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'priority_queue/version'
4
+
5
+ module Philiprehberger
6
+ module PriorityQueue
7
+ class Error < StandardError; end
8
+
9
+ # A binary heap priority queue supporting min-heap, max-heap, and custom comparators
10
+ #
11
+ # @example Min-heap (default)
12
+ # pq = Queue.new
13
+ # pq.push('low', priority: 1)
14
+ # pq.push('high', priority: 10)
15
+ # pq.pop # => "low"
16
+ #
17
+ # @example Max-heap
18
+ # pq = Queue.new(mode: :max)
19
+ # pq.push('low', priority: 1)
20
+ # pq.push('high', priority: 10)
21
+ # pq.pop # => "high"
22
+ class Queue
23
+ # Create a new priority queue
24
+ #
25
+ # @param mode [Symbol] :min (default) or :max
26
+ # @yield [a, b] optional custom comparator block
27
+ # @raise [Error] if mode is invalid
28
+ def initialize(mode: :min, &comparator)
29
+ unless %i[min max].include?(mode)
30
+ raise Error, "Invalid mode: #{mode}. Use :min or :max"
31
+ end
32
+
33
+ @heap = []
34
+ @comparator = comparator || default_comparator(mode)
35
+ end
36
+
37
+ # Push an item onto the queue with a given priority
38
+ #
39
+ # @param item [Object] the item to enqueue
40
+ # @param priority [Numeric] the priority value
41
+ # @return [self]
42
+ def push(item, priority: 0)
43
+ entry = { item: item, priority: priority }
44
+ @heap << entry
45
+ bubble_up(@heap.length - 1)
46
+ self
47
+ end
48
+
49
+ # Remove and return the highest-priority item
50
+ #
51
+ # @return [Object, nil] the item, or nil if empty
52
+ def pop
53
+ return nil if @heap.empty?
54
+
55
+ swap(0, @heap.length - 1)
56
+ entry = @heap.pop
57
+ bubble_down(0) unless @heap.empty?
58
+ entry[:item]
59
+ end
60
+
61
+ # Return the highest-priority item without removing it
62
+ #
63
+ # @return [Object, nil] the item, or nil if empty
64
+ def peek
65
+ return nil if @heap.empty?
66
+
67
+ @heap[0][:item]
68
+ end
69
+
70
+ # Return the number of items in the queue
71
+ #
72
+ # @return [Integer]
73
+ def size
74
+ @heap.length
75
+ end
76
+
77
+ # Check if the queue is empty
78
+ #
79
+ # @return [Boolean]
80
+ def empty?
81
+ @heap.empty?
82
+ end
83
+
84
+ # Update the priority of an existing item
85
+ #
86
+ # @param item [Object] the item to update
87
+ # @param new_priority [Numeric] the new priority
88
+ # @return [self]
89
+ # @raise [Error] if the item is not found
90
+ def change_priority(item, new_priority)
91
+ index = @heap.index { |e| e[:item] == item }
92
+ raise Error, 'Item not found in queue' if index.nil?
93
+
94
+ @heap[index][:priority] = new_priority
95
+ bubble_up(index)
96
+ bubble_down(index)
97
+ self
98
+ end
99
+
100
+ # Return all items as a sorted array
101
+ #
102
+ # @return [Array] items in priority order
103
+ def to_a
104
+ clone = Queue.new(&@comparator)
105
+ @heap.each { |entry| clone.push(entry[:item], priority: entry[:priority]) }
106
+
107
+ result = []
108
+ result << clone.pop until clone.empty?
109
+ result
110
+ end
111
+
112
+ # Merge another priority queue into this one
113
+ #
114
+ # @param other [Queue] the queue to merge
115
+ # @return [self]
116
+ def merge(other)
117
+ other.each_entry { |item, priority| push(item, priority: priority) }
118
+ self
119
+ end
120
+
121
+ # @api private
122
+ def each_entry
123
+ @heap.each { |entry| yield entry[:item], entry[:priority] }
124
+ end
125
+
126
+ private
127
+
128
+ def default_comparator(mode)
129
+ case mode
130
+ when :min then ->(a, b) { a[:priority] <=> b[:priority] }
131
+ when :max then ->(a, b) { b[:priority] <=> a[:priority] }
132
+ end
133
+ end
134
+
135
+ def bubble_up(index)
136
+ while index.positive?
137
+ parent = (index - 1) / 2
138
+ break unless @comparator.call(@heap[index], @heap[parent]).negative?
139
+
140
+ swap(index, parent)
141
+ index = parent
142
+ end
143
+ end
144
+
145
+ def bubble_down(index)
146
+ size = @heap.length
147
+
148
+ loop do
149
+ smallest = index
150
+ left = 2 * index + 1
151
+ right = 2 * index + 2
152
+
153
+ smallest = left if left < size && @comparator.call(@heap[left], @heap[smallest]).negative?
154
+ smallest = right if right < size && @comparator.call(@heap[right], @heap[smallest]).negative?
155
+
156
+ break if smallest == index
157
+
158
+ swap(index, smallest)
159
+ index = smallest
160
+ end
161
+ end
162
+
163
+ def swap(i, j)
164
+ @heap[i], @heap[j] = @heap[j], @heap[i]
165
+ end
166
+ end
167
+ end
168
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: philiprehberger-priority_queue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Philip Rehberger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-03-22 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: An efficient priority queue using a binary heap. Supports min-heap and
14
+ max-heap modes, custom comparators, priority updates, and merge operations.
15
+ email:
16
+ - me@philiprehberger.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - CHANGELOG.md
22
+ - LICENSE
23
+ - README.md
24
+ - lib/philiprehberger/priority_queue.rb
25
+ - lib/philiprehberger/priority_queue/version.rb
26
+ homepage: https://github.com/philiprehberger/rb-priority-queue
27
+ licenses:
28
+ - MIT
29
+ metadata:
30
+ homepage_uri: https://github.com/philiprehberger/rb-priority-queue
31
+ source_code_uri: https://github.com/philiprehberger/rb-priority-queue
32
+ changelog_uri: https://github.com/philiprehberger/rb-priority-queue/blob/main/CHANGELOG.md
33
+ bug_tracker_uri: https://github.com/philiprehberger/rb-priority-queue/issues
34
+ rubygems_mfa_required: 'true'
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 3.1.0
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubygems_version: 3.5.22
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Binary heap priority queue with min/max modes and custom comparators
54
+ test_files: []