d_heap 0.3.0 → 0.7.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/.clang-format +21 -0
- data/.github/workflows/main.yml +18 -3
- data/.gitignore +1 -0
- data/.rubocop.yml +32 -2
- data/.yardopts +10 -0
- data/CHANGELOG.md +93 -0
- data/D +7 -0
- data/README.md +416 -154
- data/d_heap.gemspec +20 -8
- data/docs/benchmarks-2.txt +93 -0
- data/docs/benchmarks-mem.txt +39 -0
- data/docs/benchmarks.txt +686 -0
- data/docs/profile.txt +358 -0
- data/ext/d_heap/.rubocop.yml +7 -0
- data/ext/d_heap/d_heap.c +917 -295
- data/ext/d_heap/extconf.rb +45 -3
- data/images/push_n.png +0 -0
- data/images/push_n_pop_n.png +0 -0
- data/images/push_pop.png +0 -0
- data/images/wikipedia-min-heap.png +0 -0
- data/lib/d_heap.rb +116 -3
- data/lib/d_heap/version.rb +1 -1
- metadata +33 -17
- data/.rspec +0 -3
- data/.travis.yml +0 -6
- data/Gemfile +0 -11
- data/Gemfile.lock +0 -67
- data/Rakefile +0 -20
- data/bin/console +0 -15
- data/bin/rake +0 -29
- data/bin/rspec +0 -29
- data/bin/rubocop +0 -29
- data/bin/setup +0 -8
- data/ext/d_heap/d_heap.h +0 -41
data/ext/d_heap/extconf.rb
CHANGED
@@ -2,10 +2,52 @@
|
|
2
2
|
|
3
3
|
require "mkmf"
|
4
4
|
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
5
|
+
# For testing in CI (because I don't otherwise have easy access to Mac OS):
|
6
|
+
# $CFLAGS << " -D__D_HEAP_DEBUG" if /darwin/ =~ RUBY_PLATFORM
|
7
|
+
# $CFLAGS << " -debug inline-debug-info "
|
8
|
+
# $CFLAGS << " -g -ginline-points "
|
9
|
+
# $CFLAGS << " -fno-omit-frame-pointer "
|
10
|
+
|
11
|
+
# Use `rake compile -- --enable-debug`
|
12
|
+
debug_mode = enable_config("debug", ENV["EXTCONF_DEBUG"] == "1")
|
13
|
+
|
14
|
+
# Use `rake compile -- --enable-development`
|
15
|
+
devel_mode = enable_config("development") || debug_mode
|
16
|
+
|
17
|
+
if debug_mode
|
18
|
+
$stderr.puts "Building in debug mode." # rubocop:disable Style/StderrPuts
|
19
|
+
CONFIG["warnflags"] \
|
20
|
+
<< " -ggdb" \
|
21
|
+
<< " -DDEBUG"
|
22
|
+
end
|
23
|
+
|
24
|
+
if devel_mode
|
25
|
+
$stderr.puts "Building in development mode." # rubocop:disable Style/StderrPuts
|
26
|
+
CONFIG["warnflags"] \
|
27
|
+
<< " -Wall " \
|
28
|
+
<< " -Wpedantic" \
|
29
|
+
# There are warnings on MacOS that are annoying to debug (I don't have a Mac).
|
30
|
+
unless RbConfig::CONFIG["target_os"] =~ /darwin/
|
31
|
+
CONFIG["warnflags"] << " -Werror"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Use `rake compile -- --enable-heapmap`
|
36
|
+
if enable_config("heapmap", true)
|
37
|
+
$stderr.puts "Building with DHeap::Map support." # rubocop:disable Style/StderrPuts
|
38
|
+
$defs.push "-DDHEAP_MAP"
|
39
|
+
end
|
8
40
|
|
9
41
|
have_func "rb_gc_mark_movable" # since ruby-2.7
|
10
42
|
|
43
|
+
check_sizeof("long")
|
44
|
+
check_sizeof("unsigned long long")
|
45
|
+
check_sizeof("long double")
|
46
|
+
check_sizeof("double")
|
47
|
+
|
48
|
+
unless have_macro("LDBL_MANT_DIG", "float.h")
|
49
|
+
raise NotImplementedError, "Missing LDBL_MANT_DIG."
|
50
|
+
end
|
51
|
+
|
52
|
+
create_header
|
11
53
|
create_makefile("d_heap/d_heap")
|
data/images/push_n.png
ADDED
Binary file
|
Binary file
|
data/images/push_pop.png
ADDED
Binary file
|
Binary file
|
data/lib/d_heap.rb
CHANGED
@@ -10,13 +10,126 @@ require "d_heap/version"
|
|
10
10
|
# the nodes have _d_ children instead of 2. This allows for "decrease priority"
|
11
11
|
# operations to be performed more quickly with the tradeoff of slower delete
|
12
12
|
# minimum. Additionally, _d_-ary heaps can have better memory cache behavior than
|
13
|
-
# binary heaps, allowing them to
|
13
|
+
# binary heaps, allowing them to pop more quickly in practice despite slower
|
14
14
|
# worst-case time complexity.
|
15
15
|
#
|
16
|
+
# Although _d_ can be configured when creating the heap, it's usually best to
|
17
|
+
# keep the default value of 4, because d=4 gives the smallest coefficient for
|
18
|
+
# <tt>(d + 1) log n / log d</tt> result. As always, use benchmarks for your
|
19
|
+
# particular use-case.
|
20
|
+
#
|
21
|
+
# @example Basic push, peek, and pop
|
22
|
+
# # create some example objects to place in our heap
|
23
|
+
# Task = Struct.new(:id, :time) do
|
24
|
+
# def to_f; time.to_f end
|
25
|
+
# end
|
26
|
+
# t1 = Task.new(1, Time.now + 5*60)
|
27
|
+
# t2 = Task.new(2, Time.now + 50)
|
28
|
+
# t3 = Task.new(3, Time.now + 60)
|
29
|
+
# t4 = Task.new(4, Time.now + 5)
|
30
|
+
#
|
31
|
+
# # create the heap
|
32
|
+
# require "d_heap"
|
33
|
+
# heap = DHeap.new
|
34
|
+
#
|
35
|
+
# # push with an explicit score (which might be extrinsic to the value)
|
36
|
+
# heap.push t1, t1.to_f
|
37
|
+
#
|
38
|
+
# # the score will be implicitly cast with Float, so any object with #to_f
|
39
|
+
# heap.push t2, t2
|
40
|
+
#
|
41
|
+
# # if the object has an intrinsic score via #to_f, "<<" is the simplest API
|
42
|
+
# heap << t3 << t4
|
43
|
+
#
|
44
|
+
# # pop returns the lowest scored item, and removes it from the heap
|
45
|
+
# heap.pop # => #<struct Task id=4, time=2021-01-17 17:02:22.5574 -0500>
|
46
|
+
# heap.pop # => #<struct Task id=2, time=2021-01-17 17:03:07.5574 -0500>
|
47
|
+
#
|
48
|
+
# # peek returns the lowest scored item, without removing it from the heap
|
49
|
+
# heap.peek # => #<struct Task id=3, time=2021-01-17 17:03:17.5574 -0500>
|
50
|
+
# heap.pop # => #<struct Task id=3, time=2021-01-17 17:03:17.5574 -0500>
|
51
|
+
#
|
52
|
+
# # pop_lte handles the common "h.pop if h.peek_score < max" pattern
|
53
|
+
# heap.pop_lte(Time.now + 65) # => nil
|
54
|
+
#
|
55
|
+
# # the heap size can be inspected with size and empty?
|
56
|
+
# heap.empty? # => false
|
57
|
+
# heap.size # => 1
|
58
|
+
# heap.pop # => #<struct Task id=1, time=2021-01-17 17:07:17.5574 -0500>
|
59
|
+
# heap.empty? # => true
|
60
|
+
# heap.size # => 0
|
61
|
+
#
|
62
|
+
# # popping from an empty heap returns nil
|
63
|
+
# heap.pop # => nil
|
64
|
+
#
|
16
65
|
class DHeap
|
66
|
+
alias deq pop
|
67
|
+
alias shift pop
|
68
|
+
alias next pop
|
69
|
+
alias pop_all_lt pop_all_below
|
70
|
+
alias pop_below pop_lt
|
71
|
+
|
72
|
+
alias enq push
|
73
|
+
|
74
|
+
alias first peek
|
75
|
+
|
76
|
+
alias length size
|
77
|
+
alias count size
|
78
|
+
|
79
|
+
# Initialize a _d_-ary min-heap.
|
80
|
+
#
|
81
|
+
# @param d [Integer] Number of children for each parent node.
|
82
|
+
# Higher values generally speed up push but slow down pop.
|
83
|
+
# If all pushes are popped, the default is probably best.
|
84
|
+
# @param capacity [Integer] initial capacity of the heap.
|
85
|
+
def initialize(d: DEFAULT_D, capacity: DEFAULT_CAPA) # rubocop:disable Naming/MethodParameterName
|
86
|
+
__init_without_kw__(d, capacity, false)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Consumes the heap by popping each minumum value until it is empty.
|
90
|
+
#
|
91
|
+
# If you want to iterate over the heap without consuming it, you will need to
|
92
|
+
# first call +#dup+
|
93
|
+
#
|
94
|
+
# @param with_score [Boolean] if scores shoul also be yielded
|
95
|
+
#
|
96
|
+
# @yieldparam value [Object] each value that would be popped
|
97
|
+
# @yieldparam score [Numeric] each value's score, if +with_scores+ is true
|
98
|
+
#
|
99
|
+
# @return [Enumerator] if no block is given
|
100
|
+
# @return [nil] if a block is given
|
101
|
+
def each_pop(with_scores: false)
|
102
|
+
return to_enum(__method__, with_scores: with_scores) unless block_given?
|
103
|
+
if with_scores
|
104
|
+
yield(*pop_with_score) until empty?
|
105
|
+
else
|
106
|
+
yield pop until empty?
|
107
|
+
end
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
if defined?(Map)
|
112
|
+
|
113
|
+
# Unlike {DHeap}, an object can only be added into a {DHeap::Map} once. Any
|
114
|
+
# subsequent addition simply rescores the already existing member. Objects
|
115
|
+
# are identified by their {#hash} value, just like with Hash.
|
116
|
+
class Map
|
117
|
+
alias score :[]
|
118
|
+
alias rescore :[]=
|
119
|
+
alias update :[]=
|
120
|
+
|
121
|
+
# Initialize a _d_-ary min-heap which can map objects to scores.
|
122
|
+
#
|
123
|
+
# @param d [Integer] Number of children for each parent node.
|
124
|
+
# Higher values generally speed up push but slow down pop.
|
125
|
+
# If all pushes are popped, the default is probably best.
|
126
|
+
# @param capacity [Integer] initial capacity of the heap.
|
127
|
+
def initialize(d: DEFAULT_D, capacity: DEFAULT_CAPA) # rubocop:disable Naming/MethodParameterName
|
128
|
+
__init_without_kw__(d, capacity, true)
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
17
132
|
|
18
|
-
def initialize_copy(other)
|
19
|
-
raise NotImplementedError, "initialize_copy should deep copy array"
|
20
133
|
end
|
21
134
|
|
22
135
|
end
|
data/lib/d_heap/version.rb
CHANGED
metadata
CHANGED
@@ -1,17 +1,31 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: d_heap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nicholas a. evans
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: benchmark_driver
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-prof
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - ">="
|
@@ -34,26 +48,28 @@ extensions:
|
|
34
48
|
- ext/d_heap/extconf.rb
|
35
49
|
extra_rdoc_files: []
|
36
50
|
files:
|
51
|
+
- ".clang-format"
|
37
52
|
- ".github/workflows/main.yml"
|
38
53
|
- ".gitignore"
|
39
|
-
- ".rspec"
|
40
54
|
- ".rubocop.yml"
|
41
|
-
- ".
|
55
|
+
- ".yardopts"
|
56
|
+
- CHANGELOG.md
|
42
57
|
- CODE_OF_CONDUCT.md
|
43
|
-
-
|
44
|
-
- Gemfile.lock
|
58
|
+
- D
|
45
59
|
- LICENSE.txt
|
46
60
|
- README.md
|
47
|
-
- Rakefile
|
48
|
-
- bin/console
|
49
|
-
- bin/rake
|
50
|
-
- bin/rspec
|
51
|
-
- bin/rubocop
|
52
|
-
- bin/setup
|
53
61
|
- d_heap.gemspec
|
62
|
+
- docs/benchmarks-2.txt
|
63
|
+
- docs/benchmarks-mem.txt
|
64
|
+
- docs/benchmarks.txt
|
65
|
+
- docs/profile.txt
|
66
|
+
- ext/d_heap/.rubocop.yml
|
54
67
|
- ext/d_heap/d_heap.c
|
55
|
-
- ext/d_heap/d_heap.h
|
56
68
|
- ext/d_heap/extconf.rb
|
69
|
+
- images/push_n.png
|
70
|
+
- images/push_n_pop_n.png
|
71
|
+
- images/push_pop.png
|
72
|
+
- images/wikipedia-min-heap.png
|
57
73
|
- lib/d_heap.rb
|
58
74
|
- lib/d_heap/version.rb
|
59
75
|
homepage: https://github.com/nevans/d_heap
|
@@ -62,7 +78,7 @@ licenses:
|
|
62
78
|
metadata:
|
63
79
|
homepage_uri: https://github.com/nevans/d_heap
|
64
80
|
source_code_uri: https://github.com/nevans/d_heap
|
65
|
-
changelog_uri: https://github.com/nevans/d_heap/blob/master/
|
81
|
+
changelog_uri: https://github.com/nevans/d_heap/blob/master/CHANGELOG.md
|
66
82
|
post_install_message:
|
67
83
|
rdoc_options: []
|
68
84
|
require_paths:
|
@@ -71,7 +87,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
71
87
|
requirements:
|
72
88
|
- - ">="
|
73
89
|
- !ruby/object:Gem::Version
|
74
|
-
version: 2.
|
90
|
+
version: 2.4.0
|
75
91
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
92
|
requirements:
|
77
93
|
- - ">="
|
@@ -81,5 +97,5 @@ requirements: []
|
|
81
97
|
rubygems_version: 3.1.4
|
82
98
|
signing_key:
|
83
99
|
specification_version: 4
|
84
|
-
summary:
|
100
|
+
summary: very fast min-heap priority queue
|
85
101
|
test_files: []
|
data/.rspec
DELETED
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/Gemfile.lock
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
d_heap (0.3.0)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
ast (2.4.1)
|
10
|
-
benchmark-malloc (0.2.0)
|
11
|
-
benchmark-perf (0.6.0)
|
12
|
-
benchmark-trend (0.4.0)
|
13
|
-
diff-lcs (1.4.4)
|
14
|
-
parallel (1.19.2)
|
15
|
-
parser (2.7.2.0)
|
16
|
-
ast (~> 2.4.1)
|
17
|
-
rainbow (3.0.0)
|
18
|
-
rake (13.0.3)
|
19
|
-
rake-compiler (1.1.1)
|
20
|
-
rake
|
21
|
-
regexp_parser (1.8.2)
|
22
|
-
rexml (3.2.3)
|
23
|
-
rspec (3.10.0)
|
24
|
-
rspec-core (~> 3.10.0)
|
25
|
-
rspec-expectations (~> 3.10.0)
|
26
|
-
rspec-mocks (~> 3.10.0)
|
27
|
-
rspec-benchmark (0.6.0)
|
28
|
-
benchmark-malloc (~> 0.2)
|
29
|
-
benchmark-perf (~> 0.6)
|
30
|
-
benchmark-trend (~> 0.4)
|
31
|
-
rspec (>= 3.0)
|
32
|
-
rspec-core (3.10.0)
|
33
|
-
rspec-support (~> 3.10.0)
|
34
|
-
rspec-expectations (3.10.0)
|
35
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
36
|
-
rspec-support (~> 3.10.0)
|
37
|
-
rspec-mocks (3.10.0)
|
38
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
39
|
-
rspec-support (~> 3.10.0)
|
40
|
-
rspec-support (3.10.0)
|
41
|
-
rubocop (1.2.0)
|
42
|
-
parallel (~> 1.10)
|
43
|
-
parser (>= 2.7.1.5)
|
44
|
-
rainbow (>= 2.2.2, < 4.0)
|
45
|
-
regexp_parser (>= 1.8)
|
46
|
-
rexml
|
47
|
-
rubocop-ast (>= 1.0.1)
|
48
|
-
ruby-progressbar (~> 1.7)
|
49
|
-
unicode-display_width (>= 1.4.0, < 2.0)
|
50
|
-
rubocop-ast (1.1.1)
|
51
|
-
parser (>= 2.7.1.5)
|
52
|
-
ruby-progressbar (1.10.1)
|
53
|
-
unicode-display_width (1.7.0)
|
54
|
-
|
55
|
-
PLATFORMS
|
56
|
-
ruby
|
57
|
-
|
58
|
-
DEPENDENCIES
|
59
|
-
d_heap!
|
60
|
-
rake (~> 13.0)
|
61
|
-
rake-compiler
|
62
|
-
rspec (~> 3.10)
|
63
|
-
rspec-benchmark
|
64
|
-
rubocop (~> 1.0)
|
65
|
-
|
66
|
-
BUNDLED WITH
|
67
|
-
2.2.3
|
data/Rakefile
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bundler/gem_tasks"
|
4
|
-
require "rspec/core/rake_task"
|
5
|
-
|
6
|
-
RSpec::Core::RakeTask.new(:spec)
|
7
|
-
|
8
|
-
require "rubocop/rake_task"
|
9
|
-
|
10
|
-
RuboCop::RakeTask.new
|
11
|
-
|
12
|
-
require "rake/extensiontask"
|
13
|
-
|
14
|
-
task build: :compile
|
15
|
-
|
16
|
-
Rake::ExtensionTask.new("d_heap") do |ext|
|
17
|
-
ext.lib_dir = "lib/d_heap"
|
18
|
-
end
|
19
|
-
|
20
|
-
task default: %i[clobber compile spec rubocop]
|
data/bin/console
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "bundler/setup"
|
5
|
-
require "d_heap"
|
6
|
-
|
7
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
9
|
-
|
10
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
-
# require "pry"
|
12
|
-
# Pry.start
|
13
|
-
|
14
|
-
require "irb"
|
15
|
-
IRB.start(__FILE__)
|
data/bin/rake
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
#
|
5
|
-
# This file was generated by Bundler.
|
6
|
-
#
|
7
|
-
# The application 'rake' is installed as part of a gem, and
|
8
|
-
# this file is here to facilitate running it.
|
9
|
-
#
|
10
|
-
|
11
|
-
require "pathname"
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
-
Pathname.new(__FILE__).realpath)
|
14
|
-
|
15
|
-
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
-
|
17
|
-
if File.file?(bundle_binstub)
|
18
|
-
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
-
load(bundle_binstub)
|
20
|
-
else
|
21
|
-
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
-
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
require "rubygems"
|
27
|
-
require "bundler/setup"
|
28
|
-
|
29
|
-
load Gem.bin_path("rake", "rake")
|