fast_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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9b07c36a55c10c48198b7822e32d013063683e20
4
+ data.tar.gz: f0e1aa78ced4e6e615ea87609969d153b3550203
5
+ SHA512:
6
+ metadata.gz: e53b4d3a10fe20394f5cc50fd5b569b1fa06ad283b2bc89fbf7c4a40b2a5a23ab44952d1220d77026e13020402f980149ee29a708deee0e72812acc7fdf508f6
7
+ data.tar.gz: c9f3dd45f5b3ea6ebdb16435290cd271d5b0d1de7532b672857a148ee3d0340cb28a6b83ba6e5a8751dfc57e581452b57b1dff1efd10bdc65066d5bb63bdf2fa
@@ -0,0 +1,15 @@
1
+ # ruby
2
+ /.bundle/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+
12
+ # rust
13
+ target/
14
+ Cargo.lock
15
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.13.2
@@ -0,0 +1,10 @@
1
+ [package]
2
+ name = "fast_priority_queue"
3
+ version = "0.1.0"
4
+ authors = ["Simon Soriano <simon0191@gmail.com>"]
5
+
6
+ [dependencies]
7
+ ruru = "0.8.1"
8
+
9
+ [lib]
10
+ crate-type = ["dylib"]
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fast_priority_queue.gemspec
4
+ gemspec
@@ -0,0 +1,92 @@
1
+ # FastPriorityQueue
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/fast_priority_queue`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'fast_priority_queue'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install fast_priority_queue
22
+
23
+ ## Usage
24
+
25
+ ```
26
+ fpq = FastPriorityQueue.new
27
+ # or use a custom comparator function
28
+ fpq = FastPriorityQueue.new { |a,b| a<=>b }
29
+ # add elements to the queue
30
+ fpq.add 5
31
+ fpq.add 4
32
+ fpq.add 3
33
+ fpq.add 2
34
+ fpq.add 1
35
+ # see what's in the top of the queue
36
+ fpq.top # => 1
37
+ # take the top element
38
+ fpq.pop # => 1
39
+ fpq.pop # => 2
40
+ # see how many elements are in the queue
41
+ fpq.length # => 3
42
+ ```
43
+
44
+ ## Benchmarks
45
+ FastPriorityQueue is super fast. Here are some benchmarks against [PQueue](https://github.com/rubyworks/pqueue):
46
+
47
+ *(the smallest the number, the better)*
48
+
49
+ Benchmark of add and pop with fixnums:
50
+ ```
51
+ Rehearsal -----------------------------------------------
52
+ Fast#add 0.830000 0.010000 0.840000 ( 0.854182)
53
+ PQueue#push 22.610000 40.740000 63.350000 ( 67.128790)
54
+ Fast#pop 1.280000 0.010000 1.290000 ( 1.331896)
55
+ PQueue#pop 0.010000 0.000000 0.010000 ( 0.012800)
56
+ ------------------------------------- total: 65.490000sec
57
+
58
+ user system total real
59
+ Fast#add 0.860000 0.010000 0.870000 ( 0.896107)
60
+ PQueue#push 22.570000 41.690000 64.260000 ( 68.815510)
61
+ Fast#pop 1.250000 0.010000 1.260000 ( 1.275417)
62
+ PQueue#pop 0.010000 0.000000 0.010000 ( 0.011788)
63
+ ```
64
+
65
+ Benchmark of add and pop with objects:
66
+ ```
67
+ Rehearsal -----------------------------------------------
68
+ Fast#add 0.380000 0.010000 0.390000 ( 0.393005)
69
+ PQueue#push 36.730000 53.990000 90.720000 ( 96.215730)
70
+ Fast#pop 1.850000 0.020000 1.870000 ( 1.911544)
71
+ PQueue#pop 0.010000 0.000000 0.010000 ( 0.012731)
72
+ ------------------------------------- total: 92.990000sec
73
+
74
+ user system total real
75
+ Fast#add 0.410000 0.010000 0.420000 ( 0.423021)
76
+ PQueue#push 37.190000 55.130000 92.320000 (100.046236)
77
+ Fast#pop 1.970000 0.010000 1.980000 ( 2.031743)
78
+ PQueue#pop 0.020000 0.000000 0.020000 ( 0.014687)
79
+ ```
80
+ I had to use `Array#pop` from ruby because there's no `Array.pop` native extension by now.
81
+ That's why `#pop` benchmark is a little bit worse than PQueue.
82
+
83
+ ## Development
84
+
85
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
86
+
87
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
88
+
89
+ ## Contributing
90
+
91
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/fast_priority_queue.
92
+
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'thermite/tasks'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ Thermite::Tasks.new
7
+
8
+ desc 'Run Rust & Ruby testsuites'
9
+ task test: ['thermite:build', 'thermite:test', :spec]
10
+
11
+ task :default => :test
@@ -0,0 +1,46 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+
3
+ require 'benchmark'
4
+ require 'fast_priority_queue'
5
+ require 'pqueue'
6
+
7
+ t = 100_000
8
+ nums = (0..t).to_a
9
+ Benchmark.bmbm do |x|
10
+ fpq = FastPriorityQueue.new { |a,b| b <=> a }
11
+ pq = PQueue.new { |a,b| a > b }
12
+
13
+
14
+ x.report('Fast#add') { nums.each { |x| fpq.add x } }
15
+ x.report('PQueue#push') { nums.each { |x| pq.push x } }
16
+
17
+ x.report("Fast#pop") { fpq.pop until fpq.empty? }
18
+ x.report("PQueue#pop") { pq.pop until pq.empty? }
19
+ end
20
+
21
+ class Patient < Struct.new(:name,:age,:severity)
22
+ def <=>(other)
23
+ if self.severity != other.severity
24
+ other.severity <=> self.severity
25
+ elsif self.age != other.age
26
+ other.age <=> self.age
27
+ else
28
+ self.name <=> other.name
29
+ end
30
+ end
31
+ end
32
+
33
+ t = 100_000
34
+ r = Random.new 182544861145423751987946792099462507225
35
+ patients = t.times.map { |i| Patient.new("patient - #{i}",r.rand(80),r.rand(3)) }
36
+ Benchmark.bmbm do |x|
37
+ fpq = FastPriorityQueue.new
38
+ pq = PQueue.new { |a,b| (a <=> b) > 0}
39
+
40
+
41
+ x.report('Fast#add') { patients.each { |x| fpq.add x } }
42
+ x.report('PQueue#push') { patients.each { |x| pq.push x } }
43
+
44
+ x.report("Fast#pop") { fpq.pop until fpq.empty? }
45
+ x.report("PQueue#pop") { pq.pop until pq.empty? }
46
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "fast_priority_queue"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ require 'thermite/tasks'
2
+
3
+ project_dir = File.dirname(File.dirname(__FILE__))
4
+ Thermite::Tasks.new(cargo_project_path: project_dir, ruby_project_path: project_dir)
5
+ task default: %w(thermite:build)
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fast_priority_queue/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "fast_priority_queue"
8
+ spec.version = FastPriorityQueue::VERSION
9
+ spec.authors = ["Simon Soriano"]
10
+ spec.email = ["simon0191@gmail.com"]
11
+
12
+ spec.summary = "Fast priority queue using Rust"
13
+ spec.description = "Fast priority queue using Rust"
14
+ spec.homepage = "https://github.com/simon0191/fast_priority_queue"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ # if spec.respond_to?(:metadata)
19
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
20
+ # else
21
+ # raise "RubyGems 2.0 or newer is required to protect against " \
22
+ # "public gem pushes."
23
+ # end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{^(test|spec|features)/})
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_development_dependency "bundler", "~> 1.13"
33
+ spec.add_development_dependency "rake", "~> 10.0"
34
+ spec.add_development_dependency "rspec", "~> 3.0"
35
+ spec.add_development_dependency "pqueue", "~> 2.1"
36
+
37
+ spec.extensions << 'ext/Rakefile'
38
+ spec.add_runtime_dependency 'thermite', '~> 0'
39
+ end
@@ -0,0 +1,46 @@
1
+ require 'fast_priority_queue/version'
2
+ require 'thermite/fiddle'
3
+
4
+ class FastPriorityQueue
5
+
6
+ attr_reader :cmp
7
+
8
+ def initialize
9
+ @array = [nil]
10
+ if block_given?
11
+ @cmp = ->(a,b) { yield a,b }
12
+ else
13
+ @cmp = ->(a,b) { a <=> b }
14
+ end
15
+ end
16
+
17
+ def length
18
+ @array.length - 1
19
+ end
20
+
21
+ def add(x)
22
+ _add(@array,@cmp,x)
23
+ end
24
+
25
+ def top
26
+ @array[1]
27
+ end
28
+
29
+ def pop
30
+ _pop(@array,@cmp)
31
+ end
32
+
33
+ def empty?
34
+ length == 0
35
+ end
36
+
37
+ def _pop_last_element
38
+ #TODO: find a way to do this in Rust
39
+ @array.pop
40
+ end
41
+ end
42
+
43
+ toplevel_dir = File.dirname(File.dirname(__FILE__))
44
+ Thermite::Fiddle.load_module('init_fast_priority_queue',
45
+ cargo_project_path: toplevel_dir,
46
+ ruby_project_path: toplevel_dir)
@@ -0,0 +1,3 @@
1
+ class FastPriorityQueue
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,101 @@
1
+ #[macro_use]
2
+ extern crate ruru;
3
+
4
+ use ruru::{Class,Object,RString,Fixnum,AnyObject,Array,Proc,NilClass};
5
+ class!(FastPriorityQueue);
6
+
7
+ methods!(
8
+ FastPriorityQueue,
9
+ _itself,
10
+
11
+ fn hello_world() -> RString {
12
+ RString::new("hello world")
13
+ }
14
+
15
+ fn _add(arr: Array, cmp: Proc, x: AnyObject) -> NilClass {
16
+ let mut arr = arr.unwrap();
17
+ let x = x.unwrap();
18
+ let cmp = cmp.unwrap();
19
+ arr.push(x);
20
+ // bubble_up
21
+ let mut curr_index = (arr.length() - 1) as i64;
22
+ loop {
23
+ if curr_index <= 1 {
24
+ return NilClass::new();
25
+ }
26
+ let parent_index = curr_index/2;
27
+ let curr = arr.at(curr_index);
28
+ let parent = arr.at(parent_index);
29
+ let cmp_result = cmp.call(vec![ arr.at(curr_index) , arr.at(parent_index) ])
30
+ .try_convert_to::<Fixnum>()
31
+ .unwrap()
32
+ .to_i64();
33
+
34
+ if cmp_result > 0 {
35
+ return NilClass::new();
36
+ }
37
+ // exchange
38
+ arr.store(curr_index,parent);
39
+ arr.store(parent_index,curr);
40
+
41
+ curr_index = parent_index;
42
+ }
43
+ }
44
+
45
+ fn _pop(arr: Array, cmp: Proc) -> AnyObject {
46
+ let mut arr = arr.unwrap();
47
+ let cmp = cmp.unwrap();
48
+ let mut arr_len = arr.length() as i64;
49
+
50
+ if arr_len <= 1 {
51
+ return NilClass::new().to_any_object()
52
+ } else if arr_len == 2 {
53
+ return _itself.send("_pop_last_element",vec![]);
54
+ }
55
+
56
+ let top_val = arr.at(1);
57
+ let last_val = arr.at(arr_len - 1);
58
+ arr.store(1,last_val);
59
+ _itself.send("_pop_last_element",vec![]);
60
+ arr_len -= 1;
61
+
62
+ let mut curr_index: i64 = 1;
63
+ while curr_index * 2 < arr_len {
64
+ let mut child_index = curr_index * 2;
65
+
66
+ let not_the_last_element = child_index + 1 < arr_len;
67
+ if not_the_last_element {
68
+ let cmp_result = cmp.call(vec![ arr.at(child_index) , arr.at(child_index + 1) ])
69
+ .try_convert_to::<Fixnum>()
70
+ .unwrap()
71
+ .to_i64();
72
+
73
+ if cmp_result > 0 {
74
+ child_index += 1;
75
+ }
76
+ }
77
+ let cmp_result = cmp.call(vec![ arr.at(curr_index) , arr.at(child_index) ])
78
+ .try_convert_to::<Fixnum>()
79
+ .unwrap()
80
+ .to_i64();
81
+ if cmp_result <= 0 {
82
+ return top_val;
83
+ }
84
+ let curr = arr.at(curr_index);
85
+ let child = arr.at(child_index);
86
+ arr.store(curr_index,child);
87
+ arr.store(child_index,curr);
88
+
89
+ curr_index = child_index;
90
+ }
91
+ return top_val;
92
+ }
93
+ );
94
+
95
+ #[no_mangle]
96
+ pub extern fn init_fast_priority_queue() {
97
+ Class::from_existing("FastPriorityQueue").define(|itself| {
98
+ itself.def("_add", _add);
99
+ itself.def("_pop", _pop);
100
+ });
101
+ }
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fast_priority_queue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Simon Soriano
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-10-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pqueue
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: thermite
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Fast priority queue using Rust
84
+ email:
85
+ - simon0191@gmail.com
86
+ executables: []
87
+ extensions:
88
+ - ext/Rakefile
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".travis.yml"
94
+ - Cargo.toml
95
+ - Gemfile
96
+ - README.md
97
+ - Rakefile
98
+ - benchmark.rb
99
+ - bin/console
100
+ - bin/setup
101
+ - ext/Rakefile
102
+ - fast_priority_queue.gemspec
103
+ - lib/fast_priority_queue.rb
104
+ - lib/fast_priority_queue/version.rb
105
+ - src/lib.rs
106
+ homepage: https://github.com/simon0191/fast_priority_queue
107
+ licenses: []
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.5.1
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Fast priority queue using Rust
129
+ test_files: []