From 086ec74767a44df5beed64ccc35e67d0534a0552 Mon Sep 17 00:00:00 2001
From: pjs <pjs@alum.mit.edu>
Date: Fri, 19 Aug 2016 15:02:00 -0700
Subject: [PATCH] Array-based priority queue

---
 lib/priority_queue.rb | 89 +++++++++++++++++++++++++++++++++++++++++++
 lib/simplekit.rb      | 13 +++----
 2 files changed, 94 insertions(+), 8 deletions(-)
 create mode 100644 lib/priority_queue.rb

diff --git a/lib/priority_queue.rb b/lib/priority_queue.rb
new file mode 100644
index 0000000..d638406
--- /dev/null
+++ b/lib/priority_queue.rb
@@ -0,0 +1,89 @@
+# dis is a PriorityQueue
+class PriorityQueue
+  def initialize
+    clear
+  end
+
+  def <<(element)
+    @elements << element
+    # bubble up the element that we just added
+    bubble_up(@elements.size - 1)
+  end
+
+  alias push <<
+
+  def peek
+    # the first element will always be the min, because of the heap constraint
+    @elements[1]
+  end
+
+  def pop
+    # remove the last element of the list
+    min = @elements[1]
+
+    # and make sure the tree is ordered again
+    bubble_down(1)
+    min
+  end
+
+  def clear
+    @elements = [nil]
+  end
+
+  def empty?
+    @elements.length < 2
+  end
+
+  private
+
+  def bubble_up(index)
+    target = @elements[index]
+    loop do
+      parent_index = (index / 2)
+
+      # return if we reach the root or the parent is less than the child
+      if parent_index < 1 || @elements[parent_index] <= target
+        @elements[index] = target
+        return
+      end
+
+      # otherwise we exchange the child with the parent
+      @elements[index] = @elements[parent_index]
+
+      # and keep bubbling up
+      index = parent_index
+    end
+  end
+
+  def bubble_down(index)
+    target = @elements.pop
+    loop do
+      child_index = (index * 2)
+
+      # stop if we reach the bottom of the tree
+      if child_index > @elements.size - 1
+        @elements[index] = target
+        return
+      end
+
+      # make sure we get the smallest child
+      not_the_last_element = child_index < @elements.size - 1
+      left_element = @elements[child_index]
+      right_element = @elements[child_index + 1]
+      child_index += 1 if not_the_last_element && right_element < left_element
+
+      # there is no need to continue if the parent element is already smaller
+      # then its children
+      if target <= @elements[child_index]
+        @elements[index] = target
+        return
+      end
+
+      @elements[index] = @elements[child_index]
+
+      # repeat the process until we reach a point where the parent
+      # is larger than its children
+      index = child_index
+    end
+  end
+end
diff --git a/lib/simplekit.rb b/lib/simplekit.rb
index 07cdc7f..cf428b4 100644
--- a/lib/simplekit.rb
+++ b/lib/simplekit.rb
@@ -1,5 +1,4 @@
-require 'rubygems' if RUBY_VERSION =~ /^1\.8/
-require 'skewheap'
+require_relative 'priority_queue'
 
 # The +SimpleKit+ module provides basic event scheduling capabilities.
 #
@@ -10,7 +9,7 @@ require 'skewheap'
 module SimpleKit
   # The set of module methods to be passed to the EventScheduler
   # if not found in the model class.
-  DELEGATED_METHODS = [:model_time, :schedule, :halt]
+  DELEGATED_METHODS = [:model_time, :schedule, :halt].freeze
 
   # Run your model by creating a new +EventScheduler+ and invoking its
   # +run+ method.
@@ -29,8 +28,8 @@ module SimpleKit
     end
   end
 
-  # Class +EventScheduler+ provides the computation engine for a discrete
-  # event simulation model. It uses the +SkewHeap+ RubyGem as a priority
+  # Class +EventScheduler+ provides the computation engine for a
+  # discrete event simulation model. It uses an array-based priority
   # queue implementation for the pending events list.
   #
   # Users must create a model class which:
@@ -43,7 +42,7 @@ module SimpleKit
     # and setting up an empty event list.
     def initialize(the_model)
       @user_model = the_model
-      @event_list = SkewHeap.new
+      @event_list = PriorityQueue.new
     end
 
     # Add an event to the pending events list.
@@ -80,8 +79,6 @@ module SimpleKit
     end
   end
 
-  private
-
   # This is a private helper Struct for the EventScheduler class.
   # Users should never try to access this directly.
   EventNotice = Struct.new(:event, :time, *:args) do
-- 
GitLab