rust 0.3 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,59 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class S4Class < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return type == "S4"
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ slots = [Rust._pull("names(getSlots(\"#{klass}\"))")].flatten
11
+
12
+ return S4Class.new(variable, klass, slots)
13
+ end
14
+
15
+ def load_in_r_as(variable_name)
16
+ Rust._eval("#{variable_name} <- #{self.r_mirror}")
17
+ end
18
+
19
+ def r_hash
20
+ "immutable"
21
+ end
22
+
23
+ def initialize(variable_name, klass, slots)
24
+ @klass = klass
25
+ @slots = slots
26
+
27
+ self.r_mirror_to(variable_name)
28
+ end
29
+
30
+ def [](key)
31
+ raise ArgumentError, "Unknown slot `#{key}` for class `#@klass`" unless @slots.include?(key)
32
+
33
+ Rust.exclusive do
34
+ return Rust["#{self.r_mirror}@#{key}"]
35
+ end
36
+ end
37
+ alias :| :[]
38
+
39
+ def []=(key, value)
40
+ raise ArgumentError, "Unknown slot `#{key}` for class `#@klass`" unless @slots.include?(key)
41
+
42
+ Rust.exclusive do
43
+ return Rust["#{self.r_mirror}@#{key}"] = value
44
+ end
45
+ end
46
+
47
+ def slots
48
+ @slots
49
+ end
50
+
51
+ def class_name
52
+ @klass
53
+ end
54
+
55
+ def inspect
56
+ return "<S4 instance of #@klass, with slots #@slots>"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,109 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class Sequence < RustDatatype
5
+ attr_reader :min
6
+ attr_reader :max
7
+
8
+ def self.can_pull?(type, klass)
9
+ return false
10
+ end
11
+
12
+ def initialize(min, max, step=1)
13
+ @min = min
14
+ @max = max
15
+ @step = step
16
+ end
17
+
18
+ def step(step)
19
+ @step = step
20
+ end
21
+
22
+ def each
23
+ (@min..@max).step(@step) do |v|
24
+ yield v
25
+ end
26
+ end
27
+
28
+ def to_a
29
+ result = []
30
+ self.each do |v|
31
+ result << v
32
+ end
33
+ return result
34
+ end
35
+
36
+ def to_R
37
+ "seq(from=#@min, to=#@max, by=#@step)"
38
+ end
39
+
40
+ def load_in_r_as(variable_name)
41
+ Rust._eval("#{variable_name} <- #{self.to_R}")
42
+ end
43
+ end
44
+
45
+ class MathArray < Array
46
+ def -(other)
47
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
48
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
49
+
50
+ result = self.clone
51
+ other = [other] * self.size if other.is_a?(Numeric)
52
+ for i in 0...self.size
53
+ result[i] -= other[i]
54
+ end
55
+
56
+ return result
57
+ end
58
+
59
+ def *(other)
60
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
61
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
62
+
63
+ result = self.clone
64
+ other = [other] * self.size if other.is_a?(Numeric)
65
+ for i in 0...self.size
66
+ result[i] *= other[i]
67
+ end
68
+
69
+ return result
70
+ end
71
+
72
+ def +(other)
73
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
74
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
75
+
76
+ result = self.clone
77
+ other = [other] * self.size if other.is_a?(Numeric)
78
+ for i in 0...self.size
79
+ result[i] += other[i]
80
+ end
81
+
82
+ return result
83
+ end
84
+
85
+ def /(other) #/# <- this comment is just to recover the syntax highlighting bug in Kate
86
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
87
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
88
+
89
+ result = self.clone
90
+ other = [other] * self.size if other.is_a?(Numeric)
91
+ for i in 0...self.size
92
+ result[i] /= other[i]
93
+ end
94
+
95
+ return result
96
+ end
97
+
98
+ def **(other)
99
+ raise ArgumentError, "Expected numeric" if !other.is_a?(Numeric)
100
+
101
+ result = self.clone
102
+ for i in 0...self.size
103
+ result[i] = result[i] ** other
104
+ end
105
+
106
+ return result
107
+ end
108
+ end
109
+ end
data/lib/rust/core.rb ADDED
@@ -0,0 +1,7 @@
1
+ require_relative 'core/rust'
2
+ require_relative 'core/csv'
3
+
4
+ self_path = File.expand_path(__FILE__)
5
+ Dir.glob(File.join(File.dirname(self_path), "core/types/*.rb")).each do |lib|
6
+ require_relative lib
7
+ end
@@ -0,0 +1,4 @@
1
+ self_path = File.expand_path(__FILE__)
2
+ Dir.glob(File.dirname(self_path) + "/*.rb").each do |lib|
3
+ require_relative lib unless lib == self_path
4
+ end