mruby_tools 0.0.1.1

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: 9aa01e1c7834a6b9ba351aa1e70df7399628dc8c
4
+ data.tar.gz: 42b72b7ca714f805e00a0f73b12439d1611dbd65
5
+ SHA512:
6
+ metadata.gz: 5aae2588bb3b849cba817a377f2995c55d7e0e8b27f770b9f5c991093f6c9f3032a7d2e9e64810bee44ffb8b616b2e6a1f55694297af1b8ca20bc22c1c4b5cde
7
+ data.tar.gz: 2e225121b919de40e9fbd803fbf0c3aa2cf5ebb7b0433c1bfa30e58e52cce864361f4248ef00b90892669a239d5908c0cab64b1cbf3ea72f1ee868f63dd34a40
@@ -0,0 +1 @@
1
+ # MRuby Tools
@@ -0,0 +1,55 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new :test do |t|
4
+ t.pattern = "test/*.rb"
5
+ t.warning = true
6
+ end
7
+
8
+
9
+ #
10
+ # METRICS
11
+ #
12
+
13
+ begin
14
+ require 'flog_task'
15
+ FlogTask.new do |t|
16
+ t.dirs = ['lib']
17
+ t.verbose = true
18
+ end
19
+ rescue LoadError
20
+ warn 'flog_task unavailable'
21
+ end
22
+
23
+ begin
24
+ require 'flay_task'
25
+ FlayTask.new do |t|
26
+ t.dirs = ['lib']
27
+ t.verbose = true
28
+ end
29
+ rescue LoadError
30
+ warn 'flay_task unavailable'
31
+ end
32
+
33
+ begin
34
+ require 'roodi_task'
35
+ # RoodiTask.new config: '.roodi.yml', patterns: ['lib/**/*.rb']
36
+ RoodiTask.new patterns: ['lib/**/*.rb']
37
+ rescue LoadError
38
+ warn "roodi_task unavailable"
39
+ end
40
+
41
+ #
42
+ # GEM BUILD / PUBLISH
43
+ #
44
+
45
+ begin
46
+ require 'buildar'
47
+
48
+ Buildar.new do |b|
49
+ b.gemspec_file = 'mruby_tools.gemspec'
50
+ b.version_file = 'VERSION'
51
+ b.use_git = true
52
+ end
53
+ rescue LoadError
54
+ warn "buildar tasks unavailable"
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1.1
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tempfile'
4
+ require 'mruby_tools'
5
+
6
+ # args like: file1.rb file2.rb -o outfile
7
+ # possibly: file1.rb -o outfile file2.rb -c generated.c
8
+
9
+ opts = MRubyTools.args(ARGV)
10
+ outfile = opts.fetch(:outfile)
11
+
12
+ c_code = <<'EOF'
13
+ #include <stdlib.h>
14
+ #include <mruby.h>
15
+ #include <mruby/compile.h>
16
+ #include <mruby/string.h>
17
+
18
+ void check_exc(mrb_state *mrb, char *filename) {
19
+ if (mrb->exc) {
20
+ mrb_value exc = mrb_obj_value(mrb->exc);
21
+ mrb_value exc_msg = mrb_funcall(mrb, exc, "to_s", 0);
22
+ fprintf(stderr, "ERROR in %s - %s: %s\n",
23
+ filename,
24
+ mrb_obj_classname(mrb, exc),
25
+ mrb_str_to_cstr(mrb, exc_msg));
26
+ /* mrb_print_backtrace(mrb); # empty */
27
+ exit(1);
28
+ }
29
+ }
30
+
31
+ int
32
+ main(void)
33
+ {
34
+ mrb_state *mrb = mrb_open();
35
+ if (!mrb) {
36
+ printf("mrb problem");
37
+ exit(1);
38
+ }
39
+ EOF
40
+
41
+ c_code += opts.fetch(:rb_files).map { |rbf|
42
+ "\n" + MRubyTools.rb2c(rbf) + "\n\n"
43
+ }.join
44
+
45
+ c_code += <<EOF
46
+ mrb_close(mrb);
47
+ return 0;
48
+ }
49
+ EOF
50
+
51
+ puts c_code + "\n" if opts[:verbose]
52
+
53
+ file = opts[:cfile] || Tempfile.new(['generated', '.c'])
54
+ file.write(c_code)
55
+ file.close
56
+
57
+ msd = MRubyTools.mruby_src_dir
58
+
59
+ gcc_args = [
60
+ '-std=c99', "-I", File.join(msd, 'include'), file.path, "-o", outfile,
61
+ File.join(msd, 'build', 'host', 'lib', 'libmruby.a'), '-lm',
62
+ ]
63
+
64
+ puts "compiling..."
65
+ puts "created binary executable: #{outfile}" if system('gcc', *gcc_args)
@@ -0,0 +1,98 @@
1
+ SIMPLEX_PARAMS = [
2
+ # 1 (index 0)
3
+ [[1, 1],
4
+ [[2, 1],
5
+ [1, 2]],
6
+ [4, 3]],
7
+
8
+ [[3, 4],
9
+ [[1, 1],
10
+ [2, 1]],
11
+ [4, 5]],
12
+
13
+ [[2, -1],
14
+ [[1, 2],
15
+ [3, 2],],
16
+ [6, 12]],
17
+
18
+ [[60, 90, 300],
19
+ [[1, 1, 1],
20
+ [1, 3, 0],
21
+ [2, 0, 1]],
22
+ [600, 600, 900]],
23
+
24
+ # 5
25
+ [[70, 210, 140],
26
+ [[1, 1, 1],
27
+ [5, 4, 4],
28
+ [40, 20, 30]],
29
+ [100, 480, 3200]],
30
+
31
+ [[2, -1, 2],
32
+ [[2, 1, 0],
33
+ [1, 2, -2],
34
+ [0, 1, 2]],
35
+ [10, 20, 5]],
36
+
37
+ [[11, 16, 15],
38
+ [[1, 2, 3.to_f / 2],
39
+ [2.to_f / 3, 2.to_f / 3, 1],
40
+ [0.5, 1.to_f / 3, 0.5]],
41
+ [12_000, 4_600, 2_400]],
42
+
43
+ [[5, 4, 3],
44
+ [[2, 3, 1],
45
+ [4, 1, 2],
46
+ [3, 4, 2]],
47
+ [5, 11, 8]],
48
+
49
+ [[3, 2, -4],
50
+ [[1, 4, 0],
51
+ [2, 4,-2],
52
+ [1, 1,-2]],
53
+ [5, 6, 2]],
54
+
55
+ # 10
56
+ [[2, -1, 8],
57
+ [[2, -4, 6],
58
+ [-1, 3, 4],
59
+ [0, 0, 2]],
60
+ [3, 2, 1]],
61
+
62
+ [[100_000, 40_000, 18_000],
63
+ [[20, 6, 3],
64
+ [0, 1, 0],
65
+ [-1,-1, 1],
66
+ [-9, 1, 1]],
67
+ [182, 10, 0, 0]],
68
+
69
+ [[1, 2, 1, 2],
70
+ [[1, 0, 1, 0],
71
+ [0, 1, 0, 1],
72
+ [1, 1, 0, 0],
73
+ [0, 0, 1, 1]],
74
+ [1, 4, 2, 2]],
75
+
76
+ [[10, -57, -9, -24],
77
+ [[0.5, -5.5, -2.5, 9],
78
+ [0.5, -1.5, -0.5, 1],
79
+ [ 1, 0, 0, 0]],
80
+ [0, 0, 1]],
81
+
82
+ # 14 (index 13)
83
+ [[25, 20],
84
+ [[20, 12],
85
+ [1, 1]],
86
+ [1800, 8*15]],
87
+ ]
88
+
89
+ def simplices
90
+ SIMPLEX_PARAMS.map { |c, a, b| Simplex.new(c, a, b) }
91
+ end
92
+
93
+ puts "calculating #{SIMPLEX_PARAMS.size} simplex solutions, repeatedly..."
94
+ val, elapsed = Timer.loop_avg {
95
+ simplices.map { |s| s.solution }.join("\n")
96
+ }
97
+
98
+ puts "%0.8f s (avg) per solution" % (elapsed / SIMPLEX_PARAMS.size)
@@ -0,0 +1 @@
1
+ puts :goodbye_world
@@ -0,0 +1 @@
1
+ puts :hello_world
@@ -0,0 +1,2 @@
1
+ raise "stuff"
2
+ #raise StandardError, "things"
@@ -0,0 +1,164 @@
1
+ class Simplex
2
+ DEFAULT_MAX_PIVOTS = 10_000
3
+
4
+ class Error < RuntimeError; end
5
+ class UnboundedProblem < Error; end
6
+ class SanityCheck < Error; end
7
+ class TooManyPivots < Error; end
8
+
9
+ attr_accessor :max_pivots
10
+
11
+ # c - coefficients of objective function; size: num_vars
12
+ # a - inequality lhs coefficients; 2dim size: num_inequalities, num_vars
13
+ # b - inequality rhs constants size: num_inequalities
14
+ def initialize(c, a, b)
15
+ num_vars = c.size
16
+ num_inequalities = b.size
17
+ raise(ArgumentError, "a doesn't match b") unless a.size == num_inequalities
18
+ raise(ArgumentError, "a doesn't match c") unless a.first.size == num_vars
19
+
20
+ @max_pivots = DEFAULT_MAX_PIVOTS
21
+
22
+ # Problem dimensions; these never change
23
+ @num_non_slack_vars = num_vars
24
+ @num_constraints = num_inequalities
25
+ @num_vars = @num_non_slack_vars + @num_constraints
26
+
27
+ # Set up initial matrix A and vectors b, c
28
+ @c = c.map { |flt| -1 * flt } + Array.new(@num_constraints, 0)
29
+ @a = a.map.with_index { |ary, i|
30
+ if ary.size != @num_non_slack_vars
31
+ raise ArgumentError, "a is inconsistent"
32
+ end
33
+ ary + Array.new(@num_constraints) { |ci| ci == i ? 1 : 0 }
34
+ }
35
+ @b = b
36
+
37
+ # set initial solution: all non-slack variables = 0
38
+ @basic_vars = (@num_non_slack_vars...@num_vars).to_a
39
+ self.update_solution
40
+ end
41
+
42
+ # does not modify vector / matrix
43
+ def update_solution
44
+ @x = Array.new(@num_vars, 0)
45
+
46
+ @basic_vars.each { |basic_var|
47
+ idx = nil
48
+ @num_constraints.times { |i|
49
+ if @a[i][basic_var] == 1
50
+ idx =i
51
+ break
52
+ end
53
+ }
54
+ raise(SanityCheck, "no idx for basic_var #{basic_var} in a") unless idx
55
+ @x[basic_var] = @b[idx]
56
+ }
57
+ end
58
+
59
+ def solution
60
+ self.solve
61
+ self.current_solution
62
+ end
63
+
64
+ def solve
65
+ count = 0
66
+ while self.can_improve?
67
+ count += 1
68
+ raise(TooManyPivots, count.to_s) unless count < @max_pivots
69
+ self.pivot
70
+ end
71
+ end
72
+
73
+ def current_solution
74
+ @x[0...@num_non_slack_vars]
75
+ end
76
+
77
+ def can_improve?
78
+ !self.entering_variable.nil?
79
+ end
80
+
81
+ # idx of @c's minimum negative value
82
+ # nil when no improvement is possible
83
+ #
84
+ def entering_variable
85
+ (0...@c.size).select { |i| @c[i] < 0 }.min_by { |i| @c[i] }
86
+ end
87
+
88
+ def pivot
89
+ pivot_column = self.entering_variable or return nil
90
+ pivot_row = self.pivot_row(pivot_column) or raise UnboundedProblem
91
+ leaving_var = nil
92
+ @a[pivot_row].each_with_index { |a, i|
93
+ if a == 1 and @basic_vars.include?(i)
94
+ leaving_var = i
95
+ break
96
+ end
97
+ }
98
+ raise(SanityCheck, "no leaving_var") if leaving_var.nil?
99
+
100
+ @basic_vars.delete(leaving_var)
101
+ @basic_vars.push(pivot_column)
102
+ @basic_vars.sort!
103
+
104
+ pivot_ratio = 1.to_f / @a[pivot_row][pivot_column]
105
+
106
+ # update pivot row
107
+ @a[pivot_row] = @a[pivot_row].map { |val| val * pivot_ratio }
108
+ @b[pivot_row] = @b[pivot_row] * pivot_ratio
109
+
110
+ # update objective
111
+ # @c -= @c[pivot_column] * @a[pivot_row]
112
+ @c = @c.map.with_index { |val, i|
113
+ val - @c[pivot_column] * @a[pivot_row][i]
114
+ }
115
+
116
+ # update A and B
117
+ @num_constraints.times { |i|
118
+ next if i == pivot_row
119
+ r = @a[i][pivot_column]
120
+ @a[i] = @a[i].map.with_index { |val, j| val - r * @a[pivot_row][j] }
121
+ @b[i] = @b[i] - r * @b[pivot_row]
122
+ }
123
+
124
+ self.update_solution
125
+ end
126
+
127
+ def pivot_row(column_ix)
128
+ min_ratio = nil
129
+ idx = nil
130
+ @num_constraints.times { |i|
131
+ a, b = @a[i][column_ix], @b[i]
132
+ next if a == 0 or (b < 0) ^ (a < 0)
133
+ ratio = b.to_f / a
134
+ idx, min_ratio = i, ratio if min_ratio.nil? or ratio <= min_ratio
135
+ }
136
+ idx
137
+ end
138
+
139
+ def formatted_tableau
140
+ if self.can_improve?
141
+ pivot_column = self.entering_variable
142
+ pivot_row = self.pivot_row(pivot_column)
143
+ else
144
+ pivot_row = nil
145
+ end
146
+ c = @c.to_a.map { |flt| "%2.3f" % flt }
147
+ b = @b.to_a.map { |flt| "%2.3f" % flt }
148
+ a = @a.to_a.map { |vec| vec.to_a.map { |flt| "%2.3f" % flt } }
149
+ if pivot_row
150
+ a[pivot_row][pivot_column] = "*" + a[pivot_row][pivot_column]
151
+ end
152
+ max = (c + b + a + ["1234567"]).flatten.map(&:size).max
153
+ result = []
154
+ result << c.map { |str| str.rjust(max, " ") }
155
+ a.zip(b) do |arow, brow|
156
+ result << (arow + [brow]).map { |val| val.rjust(max, " ") }
157
+ result.last.insert(arow.length, "|")
158
+ end
159
+ lines = result.map { |ary| ary.join(" ") }
160
+ max_line_length = lines.map(&:length).max
161
+ lines.insert(1, "-"*max_line_length)
162
+ lines.join("\n")
163
+ end
164
+ end
@@ -0,0 +1,27 @@
1
+ module Timer
2
+ def self.now
3
+ Time.now
4
+ end
5
+
6
+ def self.since(t)
7
+ self.now - t
8
+ end
9
+
10
+ def self.elapsed(&work)
11
+ t = self.now
12
+ return yield, self.since(t)
13
+ end
14
+
15
+ def self.loop_avg(count = 999, seconds = 1, &work)
16
+ i = 0
17
+ start = self.now
18
+ val = nil
19
+ loop {
20
+ val = yield
21
+ i += 1
22
+ break if i >= count
23
+ break if self.since(start) > seconds
24
+ }
25
+ return val, self.since(start) / i.to_f
26
+ end
27
+ end
@@ -0,0 +1,48 @@
1
+ module MRubyTools
2
+ def self.rb2c(rb_filename, indent: ' ')
3
+ c_str = File.read(rb_filename)
4
+ size = c_str.size
5
+ c_str = c_str.gsub("\n", '\n').gsub('"', '\"')
6
+ c_str = File.read(rb_filename).gsub("\n", '\n').gsub('"', '\"')
7
+ [ "/* #{rb_filename} */",
8
+ 'mrb_load_nstring(mrb, "' + c_str + '", ' + "#{size});",
9
+ "check_exc(mrb, \"#{rb_filename}\");",
10
+ ].map { |s| indent + s }.join("\n")
11
+ end
12
+
13
+ def self.mruby_src_dir(env_var = 'MRUBY_SRC')
14
+ mruby_src_dir = ENV[env_var]
15
+ raise "env: MRUBY_SRC is required" unless mruby_src_dir
16
+ raise "bad MRUBY_SRC #{mruby_src_dir}" unless File.directory? mruby_src_dir
17
+ mruby_inc_dir = File.join(mruby_src_dir, 'include')
18
+ raise "bad MRUBY_SRC #{mruby_inc_dir}" unless File.directory? mruby_inc_dir
19
+ mruby_src_dir
20
+ end
21
+
22
+ def self.args(argv = ARGV)
23
+ rb_files = []
24
+ outfile = nil
25
+ cfile = nil
26
+ verbose = false
27
+
28
+ while !argv.empty?
29
+ arg = argv.shift
30
+ if arg == '-o'
31
+ outfile = argv.shift
32
+ raise "no outfile provided with -o" unless outfile
33
+ raise "#{outfile} is misnamed" if File.extname(outfile) == '.rb'
34
+ elsif arg == '-c'
35
+ cfile = File.open(argv.shift || 'generated.c', "w")
36
+ elsif arg == '-v'
37
+ verbose = true
38
+ else
39
+ rb_files << arg
40
+ end
41
+ end
42
+
43
+ { verbose: verbose,
44
+ cfile: cfile,
45
+ outfile: outfile || 'outfile',
46
+ rb_files: rb_files }
47
+ end
48
+ end
@@ -0,0 +1,28 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'mruby_tools'
3
+ s.summary = "MRI Ruby tools for assisting in MRuby development"
4
+ s.description = "TBD"
5
+ s.authors = ["Rick Hull"]
6
+ s.homepage = "https://github.com/rickhull/mruby-tools"
7
+ s.license = "LGPL-3.0"
8
+
9
+ s.required_ruby_version = "~> 2"
10
+
11
+ s.version = File.read(File.join(__dir__, 'VERSION')).chomp
12
+
13
+ s.files = %w[mruby_tools.gemspec VERSION README.md Rakefile]
14
+ %w[lib bin test examples].each { |dir|
15
+ s.files += Dir[File.join(dir, '**', '*.rb')]
16
+ }
17
+
18
+ s.executables << 'mrbt'
19
+
20
+ s.add_development_dependency "rake", "~> 0"
21
+ s.add_development_dependency "buildar", "~> 3.0"
22
+ # s.add_development_dependency "minitest", "~> 5.0"
23
+ # s.add_development_dependency "flog", "~> 0"
24
+ # s.add_development_dependency "flay", "~> 0"
25
+ # s.add_development_dependency "roodi", "~> 0"
26
+ # s.add_development_dependency "ruby-prof", "~> 0"
27
+ # s.add_development_dependency "benchmark-ips", "~> 2.0"
28
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mruby_tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Rick Hull
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-11-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: buildar
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ description: TBD
42
+ email:
43
+ executables:
44
+ - mrbt
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - README.md
49
+ - Rakefile
50
+ - VERSION
51
+ - bin/mrbt
52
+ - examples/driver.rb
53
+ - examples/goodbye_world.rb
54
+ - examples/hello_world.rb
55
+ - examples/raise.rb
56
+ - examples/simplex.rb
57
+ - examples/timer.rb
58
+ - lib/mruby_tools.rb
59
+ - mruby_tools.gemspec
60
+ homepage: https://github.com/rickhull/mruby-tools
61
+ licenses:
62
+ - LGPL-3.0
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: '2'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.6.8
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: MRI Ruby tools for assisting in MRuby development
84
+ test_files: []