rust 0.7 → 0.9

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,161 @@
1
+ require_relative '../rust'
2
+
3
+ module Rust
4
+ class RustDatatype
5
+ def self.pull_variable(variable, forced_interpreter = nil)
6
+ r_type = Rust._pull("as.character(typeof(#{variable}))")
7
+ r_class = Rust._pull("as.character(class(#{variable}))")
8
+
9
+ if forced_interpreter
10
+ raise ArgumentError, "Expected null or class as forced_interpreter" if forced_interpreter && !forced_interpreter.is_a?(Class)
11
+ raise ArgumentError, "Class #{forced_interpreter} can not handle type #{r_type}, class #{r_class}" unless forced_interpreter.can_pull?(r_type, r_class)
12
+
13
+ return forced_interpreter.pull_variable(variable, r_type, r_class)
14
+ end
15
+
16
+ candidates = []
17
+ ObjectSpace.each_object(Class) do |type|
18
+ if type < RustDatatype
19
+ if type.can_pull?(r_type, r_class)
20
+ candidates << type
21
+ end
22
+ end
23
+ end
24
+
25
+ if candidates.size > 0
26
+ type = candidates.max_by { |c| c.pull_priority }
27
+
28
+ puts "Using #{type} to pull #{variable}" if Rust.debug?
29
+ return type.pull_variable(variable, r_type, r_class)
30
+ else
31
+ if Rust._pull("length(#{variable})") == 0
32
+ return []
33
+ else
34
+ return Rust._pull(variable)
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.pull_priority
40
+ 0
41
+ end
42
+
43
+ def load_in_r_as(variable_name)
44
+ raise "Loading #{self.class} in R was not implemented"
45
+ end
46
+
47
+ def r_mirror_to(other_variable)
48
+ varname = self.mirrored_R_variable_name
49
+
50
+ Rust._eval("#{varname} = #{other_variable}")
51
+ Rust["#{varname}.hash"] = self.r_hash
52
+
53
+ return varname
54
+ end
55
+
56
+ def r_mirror
57
+ varname = self.mirrored_R_variable_name
58
+
59
+ if !Rust._pull("exists(\"#{varname}\")") || Rust._pull("#{varname}.hash") != self.r_hash
60
+ puts "Loading #{varname}" if Rust.debug?
61
+ Rust[varname] = self
62
+ Rust["#{varname}.hash"] = self.r_hash
63
+ else
64
+ puts "Using cached value for #{varname}" if Rust.debug?
65
+ end
66
+
67
+ return varname
68
+ end
69
+
70
+ def r_hash
71
+ self.hash.to_s
72
+ end
73
+
74
+ private
75
+ def mirrored_R_variable_name
76
+ return "rust.mirrored.#{self.object_id}"
77
+ end
78
+ end
79
+
80
+ class Null < RustDatatype
81
+ def self.can_pull?(type, klass)
82
+ return type == "NULL" && klass == "NULL"
83
+ end
84
+
85
+ def self.pull_variable(variable, type, klass)
86
+ return nil
87
+ end
88
+ end
89
+ end
90
+
91
+ class TrueClass
92
+ def to_R
93
+ "TRUE"
94
+ end
95
+ end
96
+
97
+ class FalseClass
98
+ def to_R
99
+ "FALSE"
100
+ end
101
+ end
102
+
103
+ class Object
104
+ def to_R
105
+ raise TypeError, "Unsupported type for #{self.class}"
106
+ end
107
+ end
108
+
109
+ class NilClass
110
+ def to_R
111
+ return "NULL"
112
+ end
113
+
114
+ def load_in_r_as(variable)
115
+ Rust._eval("#{variable} <- NULL")
116
+ end
117
+ end
118
+
119
+ class Numeric
120
+ def to_R
121
+ self.inspect
122
+ end
123
+ end
124
+
125
+ class Float
126
+ def to_R
127
+ return self.nan? ? "NA" : super
128
+ end
129
+ end
130
+
131
+ class Symbol
132
+ def to_R
133
+ return self.to_s.inspect
134
+ end
135
+ end
136
+
137
+ class Array
138
+ def to_R
139
+ return "c(#{self.map { |e| e.to_R }.join(",")})"
140
+ end
141
+
142
+ def distribution
143
+ result = {}
144
+ self.each do |value|
145
+ result[value] = result[value].to_i + 1
146
+ end
147
+ return result
148
+ end
149
+ end
150
+
151
+ class String
152
+ def to_R
153
+ return self.inspect
154
+ end
155
+ end
156
+
157
+ class Range
158
+ def to_R
159
+ [range.min, range.max].to_R
160
+ end
161
+ end
@@ -0,0 +1,131 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class Factor < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return klass == "factor"
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ levels = Rust["levels(#{variable})"]
11
+ values = Rust["as.integer(#{variable})"]
12
+
13
+ return Factor.new(values, levels)
14
+ end
15
+
16
+ def load_in_r_as(variable_name)
17
+ Rust['tmp.levels'] = @levels.map { |v| v.to_s }
18
+ Rust['tmp.values'] = @values
19
+
20
+ Rust._eval("#{variable_name} <- factor(tmp.values, labels=tmp.levels)")
21
+ end
22
+
23
+ def initialize(values, levels)
24
+ @levels = levels.map { |v| v.to_sym }
25
+ @values = values
26
+ end
27
+
28
+ def levels
29
+ @levels
30
+ end
31
+
32
+ def ==(other)
33
+ return false unless other.is_a?(Factor)
34
+
35
+ return @levels == other.levels && self.to_a == other.to_a
36
+ end
37
+
38
+ def [](i)
39
+ FactorValue.new(@values[i], @levels[@values[i] - 1])
40
+ end
41
+
42
+ def []=(i, value)
43
+ raise "The given value is outside the factor bounds" if value.is_a?(Integer) && (value < 1 || value > @levels.size)
44
+
45
+ if value.is_a?(FactorValue)
46
+ raise "Incompatible factor value, different levels used" unless @levels.include?(value.level) || @levels.index(value.level) + 1 == @value.value
47
+ value = value.value
48
+ end
49
+
50
+ if value.is_a?(String) || value.is_a?(Symbol)
51
+ value = value.to_sym
52
+ raise "Unsupported value #{value}; expected #{@levels.join(", ")}" unless @levels.include?(value)
53
+
54
+ value = @levels.index(value) + 1
55
+ end
56
+
57
+ @values[i] = value
58
+ end
59
+
60
+ def to_a
61
+ @values.map { |v| FactorValue.new(v, @levels[v - 1]) }
62
+ end
63
+
64
+ def method_missing(method, *args, &block)
65
+ raise NoMethodError, "Undefined method #{method} for Factor" if method.to_s.end_with?("!") || method.end_with?("=")
66
+
67
+ self.to_a.method(method).call(*args, &block)
68
+ end
69
+
70
+ def to_s
71
+ self.to_a.to_s
72
+ end
73
+
74
+ def inspect
75
+ self.to_a.inspect
76
+ end
77
+ end
78
+
79
+ class FactorValue
80
+ def initialize(value, level)
81
+ @value = value
82
+ @level = level
83
+ end
84
+
85
+ def value
86
+ @value
87
+ end
88
+
89
+ def level
90
+ @level
91
+ end
92
+
93
+ def to_i
94
+ @value
95
+ end
96
+
97
+ def to_sym
98
+ @level
99
+ end
100
+
101
+ def to_R
102
+ self.to_i
103
+ end
104
+
105
+ def inspect
106
+ @level.inspect
107
+ end
108
+
109
+ def ==(other)
110
+ if other.is_a?(FactorValue)
111
+ @value == other.value && @level == other.level
112
+ elsif other.is_a?(Integer)
113
+ @value == other
114
+ elsif other.is_a?(Symbol)
115
+ @level == other
116
+ end
117
+ end
118
+
119
+ def hash
120
+ @value.hash + @level.hash
121
+ end
122
+
123
+ def eql?(other)
124
+ return self == other
125
+ end
126
+
127
+ def method_missing(method, *args, &block)
128
+ @level.method(method).call(*args, &block)
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,166 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class Formula < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return klass == "formula" || (klass.is_a?(Array) && klass.include?("formula"))
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ formula_elements = Rust._pull("as.character(#{variable})")
11
+
12
+ assert("The number of elements of a formula must be 2 or 3: #{formula_elements} given") { formula_elements.size > 1 && formula_elements.size < 4 }
13
+ if formula_elements.size == 2
14
+ return Formula.new(nil, formula_elements[1])
15
+ elsif formula_elements.size == 3
16
+ return Formula.new(formula_elements[2], formula_elements[1])
17
+ end
18
+ end
19
+
20
+ def load_in_r_as(variable_name)
21
+ Rust._eval("#{variable_name} <- #{self.left_part} ~ #{self.right_part}")
22
+ end
23
+
24
+ attr_reader :left_part
25
+ attr_reader :right_part
26
+
27
+ def initialize(left_part, right_part)
28
+ raise ArgumentError, "Expected string" if left_part && !left_part.is_a?(String)
29
+ raise ArgumentError, "Expected string" if !right_part.is_a?(String)
30
+
31
+ @left_part = left_part || ""
32
+ @right_part = right_part
33
+ end
34
+
35
+ def ==(oth)
36
+ return false unless oth.is_a?(Formula)
37
+
38
+ return @left_part == oth.left_part && @right_part == oth.right_part
39
+ end
40
+
41
+ def to_R
42
+ return "#@left_part ~ #@right_part"
43
+ end
44
+
45
+ def inspect
46
+ return self.to_R.strip
47
+ end
48
+ end
49
+
50
+ class Call < RustDatatype
51
+ def self.can_pull?(type, klass)
52
+ return klass == "call"
53
+ end
54
+
55
+ def self.pull_variable(variable, type, klass)
56
+ return Call.new(Rust["deparse(#{variable})"])
57
+ end
58
+
59
+ def load_in_r_as(variable_name)
60
+ Rust["call.str"] = @value
61
+ Rust._eval("#{variable_name} <- str2lang(call.str)")
62
+ end
63
+
64
+ def initialize(value)
65
+ @value = value
66
+ end
67
+
68
+ def value
69
+ @value
70
+ end
71
+
72
+ def inspect
73
+ @value
74
+ end
75
+ end
76
+
77
+ class Environment < RustDatatype
78
+ def self.can_pull?(type, klass)
79
+ return type == "environment" && klass == "environment"
80
+ end
81
+
82
+ def self.pull_variable(variable, type, klass)
83
+ warn "Exchanging R environments is not supported!"
84
+ return Environment.new
85
+ end
86
+
87
+ def self.load_in_r_as(variable)
88
+ warn "Exchanging R environments is not supported!"
89
+ Rust._eval("#{variable} <- environment()")
90
+ end
91
+ end
92
+
93
+ class Function
94
+ attr_reader :name
95
+ attr_reader :arguments
96
+ attr_reader :options
97
+
98
+ def initialize(name)
99
+ @function = name
100
+ @arguments = Arguments.new
101
+ @options = Options.new
102
+ end
103
+
104
+ def options=(options)
105
+ raise TypeError, "Expected Options" unless options.is_a?(Options)
106
+
107
+ @options = options
108
+ end
109
+
110
+ def arguments=(arguments)
111
+ raise TypeError, "Expected Arguments" unless options.is_a?(Arguments)
112
+
113
+ @arguments = arguments
114
+ end
115
+
116
+ def to_R
117
+ params = [@arguments.to_R, @options.to_R].select { |v| v != "" }.join(",")
118
+ return "#@function(#{params})"
119
+ end
120
+
121
+ def call
122
+ Rust._eval(self.to_R)
123
+ end
124
+ end
125
+
126
+ class SimpleFormula
127
+ def initialize(dependent, independent)
128
+ @dependent = dependent
129
+ @independent = independent
130
+ end
131
+
132
+ def to_R
133
+ return "#@dependent ~ #@independent"
134
+ end
135
+ end
136
+
137
+ class Variable
138
+ def initialize(name)
139
+ @name = name
140
+ end
141
+
142
+ def to_R
143
+ @name
144
+ end
145
+ end
146
+
147
+ class Arguments < Array
148
+ def to_R
149
+ return self.map { |v| v.to_R }.join(", ")
150
+ end
151
+ end
152
+
153
+ class Options < Hash
154
+ def to_R
155
+ return self.map { |k, v| "#{k}=#{v.to_R}" }.join(", ")
156
+ end
157
+
158
+ def self.from_hash(hash)
159
+ options = Options.new
160
+ hash.each do |key, value|
161
+ options[key.to_s] = value
162
+ end
163
+ return options
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,81 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class List < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return type == "list"
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ return List.new(klass) if Rust._pull("length(#{variable})") == 0
11
+
12
+ names = [Rust["names(#{variable})"]].flatten
13
+ length = Rust["length(#{variable})"]
14
+
15
+ list = List.new(klass, names)
16
+ for i in 0...length
17
+ list[i] = Rust["#{variable}[[#{i + 1}]]"]
18
+ end
19
+
20
+ return list
21
+ end
22
+
23
+ def load_in_r_as(variable_name)
24
+ Rust._eval("#{variable_name} <- list()")
25
+ @data.each do |key, value|
26
+ Rust["#{variable_name}[[#{key + 1}]]"] = value
27
+ end
28
+ end
29
+
30
+ def initialize(klass, names = [])
31
+ @data = {}
32
+ @names = names
33
+ @klass = klass
34
+ end
35
+
36
+ def [](key)
37
+ key = get_key(key)
38
+
39
+ return @data[key]
40
+ end
41
+ alias :| :[]
42
+
43
+ def []=(key, value)
44
+ key = get_key(key)
45
+
46
+ return @data[key] = value
47
+ end
48
+
49
+ def names
50
+ @names
51
+ end
52
+
53
+ def inspect
54
+ result = ""
55
+ values_inspected = @data.map { |k, v| [k, v.inspect.split("\n").map { |l| " " + l }.join("\n")] }.to_h
56
+ max_length = [values_inspected.map { |k, v| v.split("\n").map { |line| line.length }.max.to_i }.max.to_i, 100].min
57
+
58
+ @data.keys.each do |i|
59
+ result << "-" * max_length + "\n"
60
+ result << (@names[i] || "[[#{i}]]") + "\n"
61
+ result << values_inspected[i] + "\n"
62
+ end
63
+ result << "-" * max_length
64
+
65
+ return result
66
+ end
67
+
68
+ private
69
+ def get_key(key)
70
+ if key.is_a?(String)
71
+ new_key = @names.index(key)
72
+ raise ArgumentError, "Wrong key: #{key}" unless new_key
73
+ key = new_key
74
+ end
75
+
76
+ raise ArgumentError, "The key should be either a string or an integer" unless key.is_a?(Integer)
77
+
78
+ return key
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,132 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class Matrix < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return klass.is_a?(Array) && klass.include?("matrix")
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ if Rust._pull("length(#{variable})") == 1
11
+ core = ::Matrix[[Rust._pull("#{variable}[1]")]]
12
+ else
13
+ core = Rust._pull(variable)
14
+ end
15
+ row_names = [Rust["rownames(#{variable})"]].flatten
16
+ column_names = [Rust["colnames(#{variable})"]].flatten
17
+
18
+ row_names = nil if row_names.all? { |v| v == nil }
19
+ column_names = nil if column_names.all? { |v| v == nil }
20
+
21
+ Matrix.new(core, row_names, column_names)
22
+ end
23
+
24
+ def load_in_r_as(variable_name)
25
+ matrix = ::Matrix[*@data]
26
+
27
+ Rust[variable_name] = matrix
28
+ end
29
+
30
+ def initialize(data, row_names = nil, column_names = nil)
31
+ @data = data.clone
32
+
33
+ @row_names = row_names
34
+ @column_names = column_names
35
+
36
+ if @data.is_a?(::Matrix)
37
+ @data = @data.row_vectors.map { |v| v.to_a }
38
+ end
39
+
40
+ if self.flatten.size == 0
41
+ raise "Empty matrices are not allowed"
42
+ else
43
+ raise TypeError, "Expected array of array" unless @data.is_a?(Array) || @data[0].is_a?(Array)
44
+ raise TypeError, "Only numeric matrices are supported" unless self.flatten.all? { |e| e.is_a?(Numeric) }
45
+ raise "All the rows must have the same size" unless @data.map { |row| row.size }.uniq.size == 1
46
+ raise ArgumentError, "Expected row names #@row_names to match the number of rows in #{self.inspect}" if @row_names && @row_names.size != self.rows
47
+ raise ArgumentError, "Expected column names #@column_names to match the number of columns in #{self.inspect}" if @column_names && @column_names.size != self.cols
48
+ end
49
+ end
50
+
51
+ def [](i, j)
52
+ i, j = indices(i, j)
53
+
54
+ return @data[i][j]
55
+ end
56
+
57
+ def rows
58
+ @data.size
59
+ end
60
+
61
+ def cols
62
+ @data[0].size
63
+ end
64
+
65
+ def flatten
66
+ return @data.flatten
67
+ end
68
+
69
+ def []=(i, j, value)
70
+ i, j = indices(i, j)
71
+
72
+ @data[i][j] = value
73
+ end
74
+
75
+ def inspect
76
+ row_names = @row_names || (0...self.rows).to_a.map { |v| v.to_s }
77
+ column_names = @column_names || (0...self.cols).to_a.map { |v| v.to_s }
78
+
79
+ separator = " | "
80
+ col_widths = column_names.map do |colname|
81
+ [
82
+ colname,
83
+ (
84
+ [colname ? colname.length : 1] +
85
+ @data.map {|r| r[column_names.index(colname)]}.map { |e| e.inspect.length }
86
+ ).max
87
+ ]
88
+ end.to_h
89
+ col_widths[:rowscol] = row_names.map { |rowname| rowname.length }.max + 3
90
+
91
+ result = ""
92
+ result << "-" * (col_widths.values.sum + ((col_widths.size - 1) * separator.length)) + "\n"
93
+ result << (" " * col_widths[:rowscol]) + column_names.map { |colname| (" " * (col_widths[colname] - colname.length)) + colname }.join(separator) + "\n"
94
+ result << "-" * (col_widths.values.sum + ((col_widths.size - 1) * separator.length)) + "\n"
95
+
96
+ @data.each_with_index do |row, i|
97
+ row_name = row_names[i]
98
+ row = column_names.zip(row)
99
+
100
+ index_part = "[" + (" " * (col_widths[:rowscol] - row_name.length - 3)) + "#{row_name}] "
101
+ row_part = row.map { |colname, value| (" " * (col_widths[colname] - value.inspect.length)) + value.inspect }.join(separator)
102
+
103
+ result << index_part + row_part + "\n"
104
+ end
105
+
106
+ result << "-" * (col_widths.values.sum + ((col_widths.size - 1) * separator.length))
107
+
108
+ return result
109
+ end
110
+
111
+ private
112
+ def indices(i, j)
113
+ if i.is_a?(String)
114
+ ri = @row_names.index(i)
115
+ raise ArgumentError, "Can not find row #{i}" unless ri
116
+ i = ri
117
+ end
118
+
119
+ if j.is_a?(String)
120
+ rj = @column_names.index(j)
121
+ raise ArgumentError, "Can not find column #{j}" unless rj
122
+ j = rj
123
+ end
124
+
125
+ raise ArgumentError, "Expected i and j to be both integers or strings" unless i.is_a?(Integer) && j.is_a?(Integer)
126
+ raise "Wrong i" unless i.between?(0, @data.size - 1)
127
+ raise "Wrong j" unless j.between?(0, @data[0].size - 1)
128
+
129
+ return [i, j]
130
+ end
131
+ end
132
+ end