rinruby-edge 2.1.0.edge.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rinruby.rb ADDED
@@ -0,0 +1,1028 @@
1
+ #=RinRuby: Accessing the R[http://www.r-project.org] interpreter from pure Ruby
2
+ #
3
+ #RinRuby is a Ruby library that integrates the R interpreter in Ruby, making R's statistical routines and graphics available within Ruby. The library consists of a single Ruby script that is simple to install and does not require any special compilation or installation of R. Since the library is 100% pure Ruby, it works on a variety of operating systems, Ruby implementations, and versions of R. RinRuby's methods are simple, making for readable code. The {website [rinruby.ddahl.org]}[http://rinruby.ddahl.org] describes RinRuby usage, provides comprehensive documentation, gives several examples, and discusses RinRuby's implementation.
4
+ #
5
+ #Below is a simple example of RinRuby usage for simple linear regression. The simulation parameters are defined in Ruby, computations are performed in R, and Ruby reports the results. In a more elaborate application, the simulation parameter might come from input from a graphical user interface, the statistical analysis might be more involved, and the results might be an HTML page or PDF report.
6
+ #
7
+ #<b>Code</b>:
8
+ #
9
+ # require "rinruby"
10
+ # n = 10
11
+ # beta_0 = 1
12
+ # beta_1 = 0.25
13
+ # alpha = 0.05
14
+ # seed = 23423
15
+ # R.x = (1..n).entries
16
+ # R.eval <<EOF
17
+ # set.seed(#{seed})
18
+ # y <- #{beta_0} + #{beta_1}*x + rnorm(#{n})
19
+ # fit <- lm( y ~ x )
20
+ # est <- round(coef(fit),3)
21
+ # pvalue <- summary(fit)$coefficients[2,4]
22
+ # EOF
23
+ # puts "E(y|x) ~= #{R.est[0]} + #{R.est[1]} * x"
24
+ # if R.pvalue < alpha
25
+ # puts "Reject the null hypothesis and conclude that x and y are related."
26
+ # else
27
+ # puts "There is insufficient evidence to conclude that x and y are related."
28
+ # end
29
+ #
30
+ #<b>Output</b>:
31
+ #
32
+ # E(y|x) ~= 1.264 + 0.273 * x
33
+ # Reject the null hypothesis and conclude that x and y are related.
34
+ #
35
+ #Coded by:: David B. Dahl
36
+ #Documented by:: David B. Dahl & Scott Crawford
37
+ #Maintained by:: Claudio Bustos
38
+ #Copyright:: 2005-2009
39
+ #Web page:: http://rinruby.ddahl.org
40
+ #E-mail:: mailto:rinruby@ddahl.org
41
+ #License:: GNU Lesser General Public License (LGPL), version 3 or later
42
+ #
43
+ #--
44
+ # This program is free software: you can redistribute it and/or modify
45
+ # it under the terms of the GNU Lesser General Public License as published by
46
+ # the Free Software Foundation, either version 3 of the License, or
47
+ # (at your option) any later version.
48
+ #
49
+ # This program is distributed in the hope that it will be useful,
50
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
51
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52
+ # GNU General Public License for more details.
53
+ #
54
+ # You should have received a copy of the GNU Lesser General Public License
55
+ # along with this program. If not, see <http://www.gnu.org/licenses/>
56
+ #++
57
+ #
58
+ #
59
+ #The files "java" and "readline" are used when available to add functionality.
60
+ require 'matrix'
61
+
62
+ require File.expand_path(File.dirname(__FILE__) + '/rinruby/version.rb')
63
+
64
+ class RinRuby
65
+
66
+ require 'socket'
67
+
68
+ # Exception for closed engine
69
+ EngineClosed=Class.new(RuntimeError)
70
+ # Parse error
71
+ ParseError=Class.new(RuntimeError)
72
+
73
+ RinRuby_Env = ".RinRuby"
74
+ RinRuby_Endian = ([1].pack("L").unpack("C*")[0] == 1) ? (:little) : (:big)
75
+
76
+ attr_reader :interactive
77
+ attr_reader :readline
78
+ attr_reader :echo_enabled
79
+ attr_reader :executable
80
+ attr_reader :port_number
81
+ attr_reader :port_width
82
+ attr_reader :hostname
83
+
84
+ #RinRuby is invoked within a Ruby script (or the interactive "irb" prompt denoted >>) using:
85
+ #
86
+ # >> require "rinruby"
87
+ #
88
+ #The previous statement reads the definition of the RinRuby class into the current Ruby interpreter and creates an instance of the RinRuby class named R. There is a second method for starting an instance of R which allows the user to use any name for the instance, in this case myr:
89
+ #
90
+ # >> require "rinruby"
91
+ # >> myr = RinRuby.new
92
+ # >> myr.eval "rnorm(1)"
93
+ #
94
+ #Any number of independent instances of R can be created in this way.
95
+ #
96
+ #<b>Parameters that can be passed to the new method using a Hash:</b>
97
+ #
98
+ #* :echo: By setting the echo to false, output from R is suppressed, although warnings are still printed. This option can be changed later by using the echo method. The default is true.
99
+ #* :interactive: When interactive is false, R is run in non-interactive mode, resulting in plots without an explicit device being written to Rplots.pdf. Otherwise (i.e., interactive is true), plots are shown on the screen. The default is true.
100
+ #* :executable: The path of the R executable (which is "R" in Linux and Mac OS X, or "Rterm.exe" in Windows) can be set with the executable argument. The default is nil which makes RinRuby use the registry keys to find the path (on Windows) or use the path defined by $PATH (on Linux and Mac OS X).
101
+ #* :port_number: This is the smallest port number on the local host that could be used to pass data between Ruby and R. The actual port number used depends on port_width.
102
+ #* :port_width: RinRuby will randomly select a uniform number between port_number and port_number + port_width - 1 (inclusive) to pass data between Ruby and R. If the randomly selected port is not available, RinRuby will continue selecting random ports until it finds one that is available. By setting port_width to 1, RinRuby will wait until port_number is available. The default port_width is 1000.
103
+ #
104
+ #It may be desirable to change the parameters to the instance of R, but still call it by the name of R. In that case the old instance of R which was created with the 'require "rinruby"' statement should be closed first using the quit method which is explained below. Unless the previous instance is killed, it will continue to use system resources until exiting Ruby. The following shows an example by changing the parameter echo:
105
+ #
106
+ # >> require "rinruby"
107
+ # >> R.quit
108
+ # >> R = RinRuby.new(false)
109
+ def initialize(*args)
110
+ @opts = {:echo=>true, :interactive=>true, :executable=>nil,
111
+ :port_number=>38442, :port_width=>1000, :hostname=>'127.0.0.1', :persistent => true}
112
+ if args.size==1 and args[0].is_a? Hash
113
+ @opts.merge!(args[0])
114
+ else
115
+ [:echo, :interactive, :executable, :port_number, :port_width].zip(args).each{|k, v|
116
+ @opts[k] = ((v == nil) ? @opts[k] : v)
117
+ }
118
+ end
119
+ [:port_width, :executable, :hostname, :interactive, [:echo, :echo_enabled]].each{|k_src, k_dst|
120
+ Kernel.eval("@#{k_dst || k_src} = @opts[:#{k_src}]", binding)
121
+ }
122
+ @echo_stderr = false
123
+
124
+ raise Errno::EADDRINUSE unless (@port_number =
125
+ (@opts[:port_number]...(@opts[:port_number] + @opts[:port_width])).to_a.shuffle.find{|i|
126
+ begin
127
+ @server_socket = TCPServer::new(@hostname, i)
128
+ rescue Errno::EADDRINUSE
129
+ false
130
+ end
131
+ })
132
+
133
+ @platform = case RUBY_PLATFORM
134
+ when /mswin/, /mingw/, /bccwin/ then 'windows'
135
+ when /cygwin/ then 'windows-cygwin'
136
+ when /java/
137
+ require 'java' #:nodoc:
138
+ "#{java.lang.System.getProperty('os.name') =~ /[Ww]indows/ ? 'windows' : 'default'}-java"
139
+ else 'default'
140
+ end
141
+ @executable ||= ( @platform =~ /windows/ ) ? self.class.find_R_on_windows(@platform =~ /cygwin/) : 'R'
142
+
143
+ @platform_options = []
144
+ if @interactive then
145
+ if @executable =~ /Rterm\.exe["']?$/ then
146
+ @platform_options += ['--ess']
147
+ elsif @platform !~ /java$/ then
148
+ # intentionally interactive off under java
149
+ @platform_options += ['--no-readline', '--interactive']
150
+ end
151
+ end
152
+
153
+ cmd = %Q<#{executable} #{@platform_options.join(' ')} --slave>
154
+ cmd = (@platform =~ /^windows(?!-cygwin)/) ? "#{cmd} 2>NUL" : "exec #{cmd} 2>/dev/null"
155
+ if @platform_options.include?('--interactive') then
156
+ require 'pty'
157
+ @reader, @writer, @r_pid = PTY::spawn("stty -echo && #{cmd}")
158
+ else
159
+ @writer = @reader = IO::popen(cmd, 'w+')
160
+ @r_pid = @reader.pid
161
+ end
162
+ raise EngineClosed if (@reader.closed? || @writer.closed?)
163
+
164
+ @writer.puts <<-EOF
165
+ assign("#{RinRuby_Env}", new.env(), envir = globalenv())
166
+ EOF
167
+ @socket = nil
168
+ [:socket_io, :assign, :pull, :check].each{|fname| self.send("r_rinruby_#{fname}")}
169
+ @writer.flush
170
+
171
+ @eval_count = 0
172
+ eval("0", false) # cleanup @reader
173
+
174
+ # JRuby on *NIX runs forcefully in non-interactive, where stop() halts R execution immediately in default.
175
+ # To continue when R error occurs, an error handler is added as a workaround
176
+ # @see https://stat.ethz.ch/R-manual/R-devel/library/base/html/stop.html
177
+ eval("options(error=dump.frames)") if @platform =~ /^(?!windows-).*java$/
178
+ end
179
+
180
+ #The quit method will properly close the bridge between Ruby and R, freeing up system resources. This method does not need to be run when a Ruby script ends.
181
+
182
+ def quit
183
+ begin
184
+ @writer.puts "q(save='no')"
185
+ @writer.close
186
+ rescue
187
+ end
188
+ @reader.close rescue nil
189
+ @server_socket.close rescue nil
190
+ true
191
+ end
192
+
193
+ #The eval instance method passes the R commands contained in the supplied string and displays any resulting plots or prints the output. For example:
194
+ #
195
+ # >> sample_size = 10
196
+ # >> R.eval "x <- rnorm(#{sample_size})"
197
+ # >> R.eval "summary(x)"
198
+ # >> R.eval "sd(x)"
199
+ #
200
+ #produces the following:
201
+ #
202
+ # Min. 1st Qu. Median Mean 3rd Qu. Max.
203
+ # -1.88900 -0.84930 -0.45220 -0.49290 -0.06069 0.78160
204
+ # [1] 0.7327981
205
+ #
206
+ #This example used a string substitution to make the argument to first eval method equivalent to x <- rnorm(10). This example used three invocations of the eval method, but a single invoke is possible using a here document:
207
+ #
208
+ # >> R.eval <<EOF
209
+ # x <- rnorm(#{sample_size})
210
+ # summary(x)
211
+ # sd(x)
212
+ # EOF
213
+ #
214
+ #<b>Parameters that can be passed to the eval method</b>
215
+ #
216
+ #* string: The string parameter is the code which is to be passed to R, for example, string = "hist(gamma(1000,5,3))". The string can also span several lines of code by use of a here document, as shown:
217
+ # R.eval <<EOF
218
+ # x<-rgamma(1000,5,3)
219
+ # hist(x)
220
+ # EOF
221
+ #
222
+ #* echo_override: This argument allows one to set the echo behavior for this call only. The default for echo_override is nil, which does not override the current echo behavior.
223
+ #* b: echo block, which will be used as echo_override when echo_override equals to nil
224
+
225
+ def eval(string, echo_override = nil, &b)
226
+ echo_proc = case echo_override # echo on when echo_proc == nil
227
+ when Proc
228
+ echo_override
229
+ when nil
230
+ b || (@echo_enabled ? nil : proc{})
231
+ else
232
+ echo_override ? nil : proc{}
233
+ end
234
+
235
+ if_parseable(string){|fun|
236
+ eval_engine("#{fun}()", &echo_proc)
237
+ }
238
+ end
239
+
240
+ #When sending code to Ruby using an interactive prompt, this method will change the prompt to an R prompt. From the R prompt commands can be sent to R exactly as if the R program was actually running. When the user is ready to return to Ruby, then the command exit() will return the prompt to Ruby. This is the ideal situation for the explorative programmer who needs to run several lines of code in R, and see the results after each command. This is also an easy way to execute loops without the use of a here document. It should be noted that the prompt command does not work in a script, just Ruby's interactive irb.
241
+ #
242
+ #<b>Parameters that can be passed to the prompt method:</b>
243
+ #
244
+ #* regular_prompt: This defines the string used to denote the R prompt.
245
+ #
246
+ #* continue_prompt: This is the string used to denote R's prompt for an incomplete statement (such as a multiple for loop).
247
+
248
+ def prompt(regular_prompt="> ", continue_prompt="+ ")
249
+ warn "'interactive' mode is off in this session " unless @interactive
250
+
251
+ @readline ||= begin # initialize @readline at the first invocation
252
+ require 'readline'
253
+ proc{|prompt| Readline.readline(prompt, true)}
254
+ rescue LoadError
255
+ proc{|prompt|
256
+ print prompt
257
+ $stdout.flush
258
+ gets.strip rescue nil
259
+ }
260
+ end
261
+
262
+ cmds = []
263
+ while true
264
+ cmds << @readline.call(cmds.empty? ? regular_prompt : continue_prompt)
265
+ if cmds[-1] then # the last "nil" input suspend current stack
266
+ break if /^\s*exit\s*\(\s*\)\s*$/ =~ cmds[0]
267
+ begin
268
+ completed, eval_res = if_complete(cmds){|fun|
269
+ [true, eval_engine("#{fun}()")]
270
+ }
271
+ next unless completed
272
+ break unless eval_res
273
+ rescue ParseError => e
274
+ puts e.message
275
+ end
276
+ end
277
+ cmds = []
278
+ end
279
+ true
280
+ end
281
+
282
+ #If a method is called which is not defined, then it is assumed that the user is attempting to either pull or assign a variable to R. This allows for the short-hand equivalents to the pull and assign methods. For example:
283
+ #
284
+ # >> R.x = 2
285
+ #
286
+ #is the same as:
287
+ #
288
+ # >> R.assign("x",2)
289
+ #
290
+ #Also:
291
+ #
292
+ # >> n = R.x
293
+ #
294
+ #is the same as:
295
+ #
296
+ # >> n = R.pull("x")
297
+ #
298
+ #The parameters passed to method_missing are those used for the pull or assign depending on the context.
299
+
300
+ def method_missing(symbol, *args)
301
+ name = symbol.id2name
302
+ if name =~ /(.*)=$/
303
+ raise ArgumentError, "You shouldn't assign nil" if args==[nil]
304
+ super if args.length != 1
305
+ assign($1,args[0])
306
+ else
307
+ super if args.length != 0
308
+ pull(name)
309
+ end
310
+ end
311
+
312
+ #Data is copied from Ruby to R using the assign method or a short-hand equivalent. For example:
313
+ #
314
+ # >> names = ["Lisa","Teasha","Aaron","Thomas"]
315
+ # >> R.assign "people", names
316
+ # >> R.eval "sort(people)"
317
+ #
318
+ #produces the following :
319
+ #
320
+ # [1] "Aaron" "Lisa" "Teasha" "Thomas"
321
+ #
322
+ #The short-hand equivalent to the assign method is simply:
323
+ #
324
+ # >> R.people = names
325
+ #
326
+ #Some care is needed when using the short-hand of the assign method since the label (i.e., people in this case) must be a valid method name in Ruby. For example, R.copy.of.names = names will not work, but R.copy_of_names = names is permissible.
327
+ #
328
+ #The assign method supports Ruby variables of type Fixnum (i.e., integer), Bignum (i.e., integer), Float (i.e., double), String, and arrays of one of those three fundamental types. Note that Fixnum or Bignum values that exceed the capacity of R's integers are silently converted to doubles. Data in other formats must be coerced when copying to R.
329
+ #
330
+ #<b>Parameters that can be passed to the assign method:</b>
331
+ #
332
+ #* name: The name of the variable desired in R.
333
+ #
334
+ #* value: The value the R variable should have. The assign method supports Ruby variables of type Fixnum (i.e., integer), Bignum (i.e., integer), Float (i.e., double), String, and arrays of one of those three fundamental types. Note that Fixnum or Bignum values that exceed the capacity of R's integers are silently converted to doubles. Data in other formats must be coerced when copying to R.
335
+ #
336
+ #The assign method is an alternative to the simplified method, with some additional flexibility. When using the simplified method, the parameters of name and value are automatically used, in other words:
337
+ #
338
+ # >> R.test = 144
339
+ #
340
+ #is the same as:
341
+ #
342
+ # >> R.assign("test",144)
343
+ #
344
+ #Of course it would be confusing to use the shorthand notation to assign a variable named eval, echo, or any other already defined function. RinRuby would assume you were calling the function, rather than trying to assign a variable.
345
+ #
346
+ #When assigning an array containing differing types of variables, RinRuby will follow R's conversion conventions. An array that contains any Strings will result in a character vector in R. If the array does not contain any Strings, but it does contain a Float or a large integer (in absolute value), then the result will be a numeric vector of Doubles in R. If there are only integers that are sufficiently small (in absolute value), then the result will be a numeric vector of integers in R.
347
+
348
+ def assign(name, value)
349
+ if_assignable(name){|fun|
350
+ assign_engine(fun, value)
351
+ }
352
+ end
353
+
354
+ #Data is copied from R to Ruby using the pull method or a short-hand equivalent. The R object x defined with an eval method can be copied to Ruby object copy_of_x as follows:
355
+ #
356
+ # >> R.eval "x <- rnorm(10)"
357
+ # >> copy_of_x = R.pull "x"
358
+ # >> puts copy_of_x
359
+ #
360
+ #which produces the following :
361
+ #
362
+ # -0.376404489256671
363
+ # -1.0759798269397
364
+ # -0.494240140140996
365
+ # 0.131171385795721
366
+ # -0.878328334369391
367
+ # -0.762290423047929
368
+ # -0.410227216105828
369
+ # 0.0445512804225151
370
+ # -1.88887454545995
371
+ # 0.781602719849499
372
+ #
373
+ #RinRuby also supports a convenient short-hand notation when the argument to pull is simply a previously-defined R object (whose name conforms to Ruby's requirements for method names). For example:
374
+ #
375
+ # >> copy_of_x = R.x
376
+ #
377
+ #The explicit assign method, however, can take an arbitrary R statement. For example:
378
+ #
379
+ # >> summary_of_x = R.pull "as.numeric(summary(x))"
380
+ # >> puts summary_of_x
381
+ #
382
+ #produces the following:
383
+ #
384
+ # -1.889
385
+ # -0.8493
386
+ # -0.4522
387
+ # -0.4929
388
+ # -0.06069
389
+ # 0.7816
390
+ #
391
+ #Notice the use above of the as.numeric function in R. This is necessary since the pull method only supports R vectors which are numeric (i.e., integers or doubles) and character (i.e., strings). Data in other formats must be coerced when copying to Ruby.
392
+ #
393
+ #<b>Parameters that can be passed to the pull method:</b>
394
+ #
395
+ #* string: The name of the variable that should be pulled from R. The pull method only supports R vectors which are numeric (i.e., integers or doubles) or character (i.e., strings). The R value of NA is pulled as nil into Ruby. Data in other formats must be coerced when copying to Ruby.
396
+ #
397
+ #* singletons: R represents a single number as a vector of length one, but in Ruby it is often more convenient to use a number rather than an array of length one. Setting singleton=false will cause the pull method to shed the array, while singletons=true will return the number of string within an array. The default is false.
398
+ #
399
+ #The pull method is an alternative to the simplified form where the parameters are automatically used. For example:
400
+ #
401
+ # >> puts R.test
402
+ #
403
+ #is the same as:
404
+ #
405
+ # >> puts R.pull("test")
406
+
407
+ def pull(string, singletons=false)
408
+ if_parseable(string){|fun|
409
+ pull_engine("#{fun}()", singletons)
410
+ }
411
+ end
412
+
413
+ #The echo method controls whether the eval method displays output from R and, if echo is enabled, whether messages, warnings, and errors from stderr are also displayed.
414
+ #
415
+ #<b>Parameters that can be passed to the eval method</b>
416
+ #
417
+ #* enable: Setting enable to false will turn all output off until the echo command is used again with enable equal to true. The default is nil, which will return the current setting.
418
+ #
419
+ #* stderr: Setting stderr to true will force messages, warnings, and errors from R to be routed through stdout. Using stderr redirection is typically not needed, and is thus disabled by default. Echoing must be enabled when using stderr redirection.
420
+
421
+ def echo(enable=nil, stderr=nil)
422
+ next_enabled = (enable == nil) ? @echo_enabled : (enable ? true : false)
423
+ next_stderr = case stderr
424
+ when nil
425
+ (next_enabled ? @echo_stderr : false)
426
+ else
427
+ (stderr ? true : false)
428
+ end
429
+
430
+ if (next_enabled == false) && (next_stderr == true) then # prohibited combination
431
+ raise "You can only redirect stderr if you are echoing is enabled."
432
+ end
433
+
434
+ if @echo_stderr != next_stderr then
435
+ @writer.print(<<-__TEXT__)
436
+ sink(#{'stdout(),' if next_stderr}type='message')
437
+ __TEXT__
438
+ @writer.flush
439
+ end
440
+ [@echo_enabled = next_enabled, @echo_stderr = next_stderr]
441
+ end
442
+
443
+ def echo_enabled=(enable)
444
+ echo(enable).first
445
+ end
446
+
447
+ private
448
+
449
+ #:stopdoc:
450
+ RinRuby_Type_NotFound = -2
451
+ RinRuby_Type_Unknown = -1
452
+ [
453
+ :Logical,
454
+ :Integer,
455
+ :Double,
456
+ :Character,
457
+ :Matrix,
458
+ ].each_with_index{|type, i|
459
+ Kernel.eval("RinRuby_Type_#{type} = i", binding)
460
+ }
461
+
462
+ RinRuby_Socket = "#{RinRuby_Env}$socket"
463
+ RinRuby_Test_String = "#{RinRuby_Env}$test.string"
464
+ RinRuby_Test_Result = "#{RinRuby_Env}$test.result"
465
+
466
+ RinRuby_Eval_Flag = "RINRUBY.EVAL.FLAG"
467
+
468
+ RinRuby_NA_R_Integer = -(1 << 31)
469
+ RinRuby_Max_R_Integer = (1 << 31) - 1
470
+ RinRuby_Min_R_Integer = -(1 << 31) + 1
471
+ #:startdoc:
472
+
473
+ def r_rinruby_socket_io
474
+ @writer.print <<-EOF
475
+ #{RinRuby_Socket} <- NULL
476
+ #{RinRuby_Env}$session <- function(f){
477
+ invisible(f(#{RinRuby_Socket}))
478
+ }
479
+ #{RinRuby_Env}$session.write <- function(writer){
480
+ #{RinRuby_Env}$session(function(con){
481
+ writer(function(v, ...){
482
+ invisible(lapply(list(v, ...), function(v2){
483
+ writeBin(v2, con, endian="#{RinRuby_Endian}")}))
484
+ })
485
+ })
486
+ }
487
+ #{RinRuby_Env}$session.read <- function(reader){
488
+ #{RinRuby_Env}$session(function(con){
489
+ reader(function(vtype, len){
490
+ invisible(readBin(con, vtype(), len, endian="#{RinRuby_Endian}"))
491
+ }, function(bytes){
492
+ invisible(readChar(con, bytes, useBytes = T))
493
+ })
494
+ })
495
+ }
496
+ EOF
497
+ end
498
+
499
+ def r_rinruby_check
500
+ @writer.print <<-EOF
501
+ #{RinRuby_Env}$parseable <- function(var) {
502
+ src <- srcfilecopy("<text>", lines=var, isFile=F)
503
+ parsed <- try(parse(text=var, srcfile=src, keep.source=T), silent=TRUE)
504
+ res <- function(){eval(parsed, env=globalenv())} # return evaluating function
505
+ notification <- if(inherits(parsed, "try-error")){
506
+ attributes(res)$parse.data <- getParseData(src)
507
+ 0L
508
+ }else{
509
+ 1L
510
+ }
511
+ #{RinRuby_Env}$session.write(function(write){
512
+ write(notification)
513
+ })
514
+ invisible(res)
515
+ }
516
+ #{RinRuby_Env}$last.parse.data <- function(data) {
517
+ if(nrow(data) == 0L){
518
+ c(0L, 0L, 0L)
519
+ }else{
520
+ endline <- data[max(data$line2) == data$line2, ]
521
+ last.item <- endline[max(endline$col2) == endline$col2, ]
522
+ eval(substitute(c(line2, col2, token == "';'"), last.item))
523
+ }
524
+ }
525
+ #{RinRuby_Env}$assignable <- function(var) {
526
+ parsed <- try(parse(text=paste0(var, ' <- .value')), silent=TRUE)
527
+ is_invalid <- inherits(parsed, "try-error") || (length(parsed) != 1L)
528
+ #{RinRuby_Env}$session.write(function(write){
529
+ write(ifelse(is_invalid, 0L, 1L))
530
+ })
531
+ invisible(#{RinRuby_Env}$assign(var)) # return assigning function
532
+ }
533
+ EOF
534
+ end
535
+ # Create function on ruby to get values
536
+ def r_rinruby_assign
537
+ @writer.print <<-EOF
538
+ #{RinRuby_Env}$assign <- function(var) {
539
+ expr <- parse(text=paste0(var, " <- #{RinRuby_Env}$.value"))
540
+ invisible(function(.value){
541
+ #{RinRuby_Env}$.value <- .value
542
+ eval(expr, envir=globalenv())
543
+ })
544
+ }
545
+ #{RinRuby_Env}$assign.test.string <-
546
+ #{RinRuby_Env}$assign("#{RinRuby_Test_String}")
547
+ #{RinRuby_Env}$get_value <- function() {
548
+ #{RinRuby_Env}$session.read(function(read, readchar){
549
+ value <- NULL
550
+ type <- read(integer, 1)
551
+ length <- read(integer, 1)
552
+ na.indices <- function(){
553
+ read(integer, read(integer, 1)) + 1L
554
+ }
555
+ if ( type == #{RinRuby_Type_Logical} ) {
556
+ value <- read(logical, length)
557
+ } else if ( type == #{RinRuby_Type_Integer} ) {
558
+ value <- read(integer, length)
559
+ } else if ( type == #{RinRuby_Type_Double} ) {
560
+ value <- read(double, length)
561
+ value[na.indices()] <- NA
562
+ } else if ( type == #{RinRuby_Type_Character} ) {
563
+ value <- character(length)
564
+ for(i in seq_len(length)){
565
+ nbytes <- read(integer, 1)
566
+ value[[i]] <- ifelse(nbytes >= 0, readchar(nbytes), NA)
567
+ }
568
+ }
569
+ value
570
+ })
571
+ }
572
+ EOF
573
+ end
574
+
575
+ def r_rinruby_pull
576
+ @writer.print <<-EOF
577
+ #{RinRuby_Env}$pull <- function(var){
578
+ #{RinRuby_Env}$session.write(function(write){
579
+ if ( inherits(var ,"try-error") ) {
580
+ write(#{RinRuby_Type_NotFound}L)
581
+ } else {
582
+ na.indices <- function(){
583
+ indices <- which(is.na(var) & (!is.nan(var))) - 1L
584
+ write(length(indices), indices)
585
+ }
586
+ if (is.matrix(var)) {
587
+ write(#{RinRuby_Type_Matrix}L, nrow(var), ncol(var))
588
+ } else if ( is.logical(var) ) {
589
+ write(#{RinRuby_Type_Logical}L, length(var), as.integer(var))
590
+ } else if ( is.integer(var) ) {
591
+ write(#{RinRuby_Type_Integer}L, length(var), var)
592
+ } else if ( is.double(var) ) {
593
+ write(#{RinRuby_Type_Double}L, length(var), var)
594
+ na.indices()
595
+ } else if ( is.character(var) ) {
596
+ write(#{RinRuby_Type_Character}L, length(var))
597
+ for(i in var){
598
+ if( is.na(i) ){
599
+ write(as.integer(NA))
600
+ }else{
601
+ write(nchar(i, type="bytes"), i)
602
+ }
603
+ }
604
+ } else {
605
+ write(#{RinRuby_Type_Unknown}L)
606
+ }
607
+ }
608
+ })
609
+ }
610
+ EOF
611
+ end
612
+
613
+ def socket_session(&b)
614
+ socket = @socket
615
+ # TODO check still available connection?
616
+ unless socket then
617
+ t = Thread::new{socket = @server_socket.accept}
618
+ @writer.print <<-EOF
619
+ #{RinRuby_Socket} <- socketConnection( \
620
+ "#{@hostname}", #{@port_number}, blocking=TRUE, open="rb")
621
+ EOF
622
+ @writer.puts(
623
+ "on.exit(close(#{RinRuby_Socket}, add = T))") if @opts[:persistent]
624
+ @writer.flush
625
+ t.join
626
+ end
627
+ keep_socket = @opts[:persistent]
628
+ res = nil
629
+ begin
630
+ res = b.call(socket)
631
+ rescue
632
+ keep_socket = false
633
+ raise $!
634
+ ensure
635
+ if keep_socket
636
+ @socket = socket
637
+ else
638
+ @socket = nil
639
+ @writer.print <<-EOF
640
+ close(#{RinRuby_Socket}); \
641
+ #{RinRuby_Socket} <- NULL
642
+ EOF
643
+ @writer.flush
644
+ socket.close
645
+ end
646
+ end
647
+ res
648
+ end
649
+
650
+ class R_DataType
651
+ ID = RinRuby_Type_Unknown
652
+ class <<self
653
+ def convertable?(value)
654
+ false
655
+ end
656
+ def ===(value)
657
+ convertable?(value)
658
+ end
659
+ def send(value, io)
660
+ nil
661
+ end
662
+ def receive(io)
663
+ nil
664
+ end
665
+ end
666
+ end
667
+
668
+ class R_Logical < R_DataType
669
+ ID = RinRuby_Type_Logical
670
+ CONVERT_TABLE = Hash[*({
671
+ true => 1,
672
+ false => 0,
673
+ nil => RinRuby_NA_R_Integer,
674
+ }.collect{|k, v|
675
+ [k, [v].pack('l')]
676
+ }.flatten)]
677
+ class <<self
678
+ def convertable?(value)
679
+ value.all?{|x| [true, false, nil].include?(x)}
680
+ end
681
+ def send(value, io)
682
+ # Logical format: size, data, ...
683
+ io.write([value.size].pack('l'))
684
+ value.each{|x|
685
+ io.write(CONVERT_TABLE[x])
686
+ }
687
+ end
688
+ def receive(io)
689
+ length = io.read(4).unpack('l').first
690
+ io.read(4 * length).unpack("l*").collect{|v|
691
+ (v == RinRuby_NA_R_Integer) ? nil : (v > 0)
692
+ }
693
+ end
694
+ end
695
+ end
696
+
697
+ class R_Integer < R_DataType
698
+ ID = RinRuby_Type_Integer
699
+ class <<self
700
+ def convertable?(value)
701
+ value.all?{|x|
702
+ (x == nil) ||
703
+ (x.kind_of?(Integer) && (x >= RinRuby_Min_R_Integer) && (x <= RinRuby_Max_R_Integer))
704
+ }
705
+ end
706
+ def send(value, io)
707
+ # Integer format: size, data, ...
708
+ io.write([value.size].pack('l'))
709
+ value.each{|x|
710
+ io.write([(x == nil) ? RinRuby_NA_R_Integer : x].pack('l'))
711
+ }
712
+ end
713
+ def receive(io)
714
+ length = io.read(4).unpack('l').first
715
+ io.read(4 * length).unpack("l*").collect{|v|
716
+ (v == RinRuby_NA_R_Integer) ? nil : v
717
+ }
718
+ end
719
+ end
720
+ end
721
+
722
+ class R_Double < R_DataType
723
+ ID = RinRuby_Type_Double
724
+ class <<self
725
+ def convertable?(value)
726
+ value.all?{|x|
727
+ (x == nil) || x.kind_of?(Numeric)
728
+ }
729
+ end
730
+ def send(value, io)
731
+ # Double format: data_size, data, ..., na_index_size, na_index, ...
732
+ io.write([value.size].pack('l'))
733
+ nils = []
734
+ value.each.with_index{|x, i|
735
+ if x == nil then
736
+ nils << i
737
+ io.write([Float::NAN].pack('D'))
738
+ else
739
+ io.write([x.to_f].pack('D'))
740
+ end
741
+ }
742
+ io.write(([nils.size] + nils).pack('l*'))
743
+ value
744
+ end
745
+ def receive(io)
746
+ length = io.read(4).unpack('l').first
747
+ res = io.read(8 * length).unpack("D*")
748
+ na_indices = io.read(4).unpack('l').first
749
+ io.read(4 * na_indices).unpack("l*").each{|i| res[i] = nil}
750
+ res
751
+ end
752
+ end
753
+ end
754
+
755
+ class R_Character < R_DataType
756
+ ID = RinRuby_Type_Character
757
+ class <<self
758
+ def convertable?(value)
759
+ value.all?{|x|
760
+ (x == nil) || x.kind_of?(String)
761
+ }
762
+ end
763
+ def send(value, io)
764
+ # Character format: data_size, data1_bytes, data1, data2_bytes, data2, ...
765
+ io.write([value.size].pack('l'))
766
+ value.each{|x|
767
+ if x then
768
+ bytes = x.to_s.bytes # TODO: taking care of encoding difference
769
+ io.write(([bytes.size] + bytes).pack('lC*')) # .bytes.pack("C*").encoding equals to "ASCII-8BIT"
770
+ else
771
+ io.write([RinRuby_NA_R_Integer].pack('l'))
772
+ end
773
+ }
774
+ value
775
+ end
776
+ def receive(io)
777
+ length = io.read(4).unpack('l').first
778
+ Array.new(length){|i|
779
+ nchar = io.read(4).unpack('l')[0]
780
+ # negative nchar means NA, and "+ 1" for zero-terminated string
781
+ (nchar >= 0) ? io.read(nchar + 1)[0..-2] : nil
782
+ }
783
+ end
784
+ end
785
+ end
786
+
787
+ def assign_engine(fun, value, r_type = nil)
788
+ raise EngineClosed if @writer.closed?
789
+
790
+ original_value = value
791
+
792
+ r_exp = "#{fun}(#{RinRuby_Env}$get_value())"
793
+
794
+ if value.kind_of?(::Matrix) # assignment for matrices
795
+ r_exp = "#{fun}(matrix(#{RinRuby_Env}$get_value(), " \
796
+ "nrow=#{value.row_size}, ncol=#{value.column_size}, byrow=T))"
797
+ value = value.row_vectors.collect{|row| row.to_a}.flatten
798
+ elsif !value.kind_of?(Enumerable) then # check each
799
+ value = [value]
800
+ end
801
+
802
+ r_type ||= [
803
+ R_Logical,
804
+ R_Integer,
805
+ R_Double,
806
+ R_Character,
807
+ ].find{|k|
808
+ k === value
809
+ }
810
+ raise "Unsupported data type on Ruby's end" unless r_type
811
+
812
+ socket_session{|socket|
813
+ @writer.puts(r_exp)
814
+ @writer.flush
815
+ socket.write([r_type::ID].pack('l'))
816
+ r_type.send(value, socket)
817
+ }
818
+
819
+ original_value
820
+ end
821
+
822
+ def pull_engine(string, singletons = true)
823
+ raise EngineClosed if @writer.closed?
824
+
825
+ pull_proc = proc{|var, socket|
826
+ @writer.puts "#{RinRuby_Env}$pull(try(#{var}))"
827
+ @writer.flush
828
+ type = socket.read(4).unpack('l').first
829
+ case type
830
+ when RinRuby_Type_Unknown
831
+ raise "Unsupported data type on R's end"
832
+ when RinRuby_Type_NotFound
833
+ next nil
834
+ when RinRuby_Type_Matrix
835
+ rows, cols = socket.read(8).unpack('l*')
836
+ next Matrix.rows( # get rowwise flatten vector
837
+ [pull_proc.call("as.vector(t(#{var}))", socket)].flatten.each_slice(cols).to_a,
838
+ false)
839
+ end
840
+
841
+ r_type = [
842
+ R_Logical,
843
+ R_Integer,
844
+ R_Double,
845
+ R_Character,
846
+ ].find{|k|
847
+ k::ID == type
848
+ }
849
+
850
+ raise "Unsupported data type on Ruby's end" unless r_type
851
+
852
+ res = r_type.receive(socket)
853
+ (!singletons) && (res.size == 1) ? res[0] : res
854
+ }
855
+ socket_session{|socket|
856
+ pull_proc.call(string, socket)
857
+ }
858
+ end
859
+
860
+ def if_passed(string, r_func, opt = {}, &then_proc)
861
+ assign_engine("#{RinRuby_Env}$assign.test.string", string, R_Character)
862
+ res = socket_session{|socket|
863
+ @writer.puts "#{RinRuby_Test_Result} <- #{r_func}(#{RinRuby_Test_String})"
864
+ @writer.flush
865
+ socket.read(4).unpack('l').first > 0
866
+ }
867
+ unless res then
868
+ raise ParseError, "Parse error: #{string}" unless opt[:error_proc]
869
+ opt[:error_proc].call(RinRuby_Test_Result)
870
+ return false
871
+ end
872
+ then_proc ? then_proc.call(RinRuby_Test_Result) : true
873
+ end
874
+ def if_parseable(string, opt = {}, &then_proc)
875
+ if_passed(string, "#{RinRuby_Env}$parseable", opt, &then_proc)
876
+ end
877
+ def if_assignable(name, opt = {}, &then_proc)
878
+ if_passed(name, "#{RinRuby_Env}$assignable", opt, &then_proc)
879
+ end
880
+
881
+ def if_complete(lines, &then_proc)
882
+ if_parseable(lines, {
883
+ :error_proc => proc{|var|
884
+ # extract last parsed position
885
+ l2, c2, is_separator = pull_engine(
886
+ "#{RinRuby_Env}$last.parse.data(attr(#{var}, 'parse.data'))")
887
+
888
+ # detect unrecoverable error
889
+ l2_max = lines.size + is_separator
890
+ while (l2 > 0) and (l2 <= l2_max) # parse completion is before or on the last line
891
+ end_line = lines[l2 - 1]
892
+ break if (l2 == l2_max) and (end_line[c2..-1] =~ /^\s*$/)
893
+ raise ParseError, <<-__TEXT__
894
+ Unrecoverable parse error: #{end_line}
895
+ #{' ' * (c2 - 1)}^...
896
+ __TEXT__
897
+ end
898
+ }
899
+ }, &then_proc)
900
+ end
901
+
902
+ def complete?(string)
903
+ if_complete(string.lines)
904
+ end
905
+ public :complete?
906
+
907
+ def eval_engine(r_expr, &echo_proc)
908
+ raise EngineClosed if (@writer.closed? || @reader.closed?)
909
+
910
+ run_num = (@eval_count += 1)
911
+ @writer.print(<<-__TEXT__)
912
+ {#{r_expr}}
913
+ print('#{RinRuby_Eval_Flag}.#{run_num}')
914
+ __TEXT__
915
+ @writer.flush
916
+
917
+ echo_proc ||= proc{|raw, stripped|
918
+ puts stripped.chomp("")
919
+ $stdout.flush
920
+ }
921
+
922
+ res = false
923
+ t = Thread::new{
924
+ while (line = @reader.gets)
925
+ # TODO I18N; force_encoding('origin').encode('UTF-8')
926
+ case (stripped = line.gsub(/\x1B\[[0-?]*[ -\/]*[@-~]/, '')) # drop escape sequence
927
+ when /\"#{RinRuby_Eval_Flag}\.(\d+)\"/
928
+ next if $1.to_i != run_num
929
+ res = true
930
+ break
931
+ end
932
+ echo_proc.call(line, stripped)
933
+ end
934
+ }
935
+
936
+ int_received = false
937
+ int_handler_orig = Signal.trap(:INT){
938
+ Signal.trap(:INT){} # ignore multiple reception
939
+ int_received = true
940
+ if @executable =~ /Rterm\.exe["']?$/
941
+ @writer.print [0x1B].pack('C') # simulate ESC key
942
+ @writer.flush
943
+ else
944
+ Process.kill(:INT, @r_pid)
945
+ end
946
+ t.kill
947
+ }
948
+
949
+ begin
950
+ t.join
951
+ ensure
952
+ Signal.trap(:INT, int_handler_orig)
953
+ Process.kill(:INT, $$) if int_received
954
+ end
955
+ res
956
+ end
957
+
958
+ class << self
959
+ # Remove invalid byte sequence
960
+ if RUBY_VERSION >= "2.1.0" then
961
+ define_method(:scrub){|str| str.scrub}
962
+ elsif RUBY_VERSION >= "1.9.0" then
963
+ define_method(:scrub){|str| str.chars.collect{|c| (c.valid_encoding?) ? c : '*'}.join}
964
+ else
965
+ define_method(:scrub){|str| str}
966
+ end
967
+
968
+ def find_R_dir_on_windows(cygwin = false, &b)
969
+ res = []
970
+ b ||= proc{}
971
+
972
+ # Firstly, check registry
973
+ ['HKEY_LOCAL_MACHINE', 'HKEY_CURRENT_USER'].each{|root|
974
+ if cygwin then
975
+ [:w, :W].collect{|opt| # [64bit, then 32bit registry]
976
+ [:R64, :R].collect{|mode|
977
+ `regtool list -#{opt} /#{root}/Software/R-core/#{mode} 2>/dev/null`.lines.collect{|v|
978
+ v =~ /^\d\.\d\.\d/ ? $& : nil
979
+ }.compact.sort{|a, b| # latest version has higher priority
980
+ b <=> a
981
+ }.collect{|ver|
982
+ ["-#{opt}", "/#{root}/Software/R-core/#{mode}/#{ver}/InstallPath"]
983
+ }
984
+ }
985
+ }.flatten(2).each{|args|
986
+ v = `cygpath '#{`regtool get #{args.join(' ')}`.strip}'`.strip
987
+ b.call((res << v)[-1]) unless (v.empty? || res.include?(v))
988
+ }
989
+ else
990
+ scrub(`reg query "#{root}\\Software\\R-core" /v "InstallPath" /s 2>nul`).each_line{|line|
991
+ next unless line.strip =~ /^\s*InstallPath\s+REG_SZ\s+(.+)/
992
+ b.call((res << $1)[-1]) unless res.include?($1)
993
+ }
994
+ end
995
+ }
996
+
997
+ # Secondly, check default install path
998
+ ["Program Files", "Program Files (x86)"].each{|prog_dir|
999
+ Dir::glob(File::join(cygwin ? "/cygdrive/c" : "C:", prog_dir, "R", "*")).each{|path|
1000
+ b.call((res << path)[-1]) unless res.include?(path)
1001
+ }
1002
+ }
1003
+
1004
+ res
1005
+ end
1006
+
1007
+ def find_R_on_windows(cygwin = false)
1008
+ return 'R' if cygwin && system('which R > /dev/nul 2>&1')
1009
+
1010
+ find_R_dir_on_windows(cygwin){|path|
1011
+ ['bin', 'bin/x64', 'bin/i386'].product(
1012
+ cygwin ? [path.gsub(' ','\ '), path] : [path.gsub('\\','/')]).each{|bin_dir, base_dir|
1013
+ r_exe = File::join(base_dir, bin_dir, "Rterm.exe")
1014
+ return %Q<"#{r_exe}"> if File.exists?(r_exe)
1015
+ }
1016
+ }
1017
+ raise "Cannot locate R executable"
1018
+ end
1019
+ end
1020
+
1021
+ end
1022
+
1023
+ if ! defined?(R)
1024
+ #R is an instance of RinRuby. If for some reason the user does not want R to be initialized (to save system resources), then create a default value for R (e.g., <b>R=2</b> ) in which case RinRuby will not overwrite the value of R.
1025
+
1026
+ R = RinRuby.new
1027
+ end
1028
+