From 5a49cf62c5676c3a150ce9afec37319a78195277 Mon Sep 17 00:00:00 2001 From: Josh Bielick Date: Wed, 17 Nov 2021 16:50:28 -0500 Subject: [PATCH] wip --- .gitignore | 3 + CHANGELOG | 2 +- Gemfile | 3 + Gemfile.lock | 22 + Makefile | 140 -- Rakefile | 6 + compare_comments.rb | 49 - ext/priority_queue/CPriorityQueue/extconf.rb | 2 - ext/priority_queue/extconf.rb | 2 + .../{CPriorityQueue => }/priority_queue.c | 104 +- lib/priority_queue.rb | 13 +- lib/priority_queue/c_priority_queue.rb | 1 - lib/priority_queue/poor_priority_queue.rb | 46 - lib/priority_queue/ruby_priority_queue.rb | 526 ------ priority_queue.gemspec | 20 + priority_queue.so | Bin 22530 -> 0 bytes setup.rb | 1551 ----------------- test/priority_queue_test.rb | 101 +- 18 files changed, 117 insertions(+), 2474 deletions(-) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Gemfile.lock delete mode 100644 Makefile create mode 100644 Rakefile delete mode 100644 compare_comments.rb delete mode 100644 ext/priority_queue/CPriorityQueue/extconf.rb create mode 100644 ext/priority_queue/extconf.rb rename ext/priority_queue/{CPriorityQueue => }/priority_queue.c (95%) delete mode 100644 lib/priority_queue/c_priority_queue.rb delete mode 100644 lib/priority_queue/poor_priority_queue.rb delete mode 100644 lib/priority_queue/ruby_priority_queue.rb create mode 100644 priority_queue.gemspec delete mode 100755 priority_queue.so delete mode 100644 setup.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ee6d14 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +vendor +tmp +.DS_Store diff --git a/CHANGELOG b/CHANGELOG index b9158a0..311c490 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,7 +15,7 @@ 0.1.0 * API changes * Added lots of unit tests - * Restructured + * Restructured * Fallback to ruby version if c version is not available * Added Efficent Pure Ruby Implementation (3 times slower than c version in dijkstras algorithm) diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..fa75df1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..fbb4732 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,22 @@ +PATH + remote: . + specs: + priority_queue (0.2.0) + +GEM + remote: https://rubygems.org/ + specs: + rake (13.0.6) + rake-compiler (1.1.1) + rake + +PLATFORMS + ruby + +DEPENDENCIES + priority_queue! + rake (>= 1.9.1) + rake-compiler (>= 0.8.3) + +BUNDLED WITH + 1.17.2 diff --git a/Makefile b/Makefile deleted file mode 100644 index 78f7a2c..0000000 --- a/Makefile +++ /dev/null @@ -1,140 +0,0 @@ - -SHELL = /bin/sh - -#### Start of system configuration section. #### - -srcdir = . -topdir = /usr/lib/ruby/1.8/i486-linux -hdrdir = $(topdir) -VPATH = $(srcdir):$(topdir):$(hdrdir) -prefix = $(DESTDIR)/usr -exec_prefix = $(prefix) -sitedir = $(DESTDIR)/usr/local/lib/site_ruby -rubylibdir = $(libdir)/ruby/$(ruby_version) -archdir = $(rubylibdir)/$(arch) -sbindir = $(exec_prefix)/sbin -datadir = $(prefix)/share -includedir = $(prefix)/include -infodir = $(prefix)/info -sysconfdir = $(DESTDIR)/etc -mandir = $(datadir)/man -libdir = $(exec_prefix)/lib -sharedstatedir = $(prefix)/com -oldincludedir = $(DESTDIR)/usr/include -sitearchdir = $(sitelibdir)/$(sitearch) -bindir = $(exec_prefix)/bin -localstatedir = $(DESTDIR)/var -sitelibdir = $(sitedir)/$(ruby_version) -libexecdir = $(exec_prefix)/libexec - -CC = gcc -LIBRUBY = $(LIBRUBY_SO) -LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a -LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME) -LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static - -CFLAGS = -fPIC -Wall -g -O2 -fPIC -CPPFLAGS = -I. -I$(topdir) -I$(hdrdir) -I$(srcdir) -CXXFLAGS = $(CFLAGS) -DLDFLAGS = -LDSHARED = $(CC) -shared -AR = ar -EXEEXT = - -RUBY_INSTALL_NAME = ruby1.8 -RUBY_SO_NAME = ruby1.8 -arch = i486-linux -sitearch = i486-linux -ruby_version = 1.8 -ruby = /usr/bin/ruby1.8 -RUBY = $(ruby) -RM = rm -f -MAKEDIRS = mkdir -p -INSTALL = /usr/bin/install -c -INSTALL_PROG = $(INSTALL) -m 0755 -INSTALL_DATA = $(INSTALL) -m 644 -COPY = cp - -#### End of system configuration section. #### - -preload = - -libpath = $(libdir) -LIBPATH = -L"$(libdir)" -DEFFILE = - -CLEANFILES = -DISTCLEANFILES = - -extout = -extout_prefix = -target_prefix = -LOCAL_LIBS = -LIBS = $(LIBRUBYARG_SHARED) -lpthread -ldl -lcrypt -lm -lc -SRCS = priority_queue.c -OBJS = priority_queue.o -TARGET = priority_queue -DLLIB = $(TARGET).so -STATIC_LIB = - -RUBYCOMMONDIR = $(sitedir)$(target_prefix) -RUBYLIBDIR = $(sitelibdir)$(target_prefix) -RUBYARCHDIR = $(sitearchdir)$(target_prefix) - -TARGET_SO = $(DLLIB) -CLEANLIBS = $(TARGET).so $(TARGET).il? $(TARGET).tds $(TARGET).map -CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak - -all: $(DLLIB) -static: $(STATIC_LIB) - -clean: - @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) - -distclean: clean - @-$(RM) Makefile extconf.h conftest.* mkmf.log - @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES) - -realclean: distclean -install: install-so install-rb - -install-so: $(RUBYARCHDIR) -install-so: $(RUBYARCHDIR)/$(DLLIB) -$(RUBYARCHDIR)/$(DLLIB): $(DLLIB) - $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) -install-rb: pre-install-rb install-rb-default -install-rb-default: pre-install-rb-default -pre-install-rb pre-install-rb-default: $(RUBYLIBDIR) -$(RUBYARCHDIR): - $(MAKEDIRS) $@ -$(RUBYLIBDIR): - $(MAKEDIRS) $@ - -site-install: site-install-so site-install-rb -site-install-so: install-so -site-install-rb: install-rb - -.SUFFIXES: .c .m .cc .cxx .cpp .C .o - -.cc.o: - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< - -.cxx.o: - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< - -.cpp.o: - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< - -.C.o: - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< - -.c.o: - $(CC) $(CFLAGS) $(CPPFLAGS) -c $< - -$(DLLIB): $(OBJS) - @-$(RM) $@ - $(LDSHARED) $(DLDFLAGS) $(LIBPATH) -o $@ $(OBJS) $(LOCAL_LIBS) $(LIBS) - - - -$(OBJS): ruby.h defines.h diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..72026aa --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +require 'bundler/gem_tasks' +require 'rake/extensiontask' + +gemspec = Gem::Specification.load('priority_queue.gemspec') + +Rake::ExtensionTask.new('priority_queue', gemspec) diff --git a/compare_comments.rb b/compare_comments.rb deleted file mode 100644 index 4b0ee0e..0000000 --- a/compare_comments.rb +++ /dev/null @@ -1,49 +0,0 @@ -c_file = File.read("ext/priority_queue/CPriorityQueue/priority_queue.c") -rb_file = File.read("lib/priority_queue/ruby_priority_queue.rb") - -c_comments = Hash.new { "" } - -c_file.scan(%r(/\*(.*?)\*/\s*static\s+\w+\s*pq_(\w+)\(.*?\))m).each do | match | - c_comments[match[1]] = match[0].gsub(%r(\n\s*\* {0,1})m, "\n").strip -end - -rb_comments = Hash.new { "" } - -rb_file.scan(%r(((?:\n\s*#[^\n]*)*)\s*def\s+(\w+))m).each do | match | - rb_comments[match[1]] = match[0].gsub(%r(\n\s*# {0,1})m, "\n").strip -end - -add_comments = Hash.new - -(rb_comments.keys + c_comments.keys).uniq.each do | key | - #next if rb_comments[key].gsub(/\s+/m, " ") == c_comments[key].gsub(/\s+/m, " ") - if c_comments[key].empty? - add_comments[key] = rb_comments[key] - elsif rb_comments[key].empty? - add_comments[key] = c_comments[key] - elsif rb_comments[key] != c_comments[key] - - puts key - puts "Ruby" - puts rb_comments[key] - puts "C" - puts c_comments[key] - puts - puts "Choose [c,r]" - 1 until /^([cr])/ =~ gets - add_comments[key] = ($1 == "c" ? c_comments : rb_comments)[key] - puts "-" * 80 - puts - else - add_comments[key] = rb_comments[key] - end - -end - -File.open("lib/priority_queue/ruby_priority_queue.new.rb", "wb") do | o | - o << - rb_file.gsub(%r(((?:\n\s*#[^\n]*)*)(\s*def\s+(\w+)))m) do | match | - name, all = $3, $2 - "\n" + (add_comments[name].gsub(/^/, "#")) + all - end -end diff --git a/ext/priority_queue/CPriorityQueue/extconf.rb b/ext/priority_queue/CPriorityQueue/extconf.rb deleted file mode 100644 index 700f3c0..0000000 --- a/ext/priority_queue/CPriorityQueue/extconf.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'mkmf' -create_makefile("CPriorityQueue") diff --git a/ext/priority_queue/extconf.rb b/ext/priority_queue/extconf.rb new file mode 100644 index 0000000..cc700bf --- /dev/null +++ b/ext/priority_queue/extconf.rb @@ -0,0 +1,2 @@ +require 'mkmf' +create_makefile('priority_queue') diff --git a/ext/priority_queue/CPriorityQueue/priority_queue.c b/ext/priority_queue/priority_queue.c similarity index 95% rename from ext/priority_queue/CPriorityQueue/priority_queue.c rename to ext/priority_queue/priority_queue.c index 0c95bd4..07d0f4c 100644 --- a/ext/priority_queue/CPriorityQueue/priority_queue.c +++ b/ext/priority_queue/priority_queue.c @@ -2,15 +2,15 @@ * :main:CPriorityQueue * * Ruby extension implementing a priority queue - * + * * This is a fibonacci heap priority queue implementation. * * (c) 2005 Brian Schröder - * + * * Please submit bugreports to priority_queue@brian-schroeder.de * * This extension is under the same license as ruby. - * + * * Do not hold me reliable for anything that happens to you, your programs or * anything else because of this extension. It worked for me, but there is no * guarantee it will work for you. @@ -63,7 +63,7 @@ priority_node* create_priority_node(VALUE object, VALUE priority) { result->child = NULL; result->left = result; result->right = result; - result->mark = false; + result->mark = false; return result; } @@ -87,7 +87,7 @@ void priority_node_free_recursively(priority_node* n) { } // link two binomial heaps -static +static priority_node* link_nodes(priority_queue* q, priority_node* b1, priority_node* b2) { if (q->compare_function(b2->priority, b1->priority) < 0) return link_nodes(q, b2, b1); @@ -156,7 +156,7 @@ priority_queue* meld_queue(priority_queue* q1, priority_node* q2, unsigned int l q1->length = length_q2; } else { priority_node* r1 = q1->rootlist->left; - priority_node* r2 = q2->left; + priority_node* r2 = q2->left; q1->rootlist->left = r2; r2->right = q1->rootlist; @@ -184,8 +184,8 @@ priority_node* priority_queue_add_node(priority_queue* q, VALUE object, VALUE pr } // Does not change length -static -priority_node* delete_first(priority_queue* const q) { +static +priority_node* delete_first(priority_queue* const q) { if (q->rootlist) { priority_node* result = q->rootlist; if (result == result->right) @@ -209,7 +209,7 @@ void assert_pointers_correct(priority_node* n) { priority_node *n1 = n->right; while(n != n1) { - if (n1->child && (n1 != n1->child->parent)) + if (n1->child && (n1 != n1->child->parent)) printf("Eltern-Kind Zeiger inkorrekt: %p\n", n); if (n1 != n1->right->left) @@ -224,7 +224,7 @@ void assert_pointers_correct(priority_node* n) { } // Consolidate a queue in amortized O(log n) -static +static void consolidate_queue(priority_queue* const q) { unsigned int array_size = 2 * log(q->length) / log(2) + 1; priority_node* tree_by_degree[array_size]; @@ -242,10 +242,10 @@ void consolidate_queue(priority_queue* const q) { tree_by_degree[n->degree] = n; } - // Find minimum value in O(log n) + // Find minimum value in O(log n) q->rootlist = NULL; q->min = NULL; - for (i=0; iparent) - return q; + return q; n->parent->degree--; if (n->parent->child == n) { if (n->right == n) n->parent->child = NULL; else - n->parent->child = n->right; + n->parent->child = n->right; } n->parent = NULL; n->right->left = n->left; @@ -359,7 +359,7 @@ priority_queue* priority_queue_delete(priority_queue* q, priority_node* n) { priority_node* n1 = n->right; q->min = n1; do { - if (q->compare_function(n1->priority, q->min->priority) <= 0) + if (q->compare_function(n1->priority, q->min->priority) <= 0) q->min = n1; n1 = n1->right; } while(n1 != n); @@ -380,7 +380,7 @@ priority_queue* priority_queue_change_priority(priority_queue* q, priority_node* n->priority = priority; meld_queue(q, n, 1); return q; - } + } n->priority = priority; if (q->compare_function(n->priority, q->min->priority) < 0) q->min = n; @@ -406,7 +406,7 @@ _Bool priority_queue_empty(priority_queue *q) { } // change the priority of a priority_node and restructure the queue -priority_queue* priority_queue_each_node(priority_queue* q, priority_node* n, +priority_queue* priority_queue_each_node(priority_queue* q, priority_node* n, void (*each)(priority_queue* q_, priority_node* n_, void* args), void* arguments) { priority_node* end = n; do { @@ -421,7 +421,7 @@ priority_queue* priority_queue_each_node(priority_queue* q, priority_node* n, return q; } -priority_queue* priority_queue_each(priority_queue* q, +priority_queue* priority_queue_each(priority_queue* q, void (*each)(priority_queue* q, priority_node* n, void* args), void* arguments) { if (q->rootlist) priority_queue_each_node(q, q->rootlist, each, arguments); @@ -436,7 +436,7 @@ static int id_format; static int id_display; priority_queue* get_pq_from_value(VALUE self) { - priority_queue *q; + priority_queue *q; Data_Get_Struct(self, priority_queue, q); return q; } @@ -465,7 +465,7 @@ void pq_mark_recursive(priority_node* n) { } static -void pq_mark(void *q) { +void pq_mark(void *q) { priority_node* n1 = ((priority_queue*) q)->rootlist; if (!n1) return; @@ -476,7 +476,7 @@ void pq_mark(void *q) { } while (n1 != n2); } -static +static VALUE pq_alloc(VALUE klass) { priority_queue *q; VALUE object; @@ -516,7 +516,7 @@ VALUE pq_push(VALUE self, VALUE object, VALUE priority) { /* call-seq: * min -> [object, priority] - * + * * Return the pair [object, priority] with minimal priority or nil when the * queue is empty. * @@ -542,7 +542,7 @@ VALUE pq_min(VALUE self) { /* call-seq: * min_key -> object - * + * * Return the key that has the minimal priority or nil when the queue is empty. * * q = PriorityQueue.new @@ -592,7 +592,7 @@ VALUE pq_min_priority(VALUE self) { /* call-seq: * delete_min -> [key, priority] - * + * * Delete key with minimal priority and return [key, priority] * * q = PriorityQueue.new @@ -611,7 +611,7 @@ VALUE pq_delete_min(VALUE self) { if (n) { rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object? - return rb_ary_new3(2, n->object, n->priority); + return rb_ary_new3(2, n->object, n->priority); } else { return Qnil; } @@ -619,7 +619,7 @@ VALUE pq_delete_min(VALUE self) { /* call-seq: * delete_min_return_key -> key - * + * * Delete key with minimal priority and return the key * * q = PriorityQueue.new @@ -638,7 +638,7 @@ VALUE pq_delete_min_return_key(VALUE self) { if (n) { rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object? - return n->object; + return n->object; } else { return Qnil; } @@ -666,7 +666,7 @@ VALUE pq_delete_min_return_priority(VALUE self) { if (n) { rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object? - return n->priority; + return n->priority; } else { return Qnil; } @@ -719,7 +719,7 @@ VALUE pq_get_priority(VALUE self, VALUE object) { VALUE hash = rb_iv_get(self, "@__node_by_object__"); VALUE node_pointer = rb_hash_aref(hash, object); - + if (NIL_P(node_pointer)) return Qnil; else @@ -729,12 +729,12 @@ VALUE pq_get_priority(VALUE self, VALUE object) { /* * call-seq: * has_key? key -> boolean - * + * * Return false if the key is not in the queue, true otherwise. * * q = PriorityQueue.new * (0..10).each do | i | q[i.to_s] = i end - * q.has_key("5") #=> true + * q.has_key("5") #=> true * q.has_key(5) #=> false */ static @@ -742,14 +742,14 @@ VALUE pq_has_key(VALUE self, VALUE object) { VALUE hash = rb_iv_get(self, "@__node_by_object__"); VALUE node_pointer = rb_hash_aref(hash, object); - + return NIL_P(node_pointer) ? Qfalse : Qtrue; } /* call-seq: * length -> Fixnum - * + * * Returns the number of elements of the queue. - * + * * q = PriorityQueue.new * q.length #=> 0 * q[0] = 1 @@ -765,10 +765,10 @@ VALUE pq_length(VALUE self) { /* call-seq: * delete(key) -> [key, priority] * delete(key) -> nil - * + * * Delete a key from the priority queue. Returns nil when the key was not in * the queue and [key, priority] otherwise. - * + * * q = PriorityQueue.new * (0..10).each do | i | q[i.to_s] = i end * q.delete(5) #=> ["5", 5] @@ -780,8 +780,8 @@ VALUE pq_delete(VALUE self, VALUE object) { VALUE hash = rb_iv_get(self, "@__node_by_object__"); - VALUE node_pointer = rb_hash_aref(hash, object); - + VALUE node_pointer = rb_hash_aref(hash, object); + if (NIL_P(node_pointer)) return Qnil; else { @@ -800,34 +800,34 @@ VALUE pq_delete(VALUE self, VALUE object) { // (I'm not proud of this function ;-( ) static void pq_node2dot(VALUE result_string, priority_node* n, unsigned int level) { - if (n == NULL) return; + if (n == NULL) return; unsigned int i; - for (i=0; imark) rb_str_concat(result_string, - rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i [label=\"%s (%s)\"];\n"), + rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i [label=\"%s (%s)\"];\n"), ULONG2NUM((unsigned long) n), n->object, n->priority)); else rb_str_concat(result_string, - rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i [label=\"%s (%s)\",shape=box];\n"), + rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i [label=\"%s (%s)\",shape=box];\n"), ULONG2NUM((unsigned long) n), n->object, n->priority)); if (n->child != NULL) { priority_node* n1 = n->child; do { pq_node2dot(result_string, n1, level + 1); - for (i=0; i NODE%i;\n"), + rb_funcall(Qnil, id_format, 3, rb_str_new2("NODE%i -> NODE%i;\n"), ULONG2NUM((unsigned long) n), ULONG2NUM((unsigned long) n1))); n1 = n1->right; } while(n1 != n->child); } } -/* +/* * Print a priority queue as a dot-graph. The output can be fed to dot from the * vizgraph suite to create a tree depicting the internal datastructure. - * + * * (I'm not proud of this function ;-( ) */ static @@ -837,7 +837,7 @@ VALUE pq_to_dot(VALUE self) { VALUE result_string = rb_str_new2("digraph fibonacci_heap {\n"); if (q->rootlist) { priority_node* n1 = q->rootlist; - do { + do { pq_node2dot(result_string, n1, 1); n1 = n1->right; } while(n1 != q->rootlist); @@ -884,7 +884,7 @@ VALUE pq_initialize_copy(VALUE copy, VALUE orig) { return copy; rb_iterate(rb_each, orig, pq_insert_node, copy); - + return copy; } @@ -892,7 +892,7 @@ VALUE pq_initialize_copy(VALUE copy, VALUE orig) { * Returns a string representation of the priority queue. */ static -VALUE pq_inspect(VALUE self) { +VALUE pq_inspect(VALUE self) { VALUE result = rb_str_new2(""); id_format = rb_intern("format"); id_display = rb_intern("display"); - cPriorityQueue = rb_define_class("CPriorityQueue", rb_cObject); + cPriorityQueue = rb_define_class("PriorityQueue", rb_cObject); rb_define_alloc_func(cPriorityQueue, pq_alloc); rb_define_method(cPriorityQueue, "initialize", pq_init, 0); diff --git a/lib/priority_queue.rb b/lib/priority_queue.rb index 21db8a5..3291e3e 100644 --- a/lib/priority_queue.rb +++ b/lib/priority_queue.rb @@ -1,14 +1,3 @@ # A priority queue implementation. -# This extension contains two implementations, a c extension and a pure ruby -# implementation. When the compiled extension can not be found, it falls back -# to the pure ruby extension. -# -# See CPriorityQueue and RubyPriorityQueue for more information. -begin - require 'priority_queue/CPriorityQueue' - PriorityQueue = CPriorityQueue -rescue LoadError # C Version could not be found, try ruby version - require 'priority_queue/ruby_priority_queue' - PriorityQueue = RubyPriorityQueue -end +require 'priority_queue/priority_queue' diff --git a/lib/priority_queue/c_priority_queue.rb b/lib/priority_queue/c_priority_queue.rb deleted file mode 100644 index 0b9fd93..0000000 --- a/lib/priority_queue/c_priority_queue.rb +++ /dev/null @@ -1 +0,0 @@ -require "priority_queue/CPriorityQueue" diff --git a/lib/priority_queue/poor_priority_queue.rb b/lib/priority_queue/poor_priority_queue.rb deleted file mode 100644 index 982fabf..0000000 --- a/lib/priority_queue/poor_priority_queue.rb +++ /dev/null @@ -1,46 +0,0 @@ -# A Poor mans Priority Queue. (Very inefficent but minimal implemention). -class PoorPriorityQueue < Hash - def push(object, priority) - self[object] = priority - end - - def min - return nil if self.empty? - min_k = self.keys.first - min_p = self[min_k] - self.each do | k, p | - min_k, min_p = k, p if p < min_p - end - [min_k, min_p] - end - - def min_key - min[0] rescue nil - end - - def min_priority - min[1] rescue nil - end - - def delete_min - return nil if self.empty? - min_k, min_p = *min - self.delete(min_k) - [min_k, min_p] - end - - def delete_min_return_key - delete_min[0] rescue nil - end - - def delete_min_return_priority - delete_min[1] rescue nil - end - - def delete(object) - return nil unless self.has_key?(object) - result = [object, self[object]] - super - result - end -end diff --git a/lib/priority_queue/ruby_priority_queue.rb b/lib/priority_queue/ruby_priority_queue.rb deleted file mode 100644 index 5166ba2..0000000 --- a/lib/priority_queue/ruby_priority_queue.rb +++ /dev/null @@ -1,526 +0,0 @@ -# Pure ruby Priority Queue -class RubyPriorityQueue - - include Enumerable - - private - - # - def link_nodes(b1, b2) - return link_nodes(b2, b1) if b2.priority < b1.priority - - b2.parent = b1 - child = b1.child - b1.child = b2 - if child - b2.left = child.left - b2.left.right = b2 - b2.right = child - child.left = b2 - else - b2.left = b2 - b2.right = b2 - end - b1.degree += 1 - b2.mark = false # TODO: Check if this is correct, or if b1 should be marked as false - return b1 - end - - # Does not change length - def delete_first - return nil unless @rootlist - - result = @rootlist - if result == result.right - @min = @rootlist = nil - else - @rootlist = result.right - @rootlist.left = result.left - @rootlist.left.right = @rootlist - - result.right = result.left = result - end - return result; - end - - def cut_node(n) - return self unless n.parent - n.parent.degree -= 1 - if n.parent.child == n - if n.right == n - n.parent.child = nil - else - n.parent.child = n.right; - end - end - n.parent = nil - n.right.left = n.left - n.left.right = n.right - - n.right = @rootlist - n.left = @rootlist.left - @rootlist.left.right = n - @rootlist.left = n - - n.mark = false - - return self - end - - # Does not change length - def insert_tree(tree) - if @rootlist == nil - @rootlist = @min = tree - else - l = @rootlist.left - l.right = tree - @rootlist.left = tree - tree.left = l - tree.right = @rootlist - @min = tree if tree.priority < @min.priority - end - self - end - - def consolidate - return self if self.empty? - array_size = (2.0 * Math.log(self.length) / Math.log(2) + 1.0).ceil - tree_by_degree = Array.new(array_size) - - while n = delete_first - while n1 = tree_by_degree[n.degree] - tree_by_degree[n.degree] = nil; - n = link_nodes(n, n1); - end - tree_by_degree[n.degree] = n; - end - - @rootlist = @min = nil; - tree_by_degree.each do | tree | - next unless tree - insert_tree(tree) - end - self - end - - # Node class used internally - class Node # :nodoc: - attr_accessor :parent, :left, :right, :key, :priority, :degree, :mark - attr_reader :child - - def child=(c) - raise "Circular Child" if c == self - raise "Child is neighbour" if c == self.right - raise "Child is neighbour" if c == self.left - @child = c - end - - def to_dot(only_down = false, known_nodes = []) - p known_nodes.map { | n | n.dot_id } - p self.dot_id - result = [] - if only_down - raise "Circular #{caller.inspect}" if known_nodes.include?(self) - known_nodes << self - - result << "#{dot_id} [label=\"#{@key}: #{@priority}\"];" - l = " " - #l << "#{@left.dot_id} <- #{dot_id}; " if @left - l << "#{dot_id} -> #{@left.dot_id} [constraint=false]; " if @left and @left.dot_id < self.dot_id - l << "#{dot_id} -> #{@right.dot_id} [constraint=false];\t\t\t\t/*neighbours*/" if @right and @right.dot_id <= self.dot_id - result << l - result << " #{dot_id} -> #{@child.dot_id}; //child" if @child - result << @child.to_dot(false, known_nodes) if @child - else - n = self - begin - result.concat(n.to_dot(true, known_nodes)) - n = n.right - end while n != self - end - result.flatten.map{|r| " " << r} - end - - def dot_id - "N#{@key}" - end - - def initialize(key, priority) - @key = key; @priority = priority; @degree = 0 - end - end - - public - - # Returns the number of elements of the queue. - # - # q = PriorityQueue.new - # q.length #=> 0 - # q[0] = 1 - # q.length #=> 1 - attr_reader :length - - # Create a new, empty PriorityQueue - def initialize - @nodes = Hash.new - @rootlist = nil - @min = nil - @length = 0 - end - - # Print a priority queue as a dot-graph. The output can be fed to dot from the - # vizgraph suite to create a tree depicting the internal datastructure. - def to_dot - r = ["digraph fibheap {"] - #r << @rootlist.to_dot.join("\n") if @rootlist - r << "ROOT -> #{@rootlist.dot_id};" if @rootlist - @nodes.to_a.sort.each do | (_, n) | - r << " #{n.dot_id} [label=\"#{n.key}: #{n.priority}\"];" - r << " #{n.dot_id} -> #{n.right.dot_id} [constraint=false];" if n.right# and n.dot_id < n.right.dot_id - r << " #{n.dot_id} -> #{n.left.dot_id} [constraint=false];" if n.left #and n.dot_id < n.left.dot_id - r << " #{n.dot_id} -> #{n.child.dot_id}" if n.child - end - r << "}" - r.join("\n") - r - end - - # Call dot and gv displaying the datstructure - def display_dot - puts to_dot - system "echo '#{to_dot}' | twopi -Tps -Groot=ROOT -Goverlap=false> /tmp/dotfile.ps; gv /tmp/dotfile.ps" - end - - # call-seq: - # [key] = priority - # change_priority(key, priority) - # push(key, priority) - # - # Set the priority of a key. - # - # q = PriorityQueue.new - # q["car"] = 50 - # q["train"] = 50 - # q["bike"] = 10 - # q.min #=> ["bike", 10] - # q["car"] = 0 - # q.min #=> ["car", 0] - def change_priority(key, priority) - return push(key, priority) unless @nodes[key] - - n = @nodes[key] - if n.priority < priority # Priority was increased. Remove the node and reinsert. - self.delete(key) - self.push(key, priority); - return self - end - n.priority = priority; - @min = n if n.priority < @min.priority - - return self if !n.parent or n.parent.priority <= n.priority # Already in rootlist or bigger than parent - begin # Cascading Cuts - p = n.parent - cut_node(n) - n = p - end while n.mark and n.parent - n.mark = true if n.parent - - self - end - - # Add an object to the queue. - def push(key, priority) - return change_priority(key, priority) if @nodes[key] - @nodes[key] = node = Node.new(key, priority) - @min = node if !@min or priority < @min.priority - if not @rootlist - @rootlist = node - node.left = node.right = node - else - node.left = @rootlist.left - node.right = @rootlist - @rootlist.left.right = node - @rootlist.left = node - end - @length += 1 - self - end - - # Returns true if the array is empty, false otherwise. - def empty? - @rootlist.nil? - end - - # call-seq: - # [key] -> priority - # - # Return the priority of a key or nil if the key is not in the queue. - # - # q = PriorityQueue.new - # (0..10).each do | i | q[i.to_s] = i end - # q["5"] #=> 5 - # q[5] #=> nil - def [](key) - @nodes[key] and @nodes[key].priority - end - - # call-seq: - # has_key? key -> boolean - # - # Return false if the key is not in the queue, true otherwise. - # - # q = PriorityQueue.new - # (0..10).each do | i | q[i.to_s] = i end - # q.has_key("5") #=> true - # q.has_key(5) #=> false - def has_key?(key) - @nodes.has_key?(key) - end - - alias :[]= :push - - # Call the given block with each [key, priority] pair in the queue - # - # Beware: Changing the queue in the block may lead to unwanted behaviour and - # even infinite loops. - def each - @nodes.each do | key, node | - yield(key, node.priority) - end - end - - # call-seq: - # min -> [object, priority] - # - # Return the pair [object, priority] with minimal priority or nil when the - # queue is empty. - # - # q = PriorityQueue.new - # q["a"] = 10 - # q["b"] = 20 - # q.min #=> ["a", 10] - # q.delete_min #=> ["a", 10] - # q.min #=> ["b", 20] - # q.delete_min #=> ["b", 20] - # q.min #=> nil - def min - [@min.key, @min.priority] rescue nil - end - - # call-seq: - # min_key -> object - # - # Return the key that has the minimal priority or nil when the queue is empty. - # - # q = PriorityQueue.new - # q["a"] = 10 - # q["b"] = 20 - # q.min_key #=> "a" - # q.delete_min #=> ["a", 10] - # q.min_key #=> "b" - # q.delete_min #=> ["b", 20] - # q.min_key #=> nil - def min_key - @min.key rescue nil - end - - # call-seq: - # min_priority -> priority - # - # Return the minimal priority or nil when the queue is empty. - # - # q = PriorityQueue.new - # q["a"] = 10 - # q["b"] = 20 - # q.min_priority #=> 10 - # q.delete_min #=> ["a", 10] - # q.min_priority #=> 20 - # q.delete_min #=> ["b", 20] - # q.min_priority #=> nil - def min_priority - @min.priority rescue nil - end - - # call-seq: - # delete(key) -> [key, priority] - # delete(key) -> nil - # - # Delete a key from the priority queue. Returns nil when the key was not in - # the queue and [key, priority] otherwise. - # - # q = PriorityQueue.new - # (0..10).each do | i | q[i.to_s] = i end - # q.delete(5) #=> ["5", 5] - # q.delete(5) #=> nil - def delete(key) - return nil unless n = @nodes.delete(key) - - if n.child - c = n.child - e = n.child - begin - r = c.right - cut_node(c) - c = r - end while c != e - end - cut_node(n) if n.parent - - if n == n.right - @min = nil; - @rootlist = nil; - else - @rootlist = n.right if @rootlist == n - if @min == n - n1 = n.right - @min = n1 - begin - @min = n1 if n1.priority < @min.priority - n1 = n1.right - end while(n1 != n); - end - n.right.left = n.left - n.left.right = n.right - n.left = n - n.right = n - end - @length -= 1 - return [n.key, n.priority] - end - - # call-seq: - # delete_min_return_key -> key - # - # Delete key with minimal priority and return the key - # - # q = PriorityQueue.new - # q["a"] = 1 - # q["b"] = 0 - # q.delete_min_return_key #=> "b" - # q.delete_min_return_key #=> "a" - # q.delete_min_return_key #=> nil - def delete_min_return_key - delete_min[0] rescue nil - end - - # call-seq: - # delete_min_return_priority -> priority - # - # Delete key with minimal priority and return the priority value - # - # q = PriorityQueue.new - # q["a"] = 1 - # q["b"] = 0 - # q.delete_min_return_priority #=> 0 - # q.delete_min_return_priority #=> 1 - # q.delete_min_return_priority #=> nil - def delete_min_return_priority - delete_min[1] rescue nil - end - - # call-seq: - # delete_min -> [key, priority] - # - # Delete key with minimal priority and return [key, priority] - # - # q = PriorityQueue.new - # q["a"] = 1 - # q["b"] = 0 - # q.delete_min #=> ["b", 0] - # q.delete_min #=> ["a", 1] - # q.delete_min #=> nil - def delete_min - return nil if self.empty? - result = self.min - - @nodes.delete(@min.key) - - if @length == 1 - @rootlist = @min = nil - @length = 0 - else - min = @min - if @min == @rootlist # If the rootlist is anchored at the minimum, shift to the right - if @rootlist == @rootlist.right - @rootlist = @min = nil - else - @rootlist = @min = @min.right - end - end - min.left.right = min.right; - min.right.left = min.left; - min.left = min.right = min; - if min.child - # Kinder und Eltern trennen, Markierung aufheben - n = min.child; - begin - n.parent = nil; - n.mark = false; - n = n.right; - end while n != min.child - - # Kinder einfügen - if @rootlist - l1 = @rootlist.left - l2 = n.left - - l1.right = n - n.left = l1 - l2.right = @rootlist - @rootlist.left = l2 - else - @rootlist = n - end - end - - # Größe anpassen - @length -= 1 - - # Wieder aufhübschen - consolidate - end - - result - end - - # Returns a string representation of the priority queue. - def inspect - "" - end - - def initialize_copy(copy) - copy_nodes = @nodes - @nodes = {} - - copy_nodes.each do | (_, cn) | - n = @nodes[cn.key] = Node.new(cn.key, cn.priority) - n.mark = cn.mark - n.degree = cn.degree - end - - copy_nodes.each do | (_, cn) | - n = @nodes[cn.key] - n.left = @nodes[cn.left.key] if cn.left - n.right = @nodes[cn.right.key] if cn.right - n.parent = @nodes[cn.parent.key] if cn.parent - n.child = @nodes[cn.child.key] if cn.child - end - @rootlist = @nodes[@rootlist.key] if @rootlist - @min = @nodes[@min.key] if @min - self - end -end - -if __FILE__ == $0 - q = RubyPriorityQueue.new - - ('a'..'z').each do | n | - q[n] = n[0] - end - q.delete_min - q.delete_min - q.delete_min - q.delete_min - q.display_dot - q.delete_min -end diff --git a/priority_queue.gemspec b/priority_queue.gemspec new file mode 100644 index 0000000..096a613 --- /dev/null +++ b/priority_queue.gemspec @@ -0,0 +1,20 @@ +Gem::Specification.new do |spec| + spec.name = 'priority_queue' + spec.version = '0.2.0' + spec.authors = ['Brian Schröder'] + spec.email = ['priority_queue@brian-schroeder.de'] + + spec.summary = 'This is a fibonacci-heap priority-queue implementation.' + spec.description = 'This is a fibonacci-heap priority-queue implementation.' + spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0") + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do + `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + end + spec.require_paths = ['lib'] + spec.add_development_dependency 'rake', '>= 1.9.1' + spec.add_development_dependency 'rake-compiler', '>= 0.8.3' + spec.extensions = %w[ext/priority_queue/extconf.rb] +end diff --git a/priority_queue.so b/priority_queue.so deleted file mode 100755 index 53aeed4fc009db9169bb64274014d6fec1e0065f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22530 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%0D4>ZdEj0K{#sQgm1njg>+9_#@ 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 1.4.4? - newpath_p = ((major >= 2) or - ((major == 1) and - ((minor >= 5) or - ((minor == 4) and (teeny >= 4))))) - - if c['rubylibdir'] - # V > 1.6.3 - libruby = "#{c['prefix']}/lib/ruby" - librubyver = c['rubylibdir'] - librubyverarch = c['archdir'] - siteruby = c['sitedir'] - siterubyver = c['sitelibdir'] - siterubyverarch = c['sitearchdir'] - elsif newpath_p - # 1.4.4 <= V <= 1.6.3 - libruby = "#{c['prefix']}/lib/ruby" - librubyver = "#{c['prefix']}/lib/ruby/#{version}" - librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" - siteruby = c['sitedir'] - siterubyver = "$siteruby/#{version}" - siterubyverarch = "$siterubyver/#{c['arch']}" - else - # V < 1.4.4 - libruby = "#{c['prefix']}/lib/ruby" - librubyver = "#{c['prefix']}/lib/ruby/#{version}" - librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" - siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" - siterubyver = siteruby - siterubyverarch = "$siterubyver/#{c['arch']}" - end - parameterize = lambda {|path| - path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') - } - - if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } - makeprog = arg.sub(/'/, '').split(/=/, 2)[1] - else - makeprog = 'make' - end - - [ - ExecItem.new('installdirs', 'std/site/home', - 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ - {|val, table| - case val - when 'std' - table['rbdir'] = '$librubyver' - table['sodir'] = '$librubyverarch' - when 'site' - table['rbdir'] = '$siterubyver' - table['sodir'] = '$siterubyverarch' - when 'home' - setup_rb_error '$HOME was not set' unless ENV['HOME'] - table['prefix'] = ENV['HOME'] - table['rbdir'] = '$libdir/ruby' - table['sodir'] = '$libdir/ruby' - end - }, - PathItem.new('prefix', 'path', c['prefix'], - 'path prefix of target environment'), - PathItem.new('bindir', 'path', parameterize.call(c['bindir']), - 'the directory for commands'), - PathItem.new('libdir', 'path', parameterize.call(c['libdir']), - 'the directory for libraries'), - PathItem.new('datadir', 'path', parameterize.call(c['datadir']), - 'the directory for shared data'), - PathItem.new('mandir', 'path', parameterize.call(c['mandir']), - 'the directory for man pages'), - PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), - 'the directory for system configuration files'), - PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), - 'the directory for local state data'), - PathItem.new('libruby', 'path', libruby, - 'the directory for ruby libraries'), - PathItem.new('librubyver', 'path', librubyver, - 'the directory for standard ruby libraries'), - PathItem.new('librubyverarch', 'path', librubyverarch, - 'the directory for standard ruby extensions'), - PathItem.new('siteruby', 'path', siteruby, - 'the directory for version-independent aux ruby libraries'), - PathItem.new('siterubyver', 'path', siterubyver, - 'the directory for aux ruby libraries'), - PathItem.new('siterubyverarch', 'path', siterubyverarch, - 'the directory for aux ruby binaries'), - PathItem.new('rbdir', 'path', '$siterubyver', - 'the directory for ruby scripts'), - PathItem.new('sodir', 'path', '$siterubyverarch', - 'the directory for ruby extentions'), - PathItem.new('rubypath', 'path', rubypath, - 'the path to set to #! line'), - ProgramItem.new('rubyprog', 'name', rubypath, - 'the ruby program using for installation'), - ProgramItem.new('makeprog', 'name', makeprog, - 'the make program to compile ruby extentions'), - SelectItem.new('shebang', 'all/ruby/never', 'ruby', - 'shebang line (#!) editing mode'), - BoolItem.new('without-ext', 'yes/no', 'no', - 'does not compile/install ruby extentions') - ] - end - private :standard_entries - - def load_multipackage_entries - multipackage_entries().each do |ent| - add ent - end - end - - def multipackage_entries - [ - PackageSelectionItem.new('with', 'name,name...', '', 'ALL', - 'package names that you want to install'), - PackageSelectionItem.new('without', 'name,name...', '', 'NONE', - 'package names that you do not want to install') - ] - end - private :multipackage_entries - - ALIASES = { - 'std-ruby' => 'librubyver', - 'stdruby' => 'librubyver', - 'rubylibdir' => 'librubyver', - 'archdir' => 'librubyverarch', - 'site-ruby-common' => 'siteruby', # For backward compatibility - 'site-ruby' => 'siterubyver', # For backward compatibility - 'bin-dir' => 'bindir', - 'bin-dir' => 'bindir', - 'rb-dir' => 'rbdir', - 'so-dir' => 'sodir', - 'data-dir' => 'datadir', - 'ruby-path' => 'rubypath', - 'ruby-prog' => 'rubyprog', - 'ruby' => 'rubyprog', - 'make-prog' => 'makeprog', - 'make' => 'makeprog' - } - - def fixup - ALIASES.each do |ali, name| - @table[ali] = @table[name] - end - @items.freeze - @table.freeze - @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ - end - - def parse_opt(opt) - m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" - m.to_a[1,2] - end - - def dllext - @rbconfig['DLEXT'] - end - - def value_config?(name) - lookup(name).value? - end - - class Item - def initialize(name, template, default, desc) - @name = name.freeze - @template = template - @value = default - @default = default - @description = desc - end - - attr_reader :name - attr_reader :description - - attr_accessor :default - alias help_default default - - def help_opt - "--#{@name}=#{@template}" - end - - def value? - true - end - - def value - @value - end - - def resolve(table) - @value.gsub(%r<\$([^/]+)>) { table[$1] } - end - - def set(val) - @value = check(val) - end - - private - - def check(val) - setup_rb_error "config: --#{name} requires argument" unless val - val - end - end - - class BoolItem < Item - def config_type - 'bool' - end - - def help_opt - "--#{@name}" - end - - private - - def check(val) - return 'yes' unless val - unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val - setup_rb_error "config: --#{@name} accepts only yes/no for argument" - end - (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no' - end - end - - class PathItem < Item - def config_type - 'path' - end - - private - - def check(path) - setup_rb_error "config: --#{@name} requires argument" unless path - path[0,1] == '$' ? path : File.expand_path(path) - end - end - - class ProgramItem < Item - def config_type - 'program' - end - end - - class SelectItem < Item - def initialize(name, selection, default, desc) - super - @ok = selection.split('/') - end - - def config_type - 'select' - end - - private - - def check(val) - unless @ok.include?(val.strip) - setup_rb_error "config: use --#{@name}=#{@template} (#{val})" - end - val.strip - end - end - - class ExecItem < Item - def initialize(name, selection, desc, &block) - super name, selection, nil, desc - @ok = selection.split('/') - @action = block - end - - def config_type - 'exec' - end - - def value? - false - end - - def resolve(table) - setup_rb_error "$#{name()} wrongly used as option value" - end - - undef set - - def evaluate(val, table) - v = val.strip.downcase - unless @ok.include?(v) - setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" - end - @action.call v, table - end - end - - class PackageSelectionItem < Item - def initialize(name, template, default, help_default, desc) - super name, template, default, desc - @help_default = help_default - end - - attr_reader :help_default - - def config_type - 'package' - end - - private - - def check(val) - unless File.dir?("packages/#{val}") - setup_rb_error "config: no such package: #{val}" - end - val - end - end - - class MetaConfigEnvironment - def intiailize(config, installer) - @config = config - @installer = installer - end - - def config_names - @config.names - end - - def config?(name) - @config.key?(name) - end - - def bool_config?(name) - @config.lookup(name).config_type == 'bool' - end - - def path_config?(name) - @config.lookup(name).config_type == 'path' - end - - def value_config?(name) - @config.lookup(name).config_type != 'exec' - end - - def add_config(item) - @config.add item - end - - def add_bool_config(name, default, desc) - @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) - end - - def add_path_config(name, default, desc) - @config.add PathItem.new(name, 'path', default, desc) - end - - def set_config_default(name, default) - @config.lookup(name).default = default - end - - def remove_config(name) - @config.remove(name) - end - - # For only multipackage - def packages - raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer - @installer.packages - end - - # For only multipackage - def declare_packages(list) - raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer - @installer.packages = list - end - end - -end # class ConfigTable - - -# This module requires: #verbose?, #no_harm? -module FileOperations - - def mkdir_p(dirname, prefix = nil) - dirname = prefix + File.expand_path(dirname) if prefix - $stderr.puts "mkdir -p #{dirname}" if verbose? - return if no_harm? - - # Does not check '/', it's too abnormal. - dirs = File.expand_path(dirname).split(%r<(?=/)>) - if /\A[a-z]:\z/i =~ dirs[0] - disk = dirs.shift - dirs[0] = disk + dirs[0] - end - dirs.each_index do |idx| - path = dirs[0..idx].join('') - Dir.mkdir path unless File.dir?(path) - end - end - - def rm_f(path) - $stderr.puts "rm -f #{path}" if verbose? - return if no_harm? - force_remove_file path - end - - def rm_rf(path) - $stderr.puts "rm -rf #{path}" if verbose? - return if no_harm? - remove_tree path - end - - def remove_tree(path) - if File.symlink?(path) - remove_file path - elsif File.dir?(path) - remove_tree0 path - else - force_remove_file path - end - end - - def remove_tree0(path) - Dir.foreach(path) do |ent| - next if ent == '.' - next if ent == '..' - entpath = "#{path}/#{ent}" - if File.symlink?(entpath) - remove_file entpath - elsif File.dir?(entpath) - remove_tree0 entpath - else - force_remove_file entpath - end - end - begin - Dir.rmdir path - rescue Errno::ENOTEMPTY - # directory may not be empty - end - end - - def move_file(src, dest) - force_remove_file dest - begin - File.rename src, dest - rescue - File.open(dest, 'wb') {|f| - f.write File.binread(src) - } - File.chmod File.stat(src).mode, dest - File.unlink src - end - end - - def force_remove_file(path) - begin - remove_file path - rescue - end - end - - def remove_file(path) - File.chmod 0777, path - File.unlink path - end - - def install(from, dest, mode, prefix = nil) - $stderr.puts "install #{from} #{dest}" if verbose? - return if no_harm? - - realdest = prefix ? prefix + File.expand_path(dest) : dest - realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) - str = File.binread(from) - if diff?(str, realdest) - verbose_off { - rm_f realdest if File.exist?(realdest) - } - File.open(realdest, 'wb') {|f| - f.write str - } - File.chmod mode, realdest - - File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| - if prefix - f.puts realdest.sub(prefix, '') - else - f.puts realdest - end - } - end - end - - def diff?(new_content, path) - return true unless File.exist?(path) - new_content != File.binread(path) - end - - def command(*args) - $stderr.puts args.join(' ') if verbose? - system(*args) or raise RuntimeError, - "system(#{args.map{|a| a.inspect }.join(' ')}) failed" - end - - def ruby(*args) - command config('rubyprog'), *args - end - - def make(task = nil) - command(*[config('makeprog'), task].compact) - end - - def extdir?(dir) - File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") - end - - def files_of(dir) - Dir.open(dir) {|d| - return d.select {|ent| File.file?("#{dir}/#{ent}") } - } - end - - DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) - - def directories_of(dir) - Dir.open(dir) {|d| - return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT - } - end - -end - - -# This module requires: #srcdir_root, #objdir_root, #relpath -module HookScriptAPI - - def get_config(key) - @config[key] - end - - alias config get_config - - # obsolete: use metaconfig to change configuration - def set_config(key, val) - @config[key] = val - end - - # - # srcdir/objdir (works only in the package directory) - # - - def curr_srcdir - "#{srcdir_root()}/#{relpath()}" - end - - def curr_objdir - "#{objdir_root()}/#{relpath()}" - end - - def srcfile(path) - "#{curr_srcdir()}/#{path}" - end - - def srcexist?(path) - File.exist?(srcfile(path)) - end - - def srcdirectory?(path) - File.dir?(srcfile(path)) - end - - def srcfile?(path) - File.file?(srcfile(path)) - end - - def srcentries(path = '.') - Dir.open("#{curr_srcdir()}/#{path}") {|d| - return d.to_a - %w(. ..) - } - end - - def srcfiles(path = '.') - srcentries(path).select {|fname| - File.file?(File.join(curr_srcdir(), path, fname)) - } - end - - def srcdirectories(path = '.') - srcentries(path).select {|fname| - File.dir?(File.join(curr_srcdir(), path, fname)) - } - end - -end - - -class ToplevelInstaller - - Version = '3.4.0' - Copyright = 'Copyright (c) 2000-2005 Minero Aoki' - - TASKS = [ - [ 'all', 'do config, setup, then install' ], - [ 'config', 'saves your configurations' ], - [ 'show', 'shows current configuration' ], - [ 'setup', 'compiles ruby extentions and others' ], - [ 'install', 'installs files' ], - [ 'test', 'run all tests in test/' ], - [ 'clean', "does `make clean' for each extention" ], - [ 'distclean',"does `make distclean' for each extention" ] - ] - - def ToplevelInstaller.invoke - config = ConfigTable.new(load_rbconfig()) - config.load_standard_entries - config.load_multipackage_entries if multipackage? - config.fixup - klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) - klass.new(File.dirname($0), config).invoke - end - - def ToplevelInstaller.multipackage? - File.dir?(File.dirname($0) + '/packages') - end - - def ToplevelInstaller.load_rbconfig - if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } - ARGV.delete(arg) - load File.expand_path(arg.split(/=/, 2)[1]) - $".push 'rbconfig.rb' - else - require 'rbconfig' - end - ::Config::CONFIG - end - - def initialize(ardir_root, config) - @ardir = File.expand_path(ardir_root) - @config = config - # cache - @valid_task_re = nil - end - - def config(key) - @config[key] - end - - def inspect - "#<#{self.class} #{__id__()}>" - end - - def invoke - run_metaconfigs - case task = parsearg_global() - when nil, 'all' - parsearg_config - init_installers - exec_config - exec_setup - exec_install - else - case task - when 'config', 'test' - ; - when 'clean', 'distclean' - @config.load_savefile if File.exist?(@config.savefile) - else - @config.load_savefile - end - __send__ "parsearg_#{task}" - init_installers - __send__ "exec_#{task}" - end - end - - def run_metaconfigs - @config.load_script "#{@ardir}/metaconfig" - end - - def init_installers - @installer = Installer.new(@config, @ardir, File.expand_path('.')) - end - - # - # Hook Script API bases - # - - def srcdir_root - @ardir - end - - def objdir_root - '.' - end - - def relpath - '.' - end - - # - # Option Parsing - # - - def parsearg_global - while arg = ARGV.shift - case arg - when /\A\w+\z/ - setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) - return arg - when '-q', '--quiet' - @config.verbose = false - when '--verbose' - @config.verbose = true - when '--help' - print_usage $stdout - exit 0 - when '--version' - puts "#{File.basename($0)} version #{Version}" - exit 0 - when '--copyright' - puts Copyright - exit 0 - else - setup_rb_error "unknown global option '#{arg}'" - end - end - nil - end - - def valid_task?(t) - valid_task_re() =~ t - end - - def valid_task_re - @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ - end - - def parsearg_no_options - unless ARGV.empty? - setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" - end - end - - alias parsearg_show parsearg_no_options - alias parsearg_setup parsearg_no_options - alias parsearg_test parsearg_no_options - alias parsearg_clean parsearg_no_options - alias parsearg_distclean parsearg_no_options - - def parsearg_config - evalopt = [] - set = [] - @config.config_opt = [] - while i = ARGV.shift - if /\A--?\z/ =~ i - @config.config_opt = ARGV.dup - break - end - name, value = *@config.parse_opt(i) - if @config.value_config?(name) - @config[name] = value - else - evalopt.push [name, value] - end - set.push name - end - evalopt.each do |name, value| - @config.lookup(name).evaluate value, @config - end - # Check if configuration is valid - set.each do |n| - @config[n] if @config.value_config?(n) - end - end - - def parsearg_install - @config.no_harm = false - @config.install_prefix = '' - while a = ARGV.shift - case a - when '--no-harm' - @config.no_harm = true - when /\A--prefix=/ - path = a.split(/=/, 2)[1] - path = File.expand_path(path) unless path[0,1] == '/' - @config.install_prefix = path - else - setup_rb_error "install: unknown option #{a}" - end - end - end - - def print_usage(out) - out.puts 'Typical Installation Procedure:' - out.puts " $ ruby #{File.basename $0} config" - out.puts " $ ruby #{File.basename $0} setup" - out.puts " # ruby #{File.basename $0} install (may require root privilege)" - out.puts - out.puts 'Detailed Usage:' - out.puts " ruby #{File.basename $0} " - out.puts " ruby #{File.basename $0} [] []" - - fmt = " %-24s %s\n" - out.puts - out.puts 'Global options:' - out.printf fmt, '-q,--quiet', 'suppress message outputs' - out.printf fmt, ' --verbose', 'output messages verbosely' - out.printf fmt, ' --help', 'print this message' - out.printf fmt, ' --version', 'print version and quit' - out.printf fmt, ' --copyright', 'print copyright and quit' - out.puts - out.puts 'Tasks:' - TASKS.each do |name, desc| - out.printf fmt, name, desc - end - - fmt = " %-24s %s [%s]\n" - out.puts - out.puts 'Options for CONFIG or ALL:' - @config.each do |item| - out.printf fmt, item.help_opt, item.description, item.help_default - end - out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" - out.puts - out.puts 'Options for INSTALL:' - out.printf fmt, '--no-harm', 'only display what to do if given', 'off' - out.printf fmt, '--prefix=path', 'install path prefix', '' - out.puts - end - - # - # Task Handlers - # - - def exec_config - @installer.exec_config - @config.save # must be final - end - - def exec_setup - @installer.exec_setup - end - - def exec_install - @installer.exec_install - end - - def exec_test - @installer.exec_test - end - - def exec_show - @config.each do |i| - printf "%-20s %s\n", i.name, i.value if i.value? - end - end - - def exec_clean - @installer.exec_clean - end - - def exec_distclean - @installer.exec_distclean - end - -end # class ToplevelInstaller - - -class ToplevelInstallerMulti < ToplevelInstaller - - include FileOperations - - def initialize(ardir_root, config) - super - @packages = directories_of("#{@ardir}/packages") - raise 'no package exists' if @packages.empty? - @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) - end - - def run_metaconfigs - @config.load_script "#{@ardir}/metaconfig", self - @packages.each do |name| - @config.load_script "#{@ardir}/packages/#{name}/metaconfig" - end - end - - attr_reader :packages - - def packages=(list) - raise 'package list is empty' if list.empty? - list.each do |name| - raise "directory packages/#{name} does not exist"\ - unless File.dir?("#{@ardir}/packages/#{name}") - end - @packages = list - end - - def init_installers - @installers = {} - @packages.each do |pack| - @installers[pack] = Installer.new(@config, - "#{@ardir}/packages/#{pack}", - "packages/#{pack}") - end - with = extract_selection(config('with')) - without = extract_selection(config('without')) - @selected = @installers.keys.select {|name| - (with.empty? or with.include?(name)) \ - and not without.include?(name) - } - end - - def extract_selection(list) - a = list.split(/,/) - a.each do |name| - setup_rb_error "no such package: #{name}" unless @installers.key?(name) - end - a - end - - def print_usage(f) - super - f.puts 'Inluded packages:' - f.puts ' ' + @packages.sort.join(' ') - f.puts - end - - # - # Task Handlers - # - - def exec_config - run_hook 'pre-config' - each_selected_installers {|inst| inst.exec_config } - run_hook 'post-config' - @config.save # must be final - end - - def exec_setup - run_hook 'pre-setup' - each_selected_installers {|inst| inst.exec_setup } - run_hook 'post-setup' - end - - def exec_install - run_hook 'pre-install' - each_selected_installers {|inst| inst.exec_install } - run_hook 'post-install' - end - - def exec_test - run_hook 'pre-test' - each_selected_installers {|inst| inst.exec_test } - run_hook 'post-test' - end - - def exec_clean - rm_f @config.savefile - run_hook 'pre-clean' - each_selected_installers {|inst| inst.exec_clean } - run_hook 'post-clean' - end - - def exec_distclean - rm_f @config.savefile - run_hook 'pre-distclean' - each_selected_installers {|inst| inst.exec_distclean } - run_hook 'post-distclean' - end - - # - # lib - # - - def each_selected_installers - Dir.mkdir 'packages' unless File.dir?('packages') - @selected.each do |pack| - $stderr.puts "Processing the package `#{pack}' ..." if verbose? - Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") - Dir.chdir "packages/#{pack}" - yield @installers[pack] - Dir.chdir '../..' - end - end - - def run_hook(id) - @root_installer.run_hook id - end - - # module FileOperations requires this - def verbose? - @config.verbose? - end - - # module FileOperations requires this - def no_harm? - @config.no_harm? - end - -end # class ToplevelInstallerMulti - - -class Installer - - FILETYPES = %w( bin lib ext data conf man ) - - include FileOperations - include HookScriptAPI - - def initialize(config, srcroot, objroot) - @config = config - @srcdir = File.expand_path(srcroot) - @objdir = File.expand_path(objroot) - @currdir = '.' - end - - def inspect - "#<#{self.class} #{File.basename(@srcdir)}>" - end - - # - # Hook Script API base methods - # - - def srcdir_root - @srcdir - end - - def objdir_root - @objdir - end - - def relpath - @currdir - end - - # - # Config Access - # - - # module FileOperations requires this - def verbose? - @config.verbose? - end - - # module FileOperations requires this - def no_harm? - @config.no_harm? - end - - def verbose_off - begin - save, @config.verbose = @config.verbose?, false - yield - ensure - @config.verbose = save - end - end - - # - # TASK config - # - - def exec_config - exec_task_traverse 'config' - end - - def config_dir_bin(rel) - end - - def config_dir_lib(rel) - end - - def config_dir_man(rel) - end - - def config_dir_ext(rel) - extconf if extdir?(curr_srcdir()) - end - - def extconf - ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt - end - - def config_dir_data(rel) - end - - def config_dir_conf(rel) - end - - # - # TASK setup - # - - def exec_setup - exec_task_traverse 'setup' - end - - def setup_dir_bin(rel) - files_of(curr_srcdir()).each do |fname| - adjust_shebang "#{curr_srcdir()}/#{fname}" - end - end - - def adjust_shebang(path) - return if no_harm? - tmpfile = File.basename(path) + '.tmp' - begin - File.open(path, 'rb') {|r| - first = r.gets - return unless File.basename(first.sub(/\A\#!/, '').split[0].to_s) == 'ruby' - $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose? - File.open(tmpfile, 'wb') {|w| - w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath')) - w.write r.read - } - } - move_file tmpfile, File.basename(path) - ensure - File.unlink tmpfile if File.exist?(tmpfile) - end - end - - def setup_dir_lib(rel) - end - - def setup_dir_man(rel) - end - - def setup_dir_ext(rel) - make if extdir?(curr_srcdir()) - end - - def setup_dir_data(rel) - end - - def setup_dir_conf(rel) - end - - # - # TASK install - # - - def exec_install - rm_f 'InstalledFiles' - exec_task_traverse 'install' - end - - def install_dir_bin(rel) - install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 - end - - def install_dir_lib(rel) - install_files rubyscripts(), "#{config('rbdir')}/#{rel}", 0644 - end - - def install_dir_ext(rel) - return unless extdir?(curr_srcdir()) - install_files rubyextentions('.'), - "#{config('sodir')}/#{File.dirname(rel)}", - 0555 - end - - def install_dir_data(rel) - install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 - end - - def install_dir_conf(rel) - # FIXME: should not remove current config files - # (rename previous file to .old/.org) - install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 - end - - def install_dir_man(rel) - install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 - end - - def install_files(list, dest, mode) - mkdir_p dest, @config.install_prefix - list.each do |fname| - install fname, dest, mode, @config.install_prefix - end - end - - def rubyscripts - glob_select(@config.libsrc_pattern, targetfiles()) - end - - def rubyextentions(dir) - ents = glob_select("*.#{@config.dllext}", targetfiles()) - if ents.empty? - setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" - end - ents - end - - def targetfiles - mapdir(existfiles() - hookfiles()) - end - - def mapdir(ents) - ents.map {|ent| - if File.exist?(ent) - then ent # objdir - else "#{curr_srcdir()}/#{ent}" # srcdir - end - } - end - - # picked up many entries from cvs-1.11.1/src/ignore.c - JUNK_FILES = %w( - core RCSLOG tags TAGS .make.state - .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb - *~ *.old *.bak *.BAK *.orig *.rej _$* *$ - - *.org *.in .* - ) - - def existfiles - glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) - end - - def hookfiles - %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| - %w( config setup install clean ).map {|t| sprintf(fmt, t) } - }.flatten - end - - def glob_select(pat, ents) - re = globs2re([pat]) - ents.select {|ent| re =~ ent } - end - - def glob_reject(pats, ents) - re = globs2re(pats) - ents.reject {|ent| re =~ ent } - end - - GLOB2REGEX = { - '.' => '\.', - '$' => '\$', - '#' => '\#', - '*' => '.*' - } - - def globs2re(pats) - /\A(?:#{ - pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') - })\z/ - end - - # - # TASK test - # - - TESTDIR = 'test' - - def exec_test - unless File.directory?('test') - $stderr.puts 'no test in this package' if verbose? - return - end - $stderr.puts 'Running tests...' if verbose? - require 'test/unit' - runner = Test::Unit::AutoRunner.new(true) - runner.to_run << TESTDIR - runner.run - end - - # - # TASK clean - # - - def exec_clean - exec_task_traverse 'clean' - rm_f @config.savefile - rm_f 'InstalledFiles' - end - - def clean_dir_bin(rel) - end - - def clean_dir_lib(rel) - end - - def clean_dir_ext(rel) - return unless extdir?(curr_srcdir()) - make 'clean' if File.file?('Makefile') - end - - def clean_dir_data(rel) - end - - def clean_dir_conf(rel) - end - - # - # TASK distclean - # - - def exec_distclean - exec_task_traverse 'distclean' - rm_f @config.savefile - rm_f 'InstalledFiles' - end - - def distclean_dir_bin(rel) - end - - def distclean_dir_lib(rel) - end - - def distclean_dir_ext(rel) - return unless extdir?(curr_srcdir()) - make 'distclean' if File.file?('Makefile') - end - - def distclean_dir_data(rel) - end - - def distclean_dir_conf(rel) - end - - # - # lib - # - - def exec_task_traverse(task) - run_hook "pre-#{task}" - FILETYPES.each do |type| - if config('without-ext') == 'yes' and type == 'ext' - $stderr.puts 'skipping ext/* by user option' if verbose? - next - end - traverse task, type, "#{task}_dir_#{type}" - end - run_hook "post-#{task}" - end - - def traverse(task, rel, mid) - dive_into(rel) { - run_hook "pre-#{task}" - __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') - directories_of(curr_srcdir()).each do |d| - traverse task, "#{rel}/#{d}", mid - end - run_hook "post-#{task}" - } - end - - def dive_into(rel) - return unless File.dir?("#{@srcdir}/#{rel}") - - dir = File.basename(rel) - Dir.mkdir dir unless File.dir?(dir) - prevdir = Dir.pwd - Dir.chdir dir - $stderr.puts '---> ' + rel if verbose? - @currdir = rel - yield - Dir.chdir prevdir - $stderr.puts '<--- ' + rel if verbose? - @currdir = File.dirname(rel) - end - - def run_hook(id) - path = [ "#{curr_srcdir()}/#{id}", - "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } - return unless path - begin - instance_eval File.read(path), path, 1 - rescue - raise if $DEBUG - setup_rb_error "hook #{path} failed:\n" + $!.message - end - end - -end # class Installer - - -class SetupError < StandardError; end - -def setup_rb_error(msg) - raise SetupError, msg -end - -if $0 == __FILE__ - begin - ToplevelInstaller.invoke - rescue SetupError - raise if $DEBUG - $stderr.puts $!.message - $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." - exit 1 - end -end diff --git a/test/priority_queue_test.rb b/test/priority_queue_test.rb index 5b4ad30..04387e0 100644 --- a/test/priority_queue_test.rb +++ b/test/priority_queue_test.rb @@ -2,21 +2,14 @@ # Priority Queue tests -$:.unshift '../ext/priority_queue/CPriorityQueue' +$:.unshift '../ext/priority_queue' $:.unshift '../lib/' require 'test/unit' -require 'priority_queue/ruby_priority_queue' -require 'priority_queue/poor_priority_queue' -begin - require 'priority_queue/CPriorityQueue' -rescue LoadError - require 'CPriorityQueue' -end - +require 'priority_queue/priority_queue' -module PriorityQueueTest +module PriorityQueueTestHelper # Check that the order is maintained def teardown last = @q.min_priority @@ -101,7 +94,7 @@ def test_push 20.times do | i | @q.push i, i end - + 20.times do | i | assert_equal([i, i], @q.delete_min) end @@ -265,13 +258,13 @@ def test_each end class CPriorityQueueTest < Test::Unit::TestCase - include PriorityQueueTest + include PriorityQueueTestHelper def setup - @q = CPriorityQueue.new + @q = PriorityQueue.new end - def test_to_dot + def test_to_dot 5.times do | i | @q.push "N#{i}", i end @@ -289,83 +282,3 @@ def test_to_dot end end - -class PoorPriorityQueueTest < Test::Unit::TestCase - include PriorityQueueTest - - def setup - @q = PoorPriorityQueue.new - end - -end - -class RubyPriorityQueueTest < Test::Unit::TestCase - include PriorityQueueTest - - def setup - @q = RubyPriorityQueue.new - end - - def test_private_link_nodes - q = RubyPriorityQueue.new - q[0] = 0 - q[1] = 1 - tc = self - q.instance_eval do - n0 = @nodes[0] - n1 = @nodes[1] - n0.right = n0.left = n0 - n1.right = n1.left = n1 - tc.assert_equal(n0, link_nodes(n0, n1)) - tc.assert_equal(n0.child, n1) - tc.assert_equal(n1.child, nil) - tc.assert_equal(n0.left, n0) - tc.assert_equal(n1.left, n1) - tc.assert_equal(n0.right, n0) - tc.assert_equal(n1.right, n1) - end - q = RubyPriorityQueue.new - q[0] = 0 - q[1] = 1 - q.instance_eval do - n0 = @nodes[0] - n1 = @nodes[1] - n0.right = n0.left = n0 - n1.right = n1.left = n1 - tc.assert_equal(n0, link_nodes(n1, n0)) - tc.assert_equal(n0.child, n1) - tc.assert_equal(n1.child, nil) - tc.assert_equal(n0.left, n0) - tc.assert_equal(n1.left, n1) - tc.assert_equal(n0.right, n0) - tc.assert_equal(n1.right, n1) - end - end - - - def test_private_delete_first - q = RubyPriorityQueue.new - q[0] = 0 - q[1] = 1 - q[2] = 2 - tc = self - q.instance_eval do - 2.times do - r = @rootlist - tc.assert_equal(r, delete_first) - tc.assert_equal(r.right, r) - tc.assert_equal(r.left, r) - tc.assert_not_equal(r, @rootlist.left) - tc.assert_not_equal(r, @rootlist.right) - end - r = @rootlist - tc.assert_equal(r, delete_first) - tc.assert_equal(r.right, r) - tc.assert_equal(r.left, r) - tc.assert_equal(nil, @rootlist) - - tc.assert_equal(nil, delete_first) - end - end -end -