simplekit 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b6161ac2dfac81db037d02c9b8c67c0c03b8c44c
4
+ data.tar.gz: 4fd108d466e784133715c59de4ff2f9e49b8e31d
5
+ SHA512:
6
+ metadata.gz: d0f7a06954e3106acde5089fea579560a3b485d4bcc258f45473a439cf98b9f78bb6e72562f728214c2daefe29a6df50b5b9496deb60d1d93a2126ed0cbadffb
7
+ data.tar.gz: d8602e4a17b0b12793d1c03a789e8ee5a0fa91e6f539a0fa467a61963914decba6be8c2d0e6c5a46c2c872a7f3205ced7ee0f69a6ab04fa5f6ee98379bf3f44b
@@ -0,0 +1,90 @@
1
+ # Array-based PriorityQueue implementation
2
+ class PriorityQueue
3
+ def initialize
4
+ clear
5
+ end
6
+
7
+ def <<(element)
8
+ @elements << element
9
+ # bubble up the element that we just added
10
+ bubble_up(@elements.size - 1)
11
+ end
12
+
13
+ alias push <<
14
+
15
+ def peek
16
+ # the first element will always be the min, because of the heap constraint
17
+ @elements[1]
18
+ end
19
+
20
+ def pop
21
+ # remove the last element of the list
22
+ min = @elements[1]
23
+
24
+ # and make sure the tree is ordered again
25
+ bubble_down(1) unless empty?
26
+ min
27
+ end
28
+
29
+ def clear
30
+ @elements = [nil]
31
+ end
32
+
33
+ def empty?
34
+ @elements.length < 2
35
+ end
36
+
37
+ private
38
+
39
+ def bubble_up(index)
40
+ target = @elements[index]
41
+ loop do
42
+ parent_index = (index / 2)
43
+
44
+ # return if we reach the root or the parent is less than the child
45
+ if parent_index < 1 || @elements[parent_index] <= target
46
+ @elements[index] = target
47
+ return
48
+ end
49
+
50
+ # otherwise we exchange the child with the parent
51
+ @elements[index] = @elements[parent_index]
52
+
53
+ # and keep bubbling up
54
+ index = parent_index
55
+ end
56
+ end
57
+
58
+ def bubble_down(index)
59
+ target = @elements.pop
60
+ return if empty?
61
+ loop do
62
+ child_index = (index * 2)
63
+
64
+ # stop if we reach the bottom of the tree
65
+ if child_index >= @elements.size
66
+ @elements[index] = target
67
+ return
68
+ end
69
+
70
+ # make sure we get the smallest child
71
+ not_the_last_element = child_index < @elements.size - 1
72
+ left_element = @elements[child_index]
73
+ right_element = @elements[child_index + 1]
74
+ child_index += 1 if not_the_last_element && right_element < left_element
75
+
76
+ # there is no need to continue if the parent element is already smaller
77
+ # then its children
78
+ if target <= @elements[child_index]
79
+ @elements[index] = target
80
+ return
81
+ end
82
+
83
+ @elements[index] = @elements[child_index]
84
+
85
+ # repeat the process until we reach a point where the parent
86
+ # is larger than its children
87
+ index = child_index
88
+ end
89
+ end
90
+ end
data/lib/simplekit.rb ADDED
@@ -0,0 +1,90 @@
1
+ require_relative 'priority_queue'
2
+
3
+ # The +SimpleKit+ module provides basic event scheduling capabilities.
4
+ #
5
+ # Including +SimpleKit+ in your simulation model gives you methods +:run+,
6
+ # +:model_time+, +:schedule+, and +:halt+ as mixins. You <b>MUST NOT</b>
7
+ # provide your own implementations of methods with these names in your model.
8
+ # All but +:run+ are delegated to the +EventScheduler+ class.
9
+ module SimpleKit
10
+ # The set of module methods to be passed to the EventScheduler
11
+ # if not found in the model class.
12
+ DELEGATED_METHODS = [:model_time, :schedule, :halt].freeze
13
+
14
+ # Run your model by creating a new +EventScheduler+ and invoking its
15
+ # +run+ method.
16
+ def run
17
+ @my_sim = EventScheduler.new(self)
18
+ @my_sim.run
19
+ end
20
+
21
+ # If a method doesn't exist in the model class, try to delegate it
22
+ # to +EventScheduler+.
23
+ def method_missing(name, *args)
24
+ if DELEGATED_METHODS.include?(name)
25
+ @my_sim.send(name, *args)
26
+ else
27
+ super
28
+ end
29
+ end
30
+
31
+ # Class +EventScheduler+ provides the computation engine for a
32
+ # discrete event simulation model. It uses an array-based priority
33
+ # queue implementation for the pending events list.
34
+ #
35
+ # Users must create a model class which:
36
+ # * implements an +init+ method;
37
+ # * Instantiates a model and invokes the +run+ method to start execution.
38
+ class EventScheduler
39
+ attr_reader :model_time, :user_model
40
+
41
+ # Initialize the +EventScheduler+ by remembering the specified model
42
+ # and setting up an empty event list.
43
+ def initialize(the_model)
44
+ @user_model = the_model
45
+ @event_list = PriorityQueue.new
46
+ end
47
+
48
+ # Add an event to the pending events list.
49
+ #
50
+ # *Arguments*::
51
+ # - +event+ -> the event to be scheduled.
52
+ # - +delay+ -> the amount of time which should elapse before
53
+ # the event executes.
54
+ # - +args+ -> an optional list of arguments to pass to the event
55
+ # at invocation time.
56
+ def schedule(event, delay, *args)
57
+ raise 'Model scheduled event with negative delay.' if delay < 0
58
+ @event_list.push EventNotice.new(event, @model_time + delay, args)
59
+ end
60
+
61
+ # Start execution of a model. The simulation +model_time+ is initialized
62
+ # to zero and the model is initialized via the mandatory +init+ method.
63
+ # Then loop while events are pending on the +event_list+. The event with
64
+ # the smallest time is popped, +model_time+ is updated to the event time,
65
+ # and the event method is invoked.
66
+ def run
67
+ @model_time = 0.0
68
+ @user_model.init
69
+ while (current_event = @event_list.pop)
70
+ @model_time = current_event.time
71
+ @user_model.send(current_event.event, *current_event.args)
72
+ end
73
+ end
74
+
75
+ # Clear the event list, which causes termination of the simulation.
76
+ # Never schedule any new events after invoking +halt+.
77
+ def halt
78
+ @event_list.clear
79
+ end
80
+ end
81
+
82
+ # This is a private helper Struct for the EventScheduler class.
83
+ # Users should never try to access this directly.
84
+ EventNotice = Struct.new(:event, :time, *:args) do
85
+ include Comparable
86
+ def <=>(other)
87
+ time <=> other.time
88
+ end
89
+ end
90
+ end
data/simplekit.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- ruby -*-
2
+ _VERSION = "0.4.5"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "simplekit"
6
+ s.version = _VERSION
7
+ s.date = "2017-12-01"
8
+ s.summary = "Discrete event simulation engine."
9
+ s.homepage = "https://gitlab.nps.edu/pjsanche/simplekit-ruby.git"
10
+ s.email = "pjs@alum.mit.edu"
11
+ s.description = "This is a minimal discrete event simulation scheduling algorithm for educational use."
12
+ s.author = "Paul J Sanchez"
13
+ s.files = %w[
14
+ simplekit.gemspec
15
+ lib/simplekit.rb
16
+ lib/priority_queue.rb
17
+ ]
18
+ s.required_ruby_version = '>= 1.8.1'
19
+ s.license = 'MIT'
20
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simplekit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.5
5
+ platform: ruby
6
+ authors:
7
+ - Paul J Sanchez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-12-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: This is a minimal discrete event simulation scheduling algorithm for
14
+ educational use.
15
+ email: pjs@alum.mit.edu
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/priority_queue.rb
21
+ - lib/simplekit.rb
22
+ - simplekit.gemspec
23
+ homepage: https://gitlab.nps.edu/pjsanche/simplekit-ruby.git
24
+ licenses:
25
+ - MIT
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.8.1
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.6.14
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: Discrete event simulation engine.
47
+ test_files: []