rinruby 2.0.3 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e778d372d5d1595f97fe69f2986f96fa9a56bf86
4
+ data.tar.gz: a767fb4e5e793719268c3a561a6bb3a7201a9a65
5
+ SHA512:
6
+ metadata.gz: f87a7359c4fd94d9a100ecf3ddfe40d2e7016a1e1f5d6144ad799feed1f8d3dc07272f9c4ceae8945cd926b011dc7320d6760b3281b6d38f25ad7d8fbc0d9b31
7
+ data.tar.gz: 6fb9adbe15972112b5a5a28cb4ccd783495ddbdc8c3a10e85fc95dbdfdcb6955fb2a56bdbcf6af4d4f366e60b5d0dc943220541df35bb439e0f3652ba5bd91fe
@@ -0,0 +1,4 @@
1
+ Gemfile.lock
2
+ pkg
3
+ doc
4
+ *~
@@ -0,0 +1,24 @@
1
+ language:
2
+ ruby
3
+
4
+ rvm:
5
+ - '2.1.1'
6
+ - '2.4.4'
7
+ - '2.5.1'
8
+ - jruby-head
9
+ # - rbx-2.1.1
10
+ cache: bundler
11
+ script:
12
+ bundle exec rspec
13
+
14
+ before_install:
15
+ - sudo apt-get update -qq
16
+ - sudo apt-get install -y r-base r-base-dev
17
+ before_script:
18
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
19
+ - chmod +x ./cc-test-reporter
20
+ - ./cc-test-reporter before-build
21
+ after_script:
22
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
23
+ env:
24
+ TEST_TRAVIS=1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rinruby.gemspec
4
+ gemspec
@@ -1,8 +1,51 @@
1
+ === 2.1.0 / 2018-08-19
2
+
3
+ 638df01 Ruby 2.0.0 deprecated
4
+ 86d2370 Omit to_signed_int() with signed pack for speed optimization
5
+ 7c182ec Use native endian on Ruby side for speed optimization
6
+ ca8c9a1 Add corner case tests
7
+ 555e7d2 Add logical value pull tests
8
+ 312b9c9 Add logical value assign/pull
9
+ 350ce77 Change type from String to String_Array when assign based on procedure
10
+ e2ce0e1 Optimize pull engine
11
+ c22f821 Separate numeric pull test into integer and float tests
12
+ 1ba51e1 Add after{r.quit} to each example because of socket reusability
13
+ 7f59977 Revive echo and interactive tests, and add comment of test cache
14
+ a034124 Fix to use a cached RinRuby instance
15
+ a8f4cf1 Update README.md
16
+ 17baae9 Update matrix assignment test to use a large matrix
17
+ 1a147fd Optimize assign_engine()
18
+ 8418a65 Change string assignment scheme to accept array in a same session
19
+ 8c6fd6a Add cleanup for socket when connection is persistent
20
+ 0ddab52 Add persistent mode to reuse connection, which is now default.
21
+ aa7c90e Optimize performing singleton of pull returned value
22
+ 1a29474 Move R socket manipulation procedure into socket_seession() on Ruby
23
+ e1b96ba Refactoring; move socket.close from each procedure to socket_session
24
+ de954ed Reduce namespace contamination by using ".RinRuby" environment
25
+ 9519355 Move socketConnection() and close() to session()
26
+ 59ffe63 Move rinruby R function assignment from global to base environment
27
+ 13056c4 Change socket connection scheme for GC resistance
28
+ 97cf371 Perform communication via #{RinRuby_Socket}.write and read functions
29
+ 0a2b411 Remove duplication by using dummy R constant temporary
30
+ 964d0ae Improve possibility to find R by using regtool on cygwin
31
+ 89b7304 Add the R wrapper class without R constant
32
+ bba176b Add a safety net when Windows R is not registered in registry
33
+ 67afbe1 Internationalization; workaround for "invalid byte sequence in UTF-8"
34
+ 61d0817 Change registry search query because of structure change of R3.4.3
35
+ 9433299 Change strategy to find R executable for cygwin-windows
36
+ 2515984 Error in README.md example code
37
+ 8ff82c1 Markdownify README.md
38
+ e09cc95 Rename README.txt to README.md
39
+ 1b05f3e added support for bundler and gemspec
40
+
1
41
  === 2.0.3 / 2012-07-31
2
- * 64-bit version is in x64
3
- * Adapt to different Rterm.exe locations. On Windows at least, Rterm.exe has moved under an i386/ folder
4
- * Update README.txt
5
- * Updated spec to rspec 2.0
42
+ * Update README.txt
43
+
44
+ === 2.0.2 / 2011-02-03
45
+
46
+ * Updated RSpec to 2.0. Should work on Windows [clbustos]
47
+ * 64-bit version is in x64 [Rob Heittman]
48
+ * Adapt to different Rterm.exe locations [Rob Heittman]
6
49
 
7
50
  === 2.0.1 / 2010-05-01
8
51
  * Reimplemented Rinruby.new with ordered parameters, for complete backwards compatibility
@@ -4,6 +4,8 @@ Manifest.txt
4
4
  README.txt
5
5
  Rakefile
6
6
  lib/rinruby.rb
7
+ lib/rinruby_without_r_constant.rb
7
8
  spec/rinruby_spec.rb
9
+ spec/rinruby_without_r_constant_spec.rb
8
10
  spec/spec.opts
9
11
  spec/spec_helper.rb
@@ -1,29 +1,38 @@
1
- = rinruby
2
-
1
+ # rinruby
3
2
  * http://rinruby.ddahl.org/
4
3
 
5
- == DESCRIPTION:
6
4
 
7
- 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.
5
+ [![Build Status](https://travis-ci.org/clbustos/rinruby.svg?branch=master)](https://travis-ci.org/clbustos/rinruby)
6
+
7
+ [![Maintainability](https://api.codeclimate.com/v1/badges/d6cdb002c01f4a696ff3/maintainability)](https://codeclimate.com/github/clbustos/rinruby/maintainability)
8
+
9
+ ### DESCRIPTION
10
+
11
+ 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.
8
12
 
9
13
 
10
14
  Copyright 2005-2008 David B. Dahl
11
15
 
12
- Developed by David B. Dahl
13
- Documented by David B. Dahl and Scott Crawford
16
+ Developed by David B. Dahl. Documented by David B. Dahl and Scott Crawford
17
+
14
18
  Homepage: http://rinruby.ddahl.org
15
19
 
16
- Contributors: Claudio Bustos
20
+ *Maintainer*: Claudio Bustos
21
+
22
+ *Contributors*:
23
+
24
+ - [fenrir-naru](https://fenrir.naruoka.org)
17
25
 
18
- == FEATURES/PROBLEMS:
26
+ ### FEATURES/PROBLEMS
19
27
 
20
- * Pure Ruby. Works on Ruby 1.8.7, 1.9 and JRuby 1.4
28
+ * Pure Ruby. Works on Ruby 2.1, 2.2, 2.4 and JRuby-head (2018/03/29). There isn't any specific code that impides to use Ruby < 2.0, but is deprecated.
21
29
  * Slower than RSRuby, but more robust
22
30
 
23
- == SYNOPSIS:
31
+ ### SYNOPSIS
32
+
24
33
  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.
25
34
 
26
- <b>Code</b>:
35
+ #### Code
27
36
 
28
37
  require "rinruby"
29
38
  n = 10
@@ -33,34 +42,34 @@ Below is a simple example of RinRuby usage for simple linear regression. The sim
33
42
  seed = 23423
34
43
  R.x = (1..n).entries
35
44
  R.eval <<EOF
36
- set.seed({seed})
37
- y <- {beta_0} + {beta_1}*x + rnorm({n})
45
+ set.seed(#{seed})
46
+ y <- #{beta_0} + #{beta_1}*x + rnorm(#{n})
38
47
  fit <- lm( y ~ x )
39
48
  est <- round(coef(fit),3)
40
49
  pvalue <- summary(fit)$coefficients[2,4]
41
50
  EOF
42
- puts "E(y|x) ~= {R.est[0]} + {R.est[1]} * x"
51
+ puts "E(y|x) ~= #{R.est[0]} + #{R.est[1]} * x"
43
52
  if R.pvalue < alpha
44
53
  puts "Reject the null hypothesis and conclude that x and y are related."
45
54
  else
46
55
  puts "There is insufficient evidence to conclude that x and y are related."
47
56
  end
48
57
 
49
- <b>Output</b>:
58
+ #### Output
50
59
 
51
60
  E(y|x) ~= 1.264 + 0.273 * x
52
61
  Reject the null hypothesis and conclude that x and y are related.
53
62
 
54
- == REQUIREMENTS:
63
+ ### REQUIREMENTS
55
64
 
56
65
  * R
57
66
 
58
- == INSTALL:
67
+ ### INSTALL
59
68
 
60
69
  * sudo gem install rinruby
61
70
 
62
71
 
63
- == LICENSE:
72
+ ### LICENSE
64
73
 
65
74
  GPL-3. See LICENSE.txt for more information.
66
75
 
data/Rakefile CHANGED
@@ -14,8 +14,7 @@ Hoe.spec 'rinruby' do
14
14
  # self.rubyforge_name = 'rinruby' # if different than 'rinruby2'
15
15
  self.developer('David Dahl', 'rinruby_AT_ddahl.org')
16
16
  self.developer('Claudio Bustos', 'clbustos_AT_gmail.com')
17
- self.url = "http://rinruby.ddahl.org/"
18
-
17
+ self.urls = ["http://rinruby.ddahl.org/"]
19
18
  end
20
19
 
21
20
  # vim: syntax=ruby
@@ -2,7 +2,7 @@
2
2
  #
3
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
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.
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
6
  #
7
7
  #<b>Code</b>:
8
8
  #
@@ -34,6 +34,7 @@
34
34
  #
35
35
  #Coded by:: David B. Dahl
36
36
  #Documented by:: David B. Dahl & Scott Crawford
37
+ #Maintained by:: Claudio Bustos
37
38
  #Copyright:: 2005-2009
38
39
  #Web page:: http://rinruby.ddahl.org
39
40
  #E-mail:: mailto:rinruby@ddahl.org
@@ -58,20 +59,20 @@
58
59
  #The files "java" and "readline" are used when available to add functionality.
59
60
  require 'matrix'
60
61
  class RinRuby
61
-
62
+
62
63
  require 'socket'
63
64
 
64
-
65
65
  VERSION = '2.0.3'
66
66
 
67
-
68
67
  attr_reader :interactive
69
68
  attr_reader :readline
70
69
  # Exception for closed engine
71
70
  EngineClosed=Class.new(Exception)
72
71
  # Parse error
73
72
  ParseError=Class.new(Exception)
74
-
73
+
74
+ RinRuby_Env = ".RinRuby"
75
+ RinRuby_Endian = ([1].pack("L").unpack("C*")[0] == 1) ? (:little) : (:big)
75
76
 
76
77
  #RinRuby is invoked within a Ruby script (or the interactive "irb" prompt denoted >>) using:
77
78
  #
@@ -98,7 +99,7 @@ class RinRuby
98
99
  # >> require "rinruby"
99
100
  # >> R.quit
100
101
  # >> R = RinRuby.new(false)
101
- attr_reader :echo_enabled
102
+ attr_accessor :echo_enabled
102
103
  attr_reader :executable
103
104
  attr_reader :port_number
104
105
  attr_reader :port_width
@@ -114,8 +115,8 @@ def initialize(*args)
114
115
  opts[:port_number]=args.shift unless args.size==0
115
116
  opts[:port_width]=args.shift unless args.size==0
116
117
  end
117
- default_opts= {:echo=>true, :interactive=>true, :executable=>nil, :port_number=>38442, :port_width=>1000, :hostname=>'127.0.0.1'}
118
-
118
+ default_opts= {:echo=>true, :interactive=>true, :executable=>nil, :port_number=>38442, :port_width=>1000, :hostname=>'127.0.0.1', :persistent => true}
119
+
119
120
  @opts=default_opts.merge(opts)
120
121
  @port_width=@opts[:port_width]
121
122
  @executable=@opts[:executable]
@@ -166,37 +167,26 @@ def initialize(*args)
166
167
  @writer = @engine
167
168
  raise "Engine closed" if @engine.closed?
168
169
  @writer.puts <<-EOF
169
- #{RinRuby_KeepTrying_Variable} <- TRUE
170
- while ( #{RinRuby_KeepTrying_Variable} ) {
171
- #{RinRuby_Socket} <- try(suppressWarnings(socketConnection("#{@hostname}", #{@port_number}, blocking=TRUE, open="rb")),TRUE)
172
- if ( inherits(#{RinRuby_Socket},"try-error") ) {
173
- Sys.sleep(0.1)
174
- } else {
175
- #{RinRuby_KeepTrying_Variable} <- FALSE
176
- }
177
- }
178
- rm(#{RinRuby_KeepTrying_Variable})
170
+ assign("#{RinRuby_Env}", new.env(), baseenv())
179
171
  EOF
172
+ @socket = nil
173
+ r_rinruby_socket_io
180
174
  r_rinruby_get_value
181
175
  r_rinruby_pull
182
176
  r_rinruby_parseable
183
- @socket = @server_socket.accept
184
177
  echo(nil,true) if @platform =~ /.*-java/ # Redirect error messages on the Java platform
185
178
  end
186
179
 
187
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.
188
181
 
189
182
  def quit
190
- begin
183
+ begin
191
184
  @writer.puts "q(save='no')"
192
- # TODO: Verify if read is needed
193
- @socket.read()
194
- #@socket.close
195
- @engine.close
196
-
197
-
198
- @server_socket.close
199
- #@reader.close
185
+ @engine.close
186
+
187
+
188
+ @server_socket.close
189
+ #@reader.close
200
190
  #@writer.close
201
191
  true
202
192
  ensure
@@ -464,11 +454,7 @@ def initialize(*args)
464
454
  def pull(string, singletons=false)
465
455
  raise EngineClosed if @engine.closed?
466
456
  if complete?(string)
467
- result = pull_engine(string)
468
- if ( ! singletons ) && ( result.length == 1 ) && ( result.class != String )
469
- result = result[0]
470
- end
471
- result
457
+ pull_engine(string, singletons)
472
458
  else
473
459
  raise ParseError, "Parse error"
474
460
  end
@@ -511,231 +497,259 @@ def initialize(*args)
511
497
  RinRuby_Type_String = 2
512
498
  RinRuby_Type_String_Array = 3
513
499
  RinRuby_Type_Matrix = 4
500
+ RinRuby_Type_Boolean = 5
514
501
 
515
- RinRuby_KeepTrying_Variable = ".RINRUBY.KEEPTRYING.VARIABLE"
516
- RinRuby_Length_Variable = ".RINRUBY.PULL.LENGTH.VARIABLE"
517
- RinRuby_Type_Variable = ".RINRUBY.PULL.TYPE.VARIABLE"
518
- RinRuby_Socket = ".RINRUBY.PULL.SOCKET"
519
- RinRuby_Variable = ".RINRUBY.PULL.VARIABLE"
520
- RinRuby_Parse_String = ".RINRUBY.PARSE.STRING"
502
+ RinRuby_Socket = "#{RinRuby_Env}$socket"
503
+ RinRuby_Parse_String = "#{RinRuby_Env}$parse.string"
504
+
521
505
  RinRuby_Eval_Flag = "RINRUBY.EVAL.FLAG"
522
506
  RinRuby_Stderr_Flag = "RINRUBY.STDERR.FLAG"
523
507
  RinRuby_Exit_Flag = "RINRUBY.EXIT.FLAG"
508
+
524
509
  RinRuby_Max_Unsigned_Integer = 2**32
525
510
  RinRuby_Half_Max_Unsigned_Integer = 2**31
526
511
  RinRuby_NA_R_Integer = 2**31
527
512
  RinRuby_Max_R_Integer = 2**31-1
528
513
  RinRuby_Min_R_Integer = -2**31+1
529
514
  #:startdoc:
530
-
515
+
516
+ def r_rinruby_socket_io
517
+ @writer.puts <<-EOF
518
+ #{RinRuby_Socket} <- NULL
519
+ #{RinRuby_Env}$session <- function(f){
520
+ invisible(f(#{RinRuby_Socket}))
521
+ }
522
+ #{RinRuby_Env}$write <- function(con, v, ...){
523
+ invisible(lapply(list(v, ...), function(v2){
524
+ writeBin(v2, con, endian="#{RinRuby_Endian}")}))
525
+ }
526
+ #{RinRuby_Env}$read <- function(con, vtype, len){
527
+ invisible(readBin(con, vtype(), len, endian="#{RinRuby_Endian}"))
528
+ }
529
+ EOF
530
+ end
531
531
 
532
532
  def r_rinruby_parseable
533
533
  @writer.puts <<-EOF
534
- rinruby_parseable<-function(var) {
535
- result=try(parse(text=var),TRUE)
536
- if(inherits(result, "try-error")) {
537
- writeBin(as.integer(-1),#{RinRuby_Socket}, endian="big")
538
- } else {
539
- writeBin(as.integer(1),#{RinRuby_Socket}, endian="big")
540
- }
534
+ #{RinRuby_Env}$parseable <- function(var) {
535
+ #{RinRuby_Env}$session(function(con){
536
+ result=try(parse(text=var),TRUE)
537
+ if(inherits(result, "try-error")) {
538
+ #{RinRuby_Env}$write(con, as.integer(-1))
539
+ } else {
540
+ #{RinRuby_Env}$write(con, as.integer(1))
541
+ }
542
+ })
541
543
  }
542
544
  EOF
543
545
  end
544
546
  # Create function on ruby to get values
545
547
  def r_rinruby_get_value
546
548
  @writer.puts <<-EOF
547
- rinruby_get_value <-function() {
548
- value <- NULL
549
- type <- readBin(#{RinRuby_Socket}, integer(), 1, endian="big")
550
- length <- readBin(#{RinRuby_Socket},integer(),1,endian="big")
551
- if ( type == #{RinRuby_Type_Double} ) {
552
- value <- readBin(#{RinRuby_Socket},numeric(), length,endian="big")
549
+ #{RinRuby_Env}$get_value <- function() {
550
+ #{RinRuby_Env}$session(function(con){
551
+ value <- NULL
552
+ type <- #{RinRuby_Env}$read(con, integer, 1)
553
+ length <- #{RinRuby_Env}$read(con, integer, 1)
554
+ if ( type == #{RinRuby_Type_Double} ) {
555
+ value <- #{RinRuby_Env}$read(con, numeric, length)
553
556
  } else if ( type == #{RinRuby_Type_Integer} ) {
554
- value <- readBin(#{RinRuby_Socket},integer(), length, endian="big")
555
- } else if ( type == #{RinRuby_Type_String} ) {
556
- value <- readBin(#{RinRuby_Socket},character(),1,endian="big")
557
+ value <- #{RinRuby_Env}$read(con, integer, length)
558
+ } else if ( type == #{RinRuby_Type_Boolean} ) {
559
+ value <- #{RinRuby_Env}$read(con, logical, length)
560
+ } else if ( type == #{RinRuby_Type_String_Array} ) {
561
+ value <- character(length)
562
+ for(i in 1:length){
563
+ value[i] <- #{RinRuby_Env}$read(con, character, 1)
564
+ }
557
565
  } else {
558
566
  value <-NULL
559
567
  }
560
- value
561
- }
568
+ value
569
+ })
570
+ }
562
571
  EOF
563
572
  end
564
-
573
+
565
574
  def r_rinruby_pull
566
- @writer.puts <<-EOF
567
- rinruby_pull <-function(var)
568
- {
569
- if ( inherits(var ,"try-error") ) {
570
- writeBin(as.integer(#{RinRuby_Type_NotFound}),#{RinRuby_Socket},endian="big")
571
- } else {
572
- if (is.matrix(var)) {
573
- writeBin(as.integer(#{RinRuby_Type_Matrix}),#{RinRuby_Socket},endian="big")
574
- writeBin(as.integer(dim(var)[1]),#{RinRuby_Socket},endian="big")
575
- writeBin(as.integer(dim(var)[2]),#{RinRuby_Socket},endian="big")
576
-
577
- } else if ( is.double(var) ) {
578
- writeBin(as.integer(#{RinRuby_Type_Double}),#{RinRuby_Socket},endian="big")
579
- writeBin(as.integer(length(var)),#{RinRuby_Socket},endian="big")
580
- writeBin(var,#{RinRuby_Socket},endian="big")
581
- } else if ( is.integer(var) ) {
582
- writeBin(as.integer(#{RinRuby_Type_Integer}),#{RinRuby_Socket},endian="big")
583
- writeBin(as.integer(length(var)),#{RinRuby_Socket},endian="big")
584
- writeBin(var,#{RinRuby_Socket},endian="big")
585
- } else if ( is.character(var) && ( length(var) == 1 ) ) {
586
- writeBin(as.integer(#{RinRuby_Type_String}),#{RinRuby_Socket},endian="big")
587
- writeBin(as.integer(nchar(var)),#{RinRuby_Socket},endian="big")
588
- writeBin(var,#{RinRuby_Socket},endian="big")
589
- } else if ( is.character(var) && ( length(var) > 1 ) ) {
590
- writeBin(as.integer(#{RinRuby_Type_String_Array}),#{RinRuby_Socket},endian="big")
591
- writeBin(as.integer(length(var)),#{RinRuby_Socket},endian="big")
575
+ @writer.puts <<-EOF
576
+ #{RinRuby_Env}$pull <- function(var){
577
+ #{RinRuby_Env}$session(function(con){
578
+ if ( inherits(var ,"try-error") ) {
579
+ #{RinRuby_Env}$write(con, as.integer(#{RinRuby_Type_NotFound}))
592
580
  } else {
593
- writeBin(as.integer(#{RinRuby_Type_Unknown}),#{RinRuby_Socket},endian="big")
581
+ if (is.matrix(var)) {
582
+ #{RinRuby_Env}$write(con,
583
+ as.integer(#{RinRuby_Type_Matrix}),
584
+ as.integer(dim(var)[1]))
585
+ } else if ( is.double(var) ) {
586
+ #{RinRuby_Env}$write(con,
587
+ as.integer(#{RinRuby_Type_Double}),
588
+ as.integer(length(var)),
589
+ var)
590
+ } else if ( is.integer(var) ) {
591
+ #{RinRuby_Env}$write(con,
592
+ as.integer(#{RinRuby_Type_Integer}),
593
+ as.integer(length(var)),
594
+ var)
595
+ } else if ( is.character(var) && ( length(var) == 1 ) ) {
596
+ #{RinRuby_Env}$write(con,
597
+ as.integer(#{RinRuby_Type_String}),
598
+ as.integer(nchar(var)),
599
+ var)
600
+ } else if ( is.character(var) && ( length(var) > 1 ) ) {
601
+ #{RinRuby_Env}$write(con,
602
+ as.integer(#{RinRuby_Type_String_Array}),
603
+ as.integer(length(var)))
604
+ } else if ( is.logical(var) ) {
605
+ #{RinRuby_Env}$write(con,
606
+ as.integer(#{RinRuby_Type_Boolean}),
607
+ as.integer(length(var)),
608
+ var)
609
+ } else {
610
+ #{RinRuby_Env}$write(con, as.integer(#{RinRuby_Type_Unknown}))
611
+ }
594
612
  }
595
- }
613
+ })
596
614
  }
597
615
  EOF
598
-
599
-
600
616
  end
601
- def to_signed_int(y)
602
- if y.kind_of?(Integer)
603
- ( y > RinRuby_Half_Max_Unsigned_Integer ) ? -(RinRuby_Max_Unsigned_Integer-y) : ( y == RinRuby_NA_R_Integer ? nil : y )
617
+
618
+ def socket_session(&b)
619
+ socket = @socket
620
+ # TODO check still available connection?
621
+ unless socket then
622
+ t = Thread::new{socket = @server_socket.accept}
623
+ @writer.puts <<-EOF
624
+ #{RinRuby_Socket} <- socketConnection(
625
+ "#{@hostname}", #{@port_number}, blocking=TRUE, open="rb")
626
+ #{"on.exit(close(#{RinRuby_Socket}, add = T))" if @opts[:persistent]}
627
+ EOF
628
+ t.join
629
+ end
630
+ res = b.call(socket)
631
+ if @opts[:persistent]
632
+ @socket = socket
604
633
  else
605
- y.collect { |x| ( x > RinRuby_Half_Max_Unsigned_Integer ) ? -(RinRuby_Max_Unsigned_Integer-x) : ( x == RinRuby_NA_R_Integer ? nil : x ) }
634
+ @writer.puts <<-EOF
635
+ close(#{RinRuby_Socket})
636
+ #{RinRuby_Socket} <- NULL
637
+ EOF
638
+ socket.close
606
639
  end
640
+ res
607
641
  end
608
642
 
609
643
  def assign_engine(name, value)
610
644
  original_value = value
611
- # Special assign for matrixes
612
- if value.kind_of?(::Matrix)
613
- values=value.row_size.times.collect {|i| value.column_size.times.collect {|j| value[i,j]}}.flatten
614
- eval "#{name}=matrix(c(#{values.join(',')}), #{value.row_size}, #{value.column_size}, TRUE)"
615
- return original_value
645
+
646
+ r_exp = "#{name} <- #{RinRuby_Env}$get_value()"
647
+
648
+ if value.kind_of?(::Matrix) # assignment for matrices
649
+ r_exp = "#{name} <- matrix(#{RinRuby_Env}$get_value(), nrow=#{value.row_size}, ncol=#{value.column_size}, byrow=T)"
650
+ value = value.row_vectors.collect{|row| row.to_a}.flatten
651
+ elsif !value.kind_of?(Array) then # check Array
652
+ value = [value]
616
653
  end
617
654
 
618
- if value.kind_of?(String)
619
- type = RinRuby_Type_String
620
- length = 1
621
- elsif value.kind_of?(Integer)
622
- if ( value >= RinRuby_Min_R_Integer ) && ( value <= RinRuby_Max_R_Integer )
623
- value = [ value.to_i ]
624
- type = RinRuby_Type_Integer
625
- else
626
- value = [ value.to_f ]
627
- type = RinRuby_Type_Double
628
- end
629
- length = 1
630
- elsif value.kind_of?(Float)
631
- value = [ value.to_f ]
632
- type = RinRuby_Type_Double
633
- length = 1
634
- elsif value.kind_of?(Array)
635
- begin
636
- if value.any? { |x| x.kind_of?(String) }
637
- eval "#{name} <- character(#{value.length})"
638
- for index in 0...value.length
639
- assign_engine("#{name}[#{index}+1]",value[index])
640
- end
641
- return original_value
642
- elsif value.any? { |x| x.kind_of?(Float) }
643
- type = RinRuby_Type_Double
644
- value = value.collect { |x| x.to_f }
645
- elsif value.all? { |x| x.kind_of?(Integer) }
646
- if value.all? { |x| ( x >= RinRuby_Min_R_Integer ) && ( x <= RinRuby_Max_R_Integer ) }
647
- type = RinRuby_Type_Integer
648
- else
649
- value = value.collect { |x| x.to_f }
650
- type = RinRuby_Type_Double
655
+ type = (if value.any?{|x| x.kind_of?(String)}
656
+ value = value.collect{|v| v.to_s}
657
+ RinRuby_Type_String_Array
658
+ elsif value_b = value.collect{|v|
659
+ case v
660
+ when true; 1
661
+ when false; 0
662
+ else; break false
651
663
  end
652
- else
653
- raise "Unsupported data type on Ruby's end"
654
- end
664
+ }
665
+ value = value_b
666
+ RinRuby_Type_Boolean
667
+ elsif value.all?{|x|
668
+ x.kind_of?(Integer) && (x >= RinRuby_Min_R_Integer) && (x <= RinRuby_Max_R_Integer)
669
+ }
670
+ RinRuby_Type_Integer
671
+ else
672
+ begin
673
+ value = value.collect{|x| Float(x)}
655
674
  rescue
656
675
  raise "Unsupported data type on Ruby's end"
657
676
  end
658
- length = value.length
659
- else
660
- raise "Unsupported data type on Ruby's end"
661
- end
662
- @writer.puts "#{name} <- rinruby_get_value()"
663
-
664
- @socket.write([type,length].pack('NN'))
665
- if ( type == RinRuby_Type_String )
666
- @socket.write(value)
667
- @socket.write([0].pack('C')) # zero-terminated strings
668
- else
669
- @socket.write(value.pack( ( type==RinRuby_Type_Double ? 'G' : 'N' )*length ))
670
- end
677
+ RinRuby_Type_Double
678
+ end)
679
+
680
+ socket_session{|socket|
681
+ @writer.puts(r_exp)
682
+ socket.write([type, value.size].pack('LL'))
683
+ case type
684
+ when RinRuby_Type_String_Array
685
+ value.each{|v|
686
+ socket.write(v)
687
+ socket.write([0].pack('C')) # zero-terminated strings
688
+ }
689
+ else
690
+ socket.write(value.pack("#{(type == RinRuby_Type_Double) ? 'D' : 'l'}#{value.size}"))
691
+ end
692
+ }
693
+
671
694
  original_value
672
695
  end
673
696
 
674
- def pull_engine(string)
675
- @writer.puts <<-EOF
676
- rinruby_pull(try(#{string}))
677
- EOF
678
-
679
- buffer = ""
680
- @socket.read(4,buffer)
681
- type = to_signed_int(buffer.unpack('N')[0].to_i)
682
- if ( type == RinRuby_Type_Unknown )
683
- raise "Unsupported data type on R's end"
684
- end
685
- if ( type == RinRuby_Type_NotFound )
686
- return nil
687
- end
688
- @socket.read(4,buffer)
689
- length = to_signed_int(buffer.unpack('N')[0].to_i)
690
-
691
- if ( type == RinRuby_Type_Double )
692
- @socket.read(8*length,buffer)
693
- result = buffer.unpack('G'*length)
694
- elsif ( type == RinRuby_Type_Integer )
695
- @socket.read(4*length,buffer)
696
- result = to_signed_int(buffer.unpack('N'*length))
697
- elsif ( type == RinRuby_Type_String )
698
- @socket.read(length,buffer)
699
- result = buffer.dup
700
- @socket.read(1,buffer) # zero-terminated string
701
- result
702
- elsif ( type == RinRuby_Type_String_Array )
703
- result = Array.new(length,'')
704
- for index in 0...length
705
- result[index] = pull "#{string}[#{index+1}]"
697
+ def pull_engine(string, singletons = true)
698
+ pull_proc = proc{|var, socket|
699
+ @writer.puts "#{RinRuby_Env}$pull(try(#{var}))"
700
+ type = socket.read(4).unpack('l').first
701
+ case type
702
+ when RinRuby_Type_Unknown
703
+ raise "Unsupported data type on R's end"
704
+ when RinRuby_Type_NotFound
705
+ return nil
706
706
  end
707
- elsif (type == RinRuby_Type_Matrix)
708
- rows=length
709
- @socket.read(4,buffer)
710
- cols = to_signed_int(buffer.unpack('N')[0].to_i)
711
- elements=pull "as.vector(#{string})"
712
- index=0
713
- result=Matrix.rows(rows.times.collect {|i|
714
- cols.times.collect {|j|
715
- elements[(j*rows)+i]
707
+ length = socket.read(4).unpack('l').first
708
+
709
+ case type
710
+ when RinRuby_Type_Double
711
+ result = socket.read(8 * length).unpack("D#{length}")
712
+ (!singletons) && (length == 1) ? result[0] : result
713
+ when RinRuby_Type_Integer
714
+ result = socket.read(4 * length).unpack("l#{length}")
715
+ (!singletons) && (length == 1) ? result[0] : result
716
+ when RinRuby_Type_String
717
+ result = socket.read(length)
718
+ socket.read(1) # zero-terminated string
719
+ result
720
+ when RinRuby_Type_String_Array
721
+ Array.new(length){|i|
722
+ pull_proc.call("#{var}[#{i+1}]", socket)
716
723
  }
717
- })
718
- def result.length; 2;end
719
- else
720
- raise "Unsupported data type on Ruby's end"
721
- end
722
- result
724
+ when RinRuby_Type_Matrix
725
+ Matrix.rows(length.times.collect{|i|
726
+ pull_proc.call("#{var}[#{i+1},]", socket)
727
+ })
728
+ when RinRuby_Type_Boolean
729
+ result = socket.read(4 * length).unpack("l#{length}").collect{|v| v > 0}
730
+ (!singletons) && (length == 1) ? result[0] : result
731
+ else
732
+ raise "Unsupported data type on Ruby's end"
733
+ end
734
+ }
735
+ socket_session{|socket|
736
+ pull_proc.call(string, socket)
737
+ }
723
738
  end
724
739
 
725
740
  def complete?(string)
726
741
  assign_engine(RinRuby_Parse_String, string)
727
- @writer.puts "rinruby_parseable(#{RinRuby_Parse_String})"
728
- buffer=""
729
- @socket.read(4,buffer)
730
- @writer.puts "rm(#{RinRuby_Parse_String})"
731
- result = to_signed_int(buffer.unpack('N')[0].to_i)
742
+ result = socket_session{|socket|
743
+ @writer.puts "#{RinRuby_Env}$parseable(#{RinRuby_Parse_String})"
744
+ socket.read(4).unpack('l').first
745
+ }
732
746
  return result==-1 ? false : true
733
-
734
- =begin
735
-
747
+
748
+ =begin
749
+
736
750
  result = pull_engine("unlist(lapply(c('.*','^Error in parse.*','^Error in parse.*unexpected end of input.*'),
737
751
  grep,try({parse(text=#{RinRuby_Parse_String}); 1}, silent=TRUE)))")
738
-
752
+
739
753
  return true if result.length == 1
740
754
  return false if result.length == 3
741
755
  raise ParseError, "Parse error"
@@ -746,7 +760,6 @@ def initialize(*args)
746
760
  raise ParseError, "Parse error" if ! complete?(string)
747
761
  assign_engine(RinRuby_Parse_String,string)
748
762
  result = pull_engine("as.integer(ifelse(inherits(try({eval(parse(text=paste(#{RinRuby_Parse_String},'<- 1')))}, silent=TRUE),'try-error'),1,0))")
749
- @writer.puts "rm(#{RinRuby_Parse_String})"
750
763
  return true if result == [0]
751
764
  raise ParseError, "Parse error"
752
765
  end
@@ -754,29 +767,70 @@ def initialize(*args)
754
767
  def find_R_on_windows(cygwin)
755
768
  path = '?'
756
769
  for root in [ 'HKEY_LOCAL_MACHINE', 'HKEY_CURRENT_USER' ]
757
- `reg query "#{root}\\Software\\R-core\\R" /v "InstallPath"`.split("\n").each do |line|
758
- next if line !~ /^\s+InstallPath\s+REG_SZ\s+(.*)/
759
- path = $1
760
- while path.chomp!
770
+ if cygwin then
771
+ [:w, :W].collect{|opt| # [64bit, then 32bit registry]
772
+ [:R64, :R].collect{|mode|
773
+ `regtool list -#{opt} /#{root}/Software/R-core/#{mode} 2>/dev/null`.lines.collect{|v|
774
+ v =~ /^\d\.\d\.\d/ ? $& : nil
775
+ }.compact.sort{|a, b| # latest version has higher priority
776
+ b <=> a
777
+ }.collect{|ver|
778
+ ["-#{opt}", "/#{root}/Software/R-core/#{mode}/#{ver}/InstallPath"]
779
+ }
780
+ }
781
+ }.flatten(2).each{|args|
782
+ v = `regtool get #{args.join(' ')}`.chomp
783
+ unless v.empty? then
784
+ path = v
785
+ break
786
+ end
787
+ }
788
+ else
789
+ proc{|str| # Remove invalid byte sequence
790
+ if RUBY_VERSION >= "2.1.0" then
791
+ str.scrub
792
+ elsif RUBY_VERSION >= "1.9.0" then
793
+ str.chars.collect{|c| (c.valid_encoding?) ? c : '*'}.join
794
+ else
795
+ str
796
+ end
797
+ }.call(`reg query "#{root}\\Software\\R-core" /v "InstallPath" /s`).each_line do |line|
798
+ next if line !~ /^\s+InstallPath\s+REG_SZ\s+(.*)/
799
+ path = $1
800
+ while path.chomp!
801
+ end
802
+ break
761
803
  end
762
- break
763
804
  end
764
805
  break if path != '?'
765
806
  end
766
- raise "Cannot locate R executable" if path == '?'
807
+ if path == '?'
808
+ # search at default install path
809
+ path = [
810
+ "Program Files",
811
+ "Program Files (x86)"
812
+ ].collect{|prog_dir|
813
+ Dir::glob(File::join(
814
+ cygwin ? "/cygdrive/c" : "C:",
815
+ prog_dir, "R", "*"))
816
+ }.flatten[0]
817
+ raise "Cannot locate R executable" unless path
818
+ end
767
819
  if cygwin
768
820
  path = `cygpath '#{path}'`
769
821
  while path.chomp!
770
822
  end
771
- path.gsub!(' ','\ ')
823
+ path = [path.gsub(' ','\ '), path]
772
824
  else
773
- path.gsub!('\\','/')
825
+ path = [path.gsub('\\','/')]
774
826
  end
775
- for hierarchy in [ 'bin', 'bin/i386', 'bin/x64' ]
776
- target = "#{path}/#{hierarchy}/Rterm.exe"
777
- if File.exists? target
778
- return %Q<"#{target}">
779
- end
827
+ for hierarchy in [ 'bin', 'bin/x64', 'bin/i386']
828
+ path.each{|item|
829
+ target = "#{item}/#{hierarchy}/Rterm.exe"
830
+ if File.exists? target
831
+ return %Q<"#{target}">
832
+ end
833
+ }
780
834
  end
781
835
  raise "Cannot locate R executable"
782
836
  end