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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d542bb52d6a148cc95802bfc5d84549e605b7077009343496e51a00d405bed05
4
- data.tar.gz: b66ba40d7c3b4d29f108294e2623f85c48f52f1cdcfa7282191ed412d1877cf5
3
+ metadata.gz: 2710897393899fe90ddd5a3046f0fee0dd6d29aec842879ec28654e2767d1626
4
+ data.tar.gz: 8ccc55227d725e590ea965767765c29bb66211866cc948d81a3ed4997d6db733
5
5
  SHA512:
6
- metadata.gz: 3bacbfc0793cce5bb67c51b33a64a6a4bb91e0d2cdfea661e5ee5721433bdbc19b723724ebb54e86289b053e71740dcd4c70a7e02fbed998015cd4fa31728f67
7
- data.tar.gz: f2ec4934f51e631750aea36af36fb33a4b2dc0fee95ec1b138a8e97c75952b341f9f235e17a12868bb9233e88f6bb4bcfff0800e47fefe417712ee9167d389b1
6
+ metadata.gz: 8fd77563ddfaecf2ab085f4ff9da6b49e0c63cb65ea666ebf737a3aee175498ddb481908b2135d3ae861ae51f756f9abc9749d27a91d802b4fd8c32965d8a117
7
+ data.tar.gz: d002eba1aa937be5f7afc70e32155dccb108ef5d71cc874729379b403534e34122311234aab35f05c4c1a2df5b0ecfc7fed32b645c2865d1a59b1b911a3564dd
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,7 @@
1
+ === August 20, 2025
2
+
3
+ * Fix Heap not working in certain cases
4
+
1
5
  === January 22, 2013
2
6
 
3
7
  * JRuby support
data/README.markdown CHANGED
@@ -1,4 +1,4 @@
1
- # algorithms [![Build Status](https://travis-ci.org/kanwei/algorithms.svg?branch=master)](https://travis-ci.org/github/kanwei/algorithms)
1
+ # algorithms [![Ruby CI](https://github.com/kanwei/algorithms/actions/workflows/ci.yml/badge.svg?branch=master)](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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "algorithms"
5
- s.version = "1.0.1"
5
+ s.version = "1.1.0"
6
6
 
7
7
  s.authors = ["Kanwei Li"]
8
8
  s.email = "kanwei@gmail.com"
@@ -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 == root.key # this fixes a bug with duplicate keys not being in the right order
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
@@ -10,7 +10,6 @@ require 'containers/heap'
10
10
  This container is implemented using the Fibonacci heap included in the Collections library.
11
11
  =end
12
12
  class Containers::PriorityQueue
13
- include Enumerable
14
13
 
15
14
  # Create a new, empty PriorityQueue
16
15
  def initialize(&block)
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.1
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.2.32
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: []