algorithms 1.0.1 → 1.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 +4 -4
- data/CHANGELOG.markdown +4 -0
- data/README.markdown +1 -1
- data/algorithms.gemspec +1 -1
- data/lib/containers/heap.rb +1 -2
- data/lib/containers/priority_queue.rb +0 -1
- data/spec/heap_spec.rb +186 -0
- metadata +2 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2710897393899fe90ddd5a3046f0fee0dd6d29aec842879ec28654e2767d1626
|
4
|
+
data.tar.gz: 8ccc55227d725e590ea965767765c29bb66211866cc948d81a3ed4997d6db733
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fd77563ddfaecf2ab085f4ff9da6b49e0c63cb65ea666ebf737a3aee175498ddb481908b2135d3ae861ae51f756f9abc9749d27a91d802b4fd8c32965d8a117
|
7
|
+
data.tar.gz: d002eba1aa937be5f7afc70e32155dccb108ef5d71cc874729379b403534e34122311234aab35f05c4c1a2df5b0ecfc7fed32b645c2865d1a59b1b911a3564dd
|
data/CHANGELOG.markdown
CHANGED
data/README.markdown
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# algorithms [](https://github.com/kanwei/algorithms/actions/workflows/ci.yml)
|
2
2
|
|
3
3
|
[API Documentation](http://kanwei.github.io/algorithms/)
|
4
4
|
|
data/algorithms.gemspec
CHANGED
data/lib/containers/heap.rb
CHANGED
@@ -10,7 +10,6 @@
|
|
10
10
|
This library implements a Fibonacci heap, which allows O(1) complexity for most methods.
|
11
11
|
=end
|
12
12
|
class Containers::Heap
|
13
|
-
include Enumerable
|
14
13
|
|
15
14
|
# call-seq:
|
16
15
|
# size -> int
|
@@ -369,7 +368,7 @@ class Containers::Heap
|
|
369
368
|
degree += 1
|
370
369
|
end
|
371
370
|
degrees[degree] = root
|
372
|
-
min = root if min.key
|
371
|
+
min = root if !@compare_fn[min.key, root.key] # this fixes a bug with duplicate keys not being in the right order
|
373
372
|
end
|
374
373
|
end
|
375
374
|
@next = min
|
data/spec/heap_spec.rb
CHANGED
@@ -1,10 +1,196 @@
|
|
1
1
|
$: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
|
2
2
|
require 'algorithms'
|
3
3
|
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
class Book
|
7
|
+
attr_reader :title, :year_published
|
8
|
+
|
9
|
+
def initialize(title, year_published)
|
10
|
+
@title = title
|
11
|
+
@year_published = year_published
|
12
|
+
end
|
13
|
+
|
14
|
+
def <=>(other)
|
15
|
+
return nil unless other.is_a?(Book)
|
16
|
+
|
17
|
+
# 1. Compare by year first
|
18
|
+
year_comparison = self.year_published <=> other.year_published
|
19
|
+
|
20
|
+
# 2. If years are different, return that comparison result
|
21
|
+
return year_comparison unless year_comparison == 0
|
22
|
+
|
23
|
+
# 3. If years are the same (tie), compare by title alphabetically
|
24
|
+
# (Ensure title comparison returns -1, 0, or 1)
|
25
|
+
self.title <=> other.title
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
"\"#{title}\" (#{year_published})" # Simplified for test output
|
31
|
+
end
|
32
|
+
|
33
|
+
# Add an equality method for clearer test failures if needed,
|
34
|
+
# though <=> returning 0 handles equality for sorting/heap purposes.
|
35
|
+
def ==(other)
|
36
|
+
other.is_a?(Book) &&
|
37
|
+
self.title == other.title &&
|
38
|
+
self.year_published == other.year_published
|
39
|
+
end
|
40
|
+
alias eql? ==
|
41
|
+
|
42
|
+
def hash
|
43
|
+
[title, year_published].hash
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe ::Containers::Heap do
|
49
|
+
|
50
|
+
# --- Test Data Setup ---
|
51
|
+
let!(:book_1932) { Book.new("Brave New World", 1932) }
|
52
|
+
let!(:book_1949) { Book.new("1984", 1949) }
|
53
|
+
let!(:book_1951a) { Book.new("Foundation", 1951) }
|
54
|
+
# Add another book from the same year to test handling of equal priority items
|
55
|
+
let!(:book_1951b) { Book.new("The Illustrated Man", 1951) }
|
56
|
+
let!(:book_1965) { Book.new("Dune", 1965) }
|
57
|
+
let!(:book_1989) { Book.new("Hyperion", 1989) }
|
58
|
+
|
59
|
+
# Use shuffle to ensure tests don't depend on insertion order
|
60
|
+
let(:books) { [book_1965, book_1951a, book_1989, book_1949, book_1932, book_1951b].shuffle }
|
61
|
+
|
62
|
+
let(:expected_min_order) { books.sort }
|
63
|
+
|
64
|
+
let(:expected_max_order) { books.sort.reverse }
|
65
|
+
|
66
|
+
context "Min-Heap (using default <=> via Comparable)" do
|
67
|
+
# Initialize heap with books; it should use Book#<=> by default
|
68
|
+
let(:min_heap) { ::Containers::Heap.new(books) }
|
69
|
+
|
70
|
+
it "initializes with the correct size" do
|
71
|
+
expect(min_heap.size).to eq(books.size)
|
72
|
+
expect(min_heap.empty?).to be false
|
73
|
+
end
|
74
|
+
|
75
|
+
it "gets the next minimum element (earliest year) without removing it" do
|
76
|
+
# next should return the element with the smallest year_published
|
77
|
+
expect(min_heap.next).to eq(book_1932)
|
78
|
+
# next should not change the size
|
79
|
+
expect(min_heap.size).to eq(books.size)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "pops elements in ascending order of year_published" do
|
83
|
+
popped_books = []
|
84
|
+
while (book = min_heap.pop)
|
85
|
+
popped_books << book
|
86
|
+
end
|
87
|
+
|
88
|
+
# Verify the popped order matches the expected sorted order
|
89
|
+
expect(popped_books).to eq(expected_min_order)
|
90
|
+
|
91
|
+
# Verify the heap is now empty
|
92
|
+
expect(min_heap.size).to eq(0)
|
93
|
+
expect(min_heap.empty?).to be true
|
94
|
+
expect(min_heap.pop).to be_nil # Popping an empty heap
|
95
|
+
expect(min_heap.next).to be_nil # Getting next from an empty heap
|
96
|
+
end
|
97
|
+
|
98
|
+
it "correctly updates size after popping" do
|
99
|
+
expect(min_heap.size).to eq(books.size)
|
100
|
+
min_heap.pop
|
101
|
+
expect(min_heap.size).to eq(books.size - 1)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
context "Max-Heap (using a custom comparison block)" do
|
107
|
+
# Initialize heap with books and a block for max-heap behavior
|
108
|
+
# The block returns true if x should have higher priority (be "larger") than y
|
109
|
+
let(:max_heap) { ::Containers::Heap.new(books) { |x, y| x > y } } # Use > from Comparable
|
110
|
+
|
111
|
+
it "initializes with the correct size" do
|
112
|
+
expect(max_heap.size).to eq(books.size)
|
113
|
+
expect(max_heap.empty?).to be false
|
114
|
+
end
|
115
|
+
|
116
|
+
it "gets the next maximum element (latest year) without removing it" do
|
117
|
+
# next should return the element with the largest year_published
|
118
|
+
expect(max_heap.next).to eq(book_1989)
|
119
|
+
# next should not change the size
|
120
|
+
expect(max_heap.size).to eq(books.size)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "pops elements in descending order of year_published" do
|
124
|
+
popped_books = []
|
125
|
+
while (book = max_heap.pop)
|
126
|
+
popped_books << book
|
127
|
+
end
|
128
|
+
|
129
|
+
# Verify the popped order matches the expected reversed sorted order
|
130
|
+
expect(popped_books).to eq(expected_max_order)
|
131
|
+
|
132
|
+
# Verify the heap is now empty
|
133
|
+
expect(max_heap.size).to eq(0)
|
134
|
+
expect(max_heap.empty?).to be true
|
135
|
+
expect(max_heap.pop).to be_nil # Popping an empty heap
|
136
|
+
expect(max_heap.next).to be_nil # Getting next from an empty heap
|
137
|
+
end
|
138
|
+
|
139
|
+
it "correctly updates size after popping" do
|
140
|
+
expect(max_heap.size).to eq(books.size)
|
141
|
+
max_heap.pop
|
142
|
+
expect(max_heap.size).to eq(books.size - 1)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "Edge cases" do
|
147
|
+
it "handles an empty initialization" do
|
148
|
+
heap = ::Containers::Heap.new([])
|
149
|
+
expect(heap.size).to eq(0)
|
150
|
+
expect(heap.empty?).to be true
|
151
|
+
expect(heap.pop).to be_nil
|
152
|
+
expect(heap.next).to be_nil
|
153
|
+
end
|
154
|
+
|
155
|
+
it "handles initialization with one element" do
|
156
|
+
heap = ::Containers::Heap.new([book_1965])
|
157
|
+
expect(heap.size).to eq(1)
|
158
|
+
expect(heap.next).to eq(book_1965)
|
159
|
+
expect(heap.pop).to eq(book_1965)
|
160
|
+
expect(heap.empty?).to be true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
4
166
|
describe Containers::Heap do
|
5
167
|
before(:each) do
|
6
168
|
@heap = Containers::MaxHeap.new
|
7
169
|
end
|
170
|
+
|
171
|
+
it "should run without error when given Objects and a non-ordering comparator" do
|
172
|
+
# Create and store the initial distinct objects
|
173
|
+
initial_objects = 10.times.map { Object.new }
|
174
|
+
initial_object_ids = initial_objects.map(&:object_id) # Store IDs for comparison
|
175
|
+
|
176
|
+
min_heap = ::Containers::Heap.new(initial_objects) { |x, y| (x <=> y) == -1 }
|
177
|
+
|
178
|
+
expect(min_heap.size).to eq(10)
|
179
|
+
|
180
|
+
popped_elements = []
|
181
|
+
expect {
|
182
|
+
while val = min_heap.pop do
|
183
|
+
popped_elements << val
|
184
|
+
end
|
185
|
+
}.not_to raise_error
|
186
|
+
|
187
|
+
expect(min_heap.empty?).to be true
|
188
|
+
expect(popped_elements.size).to eq(10)
|
189
|
+
|
190
|
+
# Assert that exactly the same objects were returned, regardless of order.
|
191
|
+
# Comparing by object_id is the most reliable way for distinct Objects.
|
192
|
+
expect(popped_elements.map(&:object_id)).to contain_exactly(*initial_object_ids)
|
193
|
+
end
|
8
194
|
|
9
195
|
it "should not let you merge with non-heaps" do
|
10
196
|
expect { @heap.merge!(nil) }.to raise_error(ArgumentError)
|
metadata
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: algorithms
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kanwei Li
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
10
|
date: 2023-11-11 00:00:00.000000000 Z
|
@@ -78,7 +77,6 @@ homepage: https://github.com/kanwei/algorithms
|
|
78
77
|
licenses:
|
79
78
|
- MIT
|
80
79
|
metadata: {}
|
81
|
-
post_install_message:
|
82
80
|
rdoc_options:
|
83
81
|
- "--line-numbers"
|
84
82
|
- "--inline-source"
|
@@ -100,8 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
98
|
- !ruby/object:Gem::Version
|
101
99
|
version: '0'
|
102
100
|
requirements: []
|
103
|
-
rubygems_version: 3.
|
104
|
-
signing_key:
|
101
|
+
rubygems_version: 3.6.9
|
105
102
|
specification_version: 4
|
106
103
|
summary: Useful algorithms and data structures for Ruby. Optional C extensions.
|
107
104
|
test_files: []
|