rserve-client 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +22 -0
- data/Manifest.txt +4 -0
- data/lib/rserve.rb +4 -1
- data/lib/rserve/connection.rb +16 -2
- data/lib/rserve/protocol/rexpfactory.rb +6 -1
- data/lib/rserve/rexp.rb +9 -5
- data/lib/rserve/rexp/double.rb +20 -3
- data/lib/rserve/with2dnames.rb +67 -0
- data/lib/rserve/with2dsizes.rb +35 -0
- data/spec/rserve_double_spec.rb +23 -4
- data/spec/rserve_rexp_to_ruby_spec.rb +66 -0
- data/spec/rserve_spec.rb +1 -0
- data/spec/rserve_with2dnames_spec.rb +234 -0
- data/spec/rserve_with2dsizes_spec.rb +111 -0
- metadata +58 -58
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -1
data/History.txt
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
=== 0.3.1 / 2013-07-26
|
2
|
+
|
3
|
+
* 2D Names functionality for Arrays and Matrices
|
4
|
+
* A bug that occurs when R returns a named array consisting of only 1
|
5
|
+
* Added code to handle Infinity values from double
|
6
|
+
* Added functionality to recognize 2D named objects
|
7
|
+
* Added specs for infinite? method
|
8
|
+
* Added support for cases when only one dimension of names is specified
|
9
|
+
* Bugfix: undefined method 'new' for #<Rserve::Protocol::REXPFactory
|
10
|
+
* Fix for raw expressions transfer error.Padding to size divisible by 4 was not being done.
|
11
|
+
* Fixed bug for named numerics
|
12
|
+
* Fixed failing specs, split new functionality between With2DNames (applied to Array and Matrix) and With2DSizes (only applied to Array)
|
13
|
+
* Fixed named_2d? not returning booleans
|
14
|
+
* Fixed starting of Rserve when running on JRuby
|
15
|
+
* Fixed starting of server inside Rserve::Connection when running on JRuby
|
16
|
+
* Incorrect Handling of infinite values
|
17
|
+
* Make row_by_name and column_by_name return named arrays
|
18
|
+
* No method exception when converting a "named Float" to ruby
|
19
|
+
* Raw expression transfer to rserve error.
|
20
|
+
* eval Exception is not caused by voidEval
|
21
|
+
|
22
|
+
|
1
23
|
=== 0.3.0 / 2011-12-26
|
2
24
|
|
3
25
|
* Added some extra documentation
|
data/Manifest.txt
CHANGED
@@ -44,6 +44,8 @@ lib/rserve/rfactor.rb
|
|
44
44
|
lib/rserve/rlist.rb
|
45
45
|
lib/rserve/session.rb
|
46
46
|
lib/rserve/talk.rb
|
47
|
+
lib/rserve/with2dnames.rb
|
48
|
+
lib/rserve/with2dsizes.rb
|
47
49
|
lib/rserve/withattributes.rb
|
48
50
|
lib/rserve/withnames.rb
|
49
51
|
spec/rserve_connection_on_unix_spec.rb
|
@@ -63,5 +65,7 @@ spec/rserve_rlist_spec.rb
|
|
63
65
|
spec/rserve_session_spec.rb
|
64
66
|
spec/rserve_spec.rb
|
65
67
|
spec/rserve_talk_spec.rb
|
68
|
+
spec/rserve_with2dnames_spec.rb
|
69
|
+
spec/rserve_with2dsizes_spec.rb
|
66
70
|
spec/rserve_withnames_spec.rb
|
67
71
|
spec/spec_helper.rb
|
data/lib/rserve.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'rbconfig'
|
3
3
|
module Rserve
|
4
|
-
VERSION = '0.3.
|
4
|
+
VERSION = '0.3.1'
|
5
5
|
ON_WINDOWS=RbConfig::CONFIG['arch']=~/mswin|mingw/
|
6
6
|
end
|
7
7
|
|
8
|
+
require 'spoon' if RUBY_PLATFORM == "java"
|
8
9
|
|
9
10
|
require 'rserve/withnames'
|
10
11
|
require 'rserve/withattributes'
|
12
|
+
require 'rserve/with2dnames'
|
13
|
+
require 'rserve/with2dsizes'
|
11
14
|
|
12
15
|
|
13
16
|
require 'rserve/protocol'
|
data/lib/rserve/connection.rb
CHANGED
@@ -75,7 +75,7 @@ module Rserve
|
|
75
75
|
raise ServerNotAvailableError, "Rserve started, but not available on #{hostname}:#{port_number}"
|
76
76
|
# Rserve not available. We should instanciate it first
|
77
77
|
else
|
78
|
-
if
|
78
|
+
if run_server
|
79
79
|
# Wait a moment, please
|
80
80
|
sleep(0.25)
|
81
81
|
retry
|
@@ -200,7 +200,7 @@ module Rserve
|
|
200
200
|
if !rp.nil? and rp.ok?
|
201
201
|
parse_eval_response(rp)
|
202
202
|
else
|
203
|
-
raise EvalError.new(rp), "
|
203
|
+
raise EvalError.new(rp), "eval failed: #{rp.to_s}"
|
204
204
|
end
|
205
205
|
end
|
206
206
|
|
@@ -327,5 +327,19 @@ module Rserve
|
|
327
327
|
raise "Cannot detach"
|
328
328
|
end
|
329
329
|
end
|
330
|
+
|
331
|
+
private
|
332
|
+
|
333
|
+
def run_server
|
334
|
+
if RUBY_PLATFORM != "java"
|
335
|
+
system @cmd_init
|
336
|
+
else
|
337
|
+
pid = Spoon.spawnp *@cmd_init.split
|
338
|
+
return false if pid < 0
|
339
|
+
Process.waitpid pid
|
340
|
+
true
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
330
344
|
end
|
331
345
|
end
|
@@ -265,7 +265,7 @@ module Rserve
|
|
265
265
|
o+=4
|
266
266
|
d=buf[o,as]
|
267
267
|
o = eox;
|
268
|
-
@cont =
|
268
|
+
@cont = REXP::Raw.new(d, get_attr);
|
269
269
|
return o;
|
270
270
|
end
|
271
271
|
|
@@ -617,6 +617,11 @@ module Rserve
|
|
617
617
|
set_int(by.length,buf,off);
|
618
618
|
off+=4
|
619
619
|
by.each_with_index {|v,i| buf[off+i]=v}
|
620
|
+
off+=by.length
|
621
|
+
while ((off & 3) != 0)
|
622
|
+
buf[off] = 0
|
623
|
+
off+=1
|
624
|
+
end
|
620
625
|
elsif(rxt==XT_ARRAY_STR)
|
621
626
|
sa=cont.as_strings
|
622
627
|
io=off
|
data/lib/rserve/rexp.rb
CHANGED
@@ -388,19 +388,23 @@ module Rserve
|
|
388
388
|
#
|
389
389
|
# @return [Object] Ruby object.
|
390
390
|
def to_ruby
|
391
|
-
#pp self
|
392
391
|
v=to_ruby_internal
|
393
|
-
|
394
|
-
if !v.nil? and !v.is_a? Fixnum and !v.is_a? TrueClass and !v.is_a? FalseClass
|
392
|
+
if !v.nil? and !v.is_a? Numeric and !v.is_a? TrueClass and !v.is_a? FalseClass
|
395
393
|
v.extend Rserve::WithAttributes
|
396
394
|
v.attributes=attr.to_ruby unless attr.nil?
|
397
395
|
if !v.attributes.nil? and v.attributes.has_name? 'names'
|
398
396
|
v.attributes['names']=[v.attributes['names']] unless v.attributes['names'].is_a? Array or v.attributes['names'].nil?
|
399
|
-
|
400
397
|
v.extend Rserve::WithNames
|
401
|
-
|
402
398
|
v.names=v.attributes['names']
|
403
399
|
end
|
400
|
+
if v.attributes and v.attributes.has_name? 'dim' and v.attributes.has_name? 'dimnames' and v.attributes['dim'].size == 2
|
401
|
+
if v.is_a? Array
|
402
|
+
v.extend Rserve::With2DSizes
|
403
|
+
v.sizes = v.attributes['dim']
|
404
|
+
end
|
405
|
+
v.extend Rserve::With2DNames
|
406
|
+
v.names = v.attributes['dimnames'].map{|dimension_names| (dimension_names.nil? or dimension_names.is_a?(Array)) ? dimension_names : [dimension_names]}
|
407
|
+
end
|
404
408
|
end
|
405
409
|
|
406
410
|
# Hack: change attribute row.names according to spec
|
data/lib/rserve/rexp/double.rb
CHANGED
@@ -35,7 +35,7 @@ module Rserve
|
|
35
35
|
end
|
36
36
|
def as_integers
|
37
37
|
@payload.map do |v|
|
38
|
-
na?(v) ? nil : v.to_i
|
38
|
+
(na?(v) or Double.infinite?(v)) ? nil : v.to_i
|
39
39
|
end
|
40
40
|
end
|
41
41
|
def as_doubles
|
@@ -53,9 +53,15 @@ module Rserve
|
|
53
53
|
#else
|
54
54
|
# value.respond_to? :nan? and value.nan?
|
55
55
|
#end
|
56
|
-
return (
|
57
|
-
@payload.map {|v| (
|
56
|
+
return _na?(value) unless value.nil?
|
57
|
+
@payload.map {|v| _na?(v) }
|
58
58
|
end
|
59
|
+
|
60
|
+
def self.infinite?(value)
|
61
|
+
value.respond_to?(:infinite?) and value.infinite?
|
62
|
+
end
|
63
|
+
|
64
|
+
|
59
65
|
def to_debug_string
|
60
66
|
t=super
|
61
67
|
t << "{" << @payload.map(&:to_s).join(",") << "}"
|
@@ -71,6 +77,17 @@ module Rserve
|
|
71
77
|
super
|
72
78
|
end
|
73
79
|
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
def _na?(value)
|
84
|
+
if value.is_a? Float
|
85
|
+
return true if value.nan?
|
86
|
+
return false if value.infinite?
|
87
|
+
end
|
88
|
+
value.to_i == NA
|
89
|
+
end
|
90
|
+
|
74
91
|
end
|
75
92
|
end
|
76
93
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Rserve
|
2
|
+
|
3
|
+
module With2DNames
|
4
|
+
attr_reader :row_names, :column_names
|
5
|
+
|
6
|
+
def names=(names)
|
7
|
+
raise ArgumentError, "sizes must be of size 2" unless (names.size == 2)
|
8
|
+
if self.is_a? Array
|
9
|
+
raise ArgumentError, "mismatch between the size of row labels and the actual number of elements" if (names[0] and ((self.count % names[0].size) != 0))
|
10
|
+
raise ArgumentError, "mismatch between the size of column labels and the actual number of elements" if (names[1] and ((self.count % names[1].size) != 0))
|
11
|
+
raise ArgumentError, "mismatch between the provided sizes and the actual number of elements" if (names[0] and names[1] and ((names[0].size * names[1].size) != self.count))
|
12
|
+
elsif self.is_a? Matrix
|
13
|
+
raise ArgumentError, "mismatch between the size of row labels and the actual number of rows" if (names[0] and (names[0].size != self.row_size))
|
14
|
+
raise ArgumentError, "mismatch between the size of column labels and the actual number of rows" if (names[1] and (names[1].size != self.column_size))
|
15
|
+
else
|
16
|
+
raise ArgumentError, "unsupported type for With2DNames"
|
17
|
+
end
|
18
|
+
@row_names = names[0]
|
19
|
+
@column_names = names[1]
|
20
|
+
end
|
21
|
+
|
22
|
+
def by_name(row_name, column_name)
|
23
|
+
return nil unless @row_names and @column_names
|
24
|
+
i = @row_names.index(row_name)
|
25
|
+
j = @column_names.index(column_name)
|
26
|
+
return nil unless i and j
|
27
|
+
if self.is_a? Array
|
28
|
+
at_2d(i, j)
|
29
|
+
elsif self.is_a? Matrix
|
30
|
+
self[i, j]
|
31
|
+
else
|
32
|
+
raise ArgumentError, "unsupported type for With2DNames"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def row_by_name(name)
|
37
|
+
return nil unless @row_names
|
38
|
+
i = @row_names.index(name)
|
39
|
+
return nil unless i
|
40
|
+
result = row(i).to_a
|
41
|
+
if @column_names
|
42
|
+
result.extend WithNames
|
43
|
+
result.names = @column_names
|
44
|
+
end
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
def column_by_name(name)
|
49
|
+
return nil unless @column_names
|
50
|
+
j = @column_names.index(name)
|
51
|
+
return nil unless j
|
52
|
+
result = column(j).to_a
|
53
|
+
if @row_names
|
54
|
+
result.extend WithNames
|
55
|
+
result.names = @row_names
|
56
|
+
end
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def named_2d?
|
61
|
+
!(@row_names.nil? or @column_names.nil?)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Rserve
|
2
|
+
|
3
|
+
module With2DSizes
|
4
|
+
|
5
|
+
attr_reader :column_size, :row_size
|
6
|
+
|
7
|
+
def sizes=(sizes)
|
8
|
+
raise ArgumentError, "sizes must be of size 2" unless (sizes.size == 2)
|
9
|
+
raise ArgumentError, "mismatch between provided size info and actual number of elements" unless self.size == (sizes[0] * sizes[1])
|
10
|
+
@row_size = sizes[0]
|
11
|
+
@column_size = sizes[1]
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def at_2d(i,j)
|
16
|
+
self[i + j * row_size]
|
17
|
+
end
|
18
|
+
|
19
|
+
def row(i)
|
20
|
+
return nil unless (-row_size...row_size) === i
|
21
|
+
each_slice(row_size).map {|col| col[i]}
|
22
|
+
end
|
23
|
+
|
24
|
+
def column(j)
|
25
|
+
return nil unless (-column_size...column_size) === j
|
26
|
+
self[(j * row_size), row_size]
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
end
|
data/spec/rserve_double_spec.rb
CHANGED
@@ -15,22 +15,41 @@ describe Rserve::REXP::Double do
|
|
15
15
|
end
|
16
16
|
describe "NA management" do
|
17
17
|
before do
|
18
|
-
@payload=[3,5,Rserve::REXP::Double::NA, 10,20]
|
18
|
+
@payload=[3,5,Rserve::REXP::Double::NA, 10,20, (1.0 / 0)]
|
19
19
|
@a=Rserve::REXP::Double.new(@payload)
|
20
20
|
end
|
21
21
|
|
22
22
|
it "method na? should return coherent answer" do
|
23
23
|
@a.na?(@a.as_integers[0]).should be_false
|
24
24
|
@a.na?(@a.as_integers[2]).should be_true
|
25
|
-
@a.na?.should==[false,false,true,false,false]
|
25
|
+
@a.na?.should==[false,false,true,false,false, false]
|
26
26
|
end
|
27
27
|
it "to_a should return correct values with NA" do
|
28
|
-
@a.to_a.should==[3,5, nil, 10, 20]
|
28
|
+
@a.to_a.should==[3,5, nil, 10, 20, Float::INFINITY]
|
29
29
|
end
|
30
30
|
it "to_ruby should return correct values with NA" do
|
31
|
-
@a.to_ruby.should==[3,5, nil, 10, 20]
|
31
|
+
@a.to_ruby.should==[3,5, nil, 10, 20, Float::INFINITY]
|
32
32
|
end
|
33
|
+
|
33
34
|
end
|
35
|
+
|
36
|
+
describe "infinite?" do
|
37
|
+
|
38
|
+
it "should return false for non Float objects" do
|
39
|
+
Rserve::REXP::Double.infinite?(1).should be_false
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return false for non infinite floats" do
|
43
|
+
Rserve::REXP::Double.infinite?(Math::PI).should be_false
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return true for infinite floats" do
|
47
|
+
Rserve::REXP::Double.infinite?(-Float::INFINITY).should be_true
|
48
|
+
Rserve::REXP::Double.infinite?(Float::INFINITY).should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
34
53
|
describe "common methods" do
|
35
54
|
before do
|
36
55
|
@n=rand(10)+10
|
@@ -5,6 +5,7 @@ describe "Rserve::REXP#to_ruby" do
|
|
5
5
|
before do
|
6
6
|
@r=Rserve::Connection.new
|
7
7
|
end
|
8
|
+
|
8
9
|
after do
|
9
10
|
@r.close
|
10
11
|
end
|
@@ -74,6 +75,11 @@ describe "Rserve::REXP#to_ruby" do
|
|
74
75
|
it "should return an array of strings with a vector with two or more strings" do
|
75
76
|
@r.eval("c('a','b',NA)").to_ruby.should==['a','b',nil]
|
76
77
|
end
|
78
|
+
it "should handle named floats" do
|
79
|
+
@r.void_eval('y <- sample(1:100, 10)')
|
80
|
+
@r.void_eval('x <- sample(1:100, 10)')
|
81
|
+
@r.eval('lm(y ~ 0 + x)').to_ruby.should be_instance_of(Array)
|
82
|
+
end
|
77
83
|
it "should return an array extended with Rserve::WithNames and Rserve::WithAttributes for a list" do
|
78
84
|
expected=[1,2,3].extend Rserve::WithNames
|
79
85
|
expected.names=%w{a b c}
|
@@ -89,5 +95,65 @@ describe "Rserve::REXP#to_ruby" do
|
|
89
95
|
df.attributes['class'].should=='data.frame'
|
90
96
|
|
91
97
|
end
|
98
|
+
|
99
|
+
context "when passing an object with dim and dimnames attributes" do
|
100
|
+
|
101
|
+
let :attr_with_dimnames do
|
102
|
+
col_names = Rserve::REXP::String.new(%w(c1 c2 c3 c4), nil)
|
103
|
+
row_names = Rserve::REXP::String.new(%w(r1 r2 r3), nil)
|
104
|
+
names_list = Rserve::Rlist.new([row_names, col_names])
|
105
|
+
names_vector = Rserve::REXP::GenericVector.new(names_list)
|
106
|
+
dim_array = [3,4]
|
107
|
+
dimensions = Rserve::REXP::Integer.new(dim_array)
|
108
|
+
attr_payload = Rserve::Rlist.new([dimensions, names_vector], %w(dim dimnames))
|
109
|
+
Rserve::REXP::List.new(attr_payload)
|
110
|
+
end
|
111
|
+
|
112
|
+
context "and the object is converted to an array" do
|
113
|
+
|
114
|
+
before do
|
115
|
+
payload = [true, true, true, true, true, true, false, true, true, false, false, true]
|
116
|
+
@array = Rserve::REXP::Logical.new(payload, attr_with_dimnames).to_ruby
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should return a 2d object as an array with Rserve::With2DNames and Rserve::With2DSizes" do
|
120
|
+
@array.should be_an Array
|
121
|
+
@array.should be_a Rserve::With2DNames
|
122
|
+
@array.should be_a Rserve::With2DSizes
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should set the row and column labels" do
|
126
|
+
@array.row_names.should == %w(r1 r2 r3)
|
127
|
+
@array.column_names.should == %W(c1 c2 c3 c4)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should set the row and column sizes" do
|
131
|
+
@array.row_size.should == 3
|
132
|
+
@array.column_size.should == 4
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
context "and the object is converted to a matrix" do
|
139
|
+
|
140
|
+
before do
|
141
|
+
payload = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]
|
142
|
+
@matrix = Rserve::REXP::Double.new(payload, attr_with_dimnames).to_ruby
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should return a Matrix with Rserve::With2DNames" do
|
146
|
+
@matrix.should be_a Matrix
|
147
|
+
@matrix.should be_a Rserve::With2DNames
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should set the row and column labels" do
|
151
|
+
@matrix.row_names.should == %w(r1 r2 r3)
|
152
|
+
@matrix.column_names.should == %W(c1 c2 c3 c4)
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
92
158
|
end
|
93
159
|
end
|
data/spec/rserve_spec.rb
CHANGED
@@ -39,6 +39,7 @@ severity <- as.data.frame(cbind(diseasesev,temperature))
|
|
39
39
|
severity.lm <- lm(diseasesev~temperature,data=severity)
|
40
40
|
EOF
|
41
41
|
@r.void_eval(script)
|
42
|
+
temp = @r.eval('severity.lm')
|
42
43
|
@r.eval('severity.lm').should be_instance_of(Rserve::REXP::GenericVector)
|
43
44
|
@r.eval('severity.lm').to_ruby.should be_true
|
44
45
|
@r.eval('summary(severity.lm)').should be_true
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)+"/spec_helper.rb")
|
2
|
+
|
3
|
+
describe Rserve::With2DNames do
|
4
|
+
|
5
|
+
before do
|
6
|
+
|
7
|
+
# when a 2d object is returned by R as an array
|
8
|
+
# the elements are listed column by column
|
9
|
+
@array = [1,5,9,2,6,10,3,7,11,4,8,12]
|
10
|
+
# corresponds to a matrix like
|
11
|
+
# 1 2 3 4
|
12
|
+
# 5 6 7 8
|
13
|
+
# 9 10 11 12
|
14
|
+
@array.extend Rserve::With2DSizes
|
15
|
+
@array.sizes = [3,4]
|
16
|
+
@array.extend Rserve::With2DNames
|
17
|
+
@matrix = Matrix[[1,2,3,4],[5,6,7,8],[9,10,11,12]]
|
18
|
+
@matrix.extend Rserve::With2DNames
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "names=" do
|
22
|
+
|
23
|
+
context "when passed the correct values" do
|
24
|
+
|
25
|
+
it "should set the names for rows and columns for an array" do
|
26
|
+
@array.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
|
27
|
+
@array.row_names.should == %w(r1 r2 r3)
|
28
|
+
@array.column_names.should == %w(c1 c2 c3 c4)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should set the names for rows and columns for an array" do
|
32
|
+
@matrix.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
|
33
|
+
@matrix.row_names.should == %w(r1 r2 r3)
|
34
|
+
@matrix.column_names.should == %w(c1 c2 c3 c4)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when passing only names for one dimension" do
|
40
|
+
|
41
|
+
before do
|
42
|
+
@array.names = [%w(r1 r2 r3), nil]
|
43
|
+
@matrix.names = [nil, %w(c1 c2 c3 c4)]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should not assign the missing names" do
|
47
|
+
@array.column_names.should be_nil
|
48
|
+
@matrix.row_names.should be_nil
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when passed the wrong values" do
|
54
|
+
|
55
|
+
it "should throw if the object passed does not have two elements" do
|
56
|
+
expect{@array.names = [%w(r1 r2 r3)]}.to raise_error ArgumentError
|
57
|
+
expect{@matrix.names = [%w(r1 r2 r3)]}.to raise_error ArgumentError
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should throw if the sizes passed do not match the array size" do
|
61
|
+
expect{@array.names = [%w(r1 r2 r3 r4),%w(c1 c2 c3 c4)]}.to raise_error ArgumentError
|
62
|
+
expect{@matrix.names = [%w(r1 r2 r3 r4),%w(c1 c2 c3 c4)]}.to raise_error ArgumentError
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "row_by_name" do
|
70
|
+
|
71
|
+
before do
|
72
|
+
@array.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
|
73
|
+
@matrix.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when the name is present" do
|
77
|
+
|
78
|
+
it "should return the correct row for arrays" do
|
79
|
+
@array.row_by_name("r2").should == [5, 6, 7, 8]
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should return the correct row as an array for matrices" do
|
83
|
+
@matrix.row_by_name("r2").should == [5, 6, 7, 8]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should return a named object" do
|
87
|
+
@array.row_by_name("r1").named?.should == true
|
88
|
+
@matrix.row_by_name("r1").named?.should == true
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
context "when no row names have been assigned" do
|
94
|
+
|
95
|
+
it "should return nil" do
|
96
|
+
@array.names = [nil,%w(c1 c2 c3 c4)]
|
97
|
+
@matrix.names = [nil,%w(c1 c2 c3 c4)]
|
98
|
+
@array.row_by_name("r1").should be_nil
|
99
|
+
@matrix.row_by_name("r2").should be_nil
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
context "when no column names have been assigned" do
|
105
|
+
|
106
|
+
it "should return a plain (unnamed) array" do
|
107
|
+
@array.names = [%w(r1 r2 r3), nil]
|
108
|
+
@matrix.names = [%w(r1 r2 r3),nil]
|
109
|
+
@array.row_by_name("r1").should_not be_a Rserve::WithNames
|
110
|
+
@matrix.row_by_name("r2").should_not be_a Rserve::WithNames
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
context "when the index is out of range" do
|
116
|
+
|
117
|
+
it "should return nil" do
|
118
|
+
@array.row_by_name("not_there").should be_nil
|
119
|
+
@matrix.row_by_name("not_there").should be_nil
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "column_by_name" do
|
127
|
+
|
128
|
+
before do
|
129
|
+
@array.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
|
130
|
+
@matrix.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
|
131
|
+
end
|
132
|
+
|
133
|
+
context "when the name is present" do
|
134
|
+
|
135
|
+
it "should return the correct column for arrays" do
|
136
|
+
@array.column_by_name("c1").should == [1, 5, 9]
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should return the correct column as an array for matrix" do
|
140
|
+
@matrix.column_by_name("c1").should == [1, 5, 9]
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should return a named object" do
|
144
|
+
@array.column_by_name("c1").named?.should == true
|
145
|
+
@matrix.column_by_name("c1").named?.should == true
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
context "when no column names have been assigned" do
|
151
|
+
|
152
|
+
it "should return nil" do
|
153
|
+
@array.names = [%w(r1 r2 r3), nil]
|
154
|
+
@matrix.names = [%w(r1 r2 r3),nil]
|
155
|
+
@array.column_by_name("c1").should be_nil
|
156
|
+
@matrix.column_by_name("c2").should be_nil
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
context "when no row names have been assigned" do
|
162
|
+
|
163
|
+
it "should return a plain (unnamed) array" do
|
164
|
+
@array.names = [nil,%w(c1 c2 c3 c4)]
|
165
|
+
@matrix.names = [nil,%w(c1 c2 c3 c4)]
|
166
|
+
@array.column_by_name("c1").should_not be_a Rserve::WithNames
|
167
|
+
@matrix.column_by_name("c2").should_not be_a Rserve::WithNames
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
context "when the name is not present" do
|
173
|
+
|
174
|
+
it "should return nil" do
|
175
|
+
@array.column_by_name("not_there").should be_nil
|
176
|
+
@matrix.column_by_name("not_there").should be_nil
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
describe "named_2d?" do
|
184
|
+
|
185
|
+
it "should return false if no names are set" do
|
186
|
+
@array.named_2d?.should == false
|
187
|
+
@matrix.named_2d?.should == false
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should return true if names have been set" do
|
191
|
+
@array.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
|
192
|
+
@matrix.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
|
193
|
+
@array.named_2d?.should == true
|
194
|
+
@matrix.named_2d?.should == true
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
it "should return false if only one dimension names are set" do
|
199
|
+
@array.names = [%w(r1 r2 r3), nil]
|
200
|
+
@matrix.names = [nil, %w(c1 c2 c3 c4)]
|
201
|
+
@array.named_2d?.should == false
|
202
|
+
@array.named_2d?.should == false
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
describe "by_name" do
|
208
|
+
|
209
|
+
before do
|
210
|
+
@array.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
|
211
|
+
@matrix.names = [%w(r1 r2 r3),%w(c1 c2 c3 c4)]
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should return the correct value" do
|
215
|
+
@array.by_name("r1","c2").should == 2
|
216
|
+
@matrix.by_name("r3","c4").should == 12
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should return nil if either of the parameters is not found" do
|
220
|
+
@array.by_name("not_there","c1").should be_nil
|
221
|
+
@matrix.by_name("r1","not_there").should be_nil
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should return nil when either column or row names are set" do
|
226
|
+
@array.names = [nil,%w(c1 c2 c3 c4)]
|
227
|
+
@matrix.names = [%w(r1 r2 r3),nil]
|
228
|
+
@array.by_name("r1","c1").should be_nil
|
229
|
+
@matrix.by_name("r1","c1").should be_nil
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)+"/spec_helper.rb")
|
2
|
+
|
3
|
+
describe Rserve::With2DSizes do
|
4
|
+
|
5
|
+
before do
|
6
|
+
|
7
|
+
# when a 2d object is returned by R as an array
|
8
|
+
# the elements are listed column by column
|
9
|
+
@array = [1,5,9,2,6,10,3,7,11,4,8,12]
|
10
|
+
# corresponds to a matrix like
|
11
|
+
# 1 2 3 4
|
12
|
+
# 5 6 7 8
|
13
|
+
# 9 10 11 12
|
14
|
+
@array.extend Rserve::With2DSizes
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "sizes=" do
|
18
|
+
|
19
|
+
context "when passed the correct values" do
|
20
|
+
|
21
|
+
it "should set the size values for rows and columns" do
|
22
|
+
@array.sizes = [3,4]
|
23
|
+
@array.row_size.should == 3
|
24
|
+
@array.column_size.should == 4
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when passed the wrong values" do
|
30
|
+
|
31
|
+
it "should throw if the object passed does not have two elements" do
|
32
|
+
expect{@array.sizes = [5]}.to raise_error ArgumentError
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should throw if the sizes passed do not match the array size" do
|
36
|
+
expect{@array.sizes = [1,5]}.to raise_error ArgumentError
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "row" do
|
44
|
+
|
45
|
+
before do
|
46
|
+
@array.sizes = [3,4]
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when the index is in the valid range" do
|
50
|
+
|
51
|
+
it "should return the correct row" do
|
52
|
+
@array.row(0).should == [1, 2, 3, 4]
|
53
|
+
@array.row(-1).should == [9, 10, 11, 12]
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when the index is out of range" do
|
59
|
+
|
60
|
+
it "should return nil" do
|
61
|
+
@array.row(3).should be_nil
|
62
|
+
@array.row(-4).should be_nil
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "column" do
|
70
|
+
|
71
|
+
before do
|
72
|
+
@array.sizes = [3,4]
|
73
|
+
end
|
74
|
+
|
75
|
+
context "when the index is in the valid range" do
|
76
|
+
|
77
|
+
it "should return the correct column" do
|
78
|
+
@array.column(0).should == [1, 5, 9]
|
79
|
+
@array.column(-1).should == [4, 8, 12]
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
context "when the index is out of range" do
|
85
|
+
|
86
|
+
it "should return nil" do
|
87
|
+
@array.column(4).should be_nil
|
88
|
+
@array.column(-5).should be_nil
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "at_2d" do
|
96
|
+
|
97
|
+
before do
|
98
|
+
@array.sizes = [3,4]
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
it "should return the correct values" do
|
103
|
+
@array.at_2d(0,0).should == 1
|
104
|
+
@array.at_2d(2,0).should == 9
|
105
|
+
@array.at_2d(0,3).should == 4
|
106
|
+
@array.at_2d(2,3).should == 12
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
metadata
CHANGED
@@ -1,46 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rserve-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Claudio Bustos
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
|
-
cert_chain:
|
12
|
-
-
|
13
|
-
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNakNDQWhxZ0F3SUJB
|
14
|
-
Z0lCQURBTkJna3Foa2lHOXcwQkFRVUZBREEvTVJFd0R3WURWUVFEREFoamJH
|
15
|
-
SjEKYzNSdmN6RVZNQk1HQ2dtU0pvbVQ4aXhrQVJrV0JXZHRZV2xzTVJNd0VR
|
16
|
-
WUtDWkltaVpQeUxHUUJHUllEWTI5dApNQjRYRFRFd01ETXlPVEl4TXpnMU5W
|
17
|
-
b1hEVEV4TURNeU9USXhNemcxTlZvd1B6RVJNQThHQTFVRUF3d0lZMnhpCmRY
|
18
|
-
TjBiM014RlRBVEJnb0praWFKay9Jc1pBRVpGZ1ZuYldGcGJERVRNQkVHQ2dt
|
19
|
-
U0pvbVQ4aXhrQVJrV0EyTnYKYlRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFB
|
20
|
-
RGdnRVBBRENDQVFvQ2dnRUJBTGY4SlZNR3FFN201a1liK1BOTgpuZVp2MnBj
|
21
|
-
WFY1ZlFDaTZ4a3lHOGJpMi9TSUZ5L0x5eHV2THpFZU94QmVhejFCZTkzYmF5
|
22
|
-
SVVxdU9JcXczZHl3Ci9LWFdhMzFGeHVOdXZBbTZDTjhmeWVSWVgvb3U0Y3cz
|
23
|
-
T0lVVW5JdkI3Uk1OSXU0d2JnZU02aHRWL1FFc05McnYKYXQxL21oOUpwcWF3
|
24
|
-
UHJjaklPVk1qNEJJcDY3dm16SkNhVWYrUy9IMnVZdFNPMDlGK1lRRTN0djg1
|
25
|
-
VFBlUm1xVQp5anlYeVRjL29KaXcxY1hza1VMOFV0TVdabXJ3TkxIWHVaV1dJ
|
26
|
-
TXpraml6M1VOZGhKci90NVJPazhTMldQem5sCjBiTXkvUE1JbEFicVdvbFJu
|
27
|
-
MXpsMlZGSjNUYVhTY2JxSW1ZOFdmNGc2MmIvMVpTVWxHcnRuTE5zQ1lYcldp
|
28
|
-
c28KVVBVQ0F3RUFBYU01TURjd0NRWURWUjBUQkFJd0FEQUxCZ05WSFE4RUJB
|
29
|
-
TUNCTEF3SFFZRFZSME9CQllFRkd1OQpyckoxSDY0cVJtTk51M0pqL1Fqdmgw
|
30
|
-
dTVNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0SUJBUUNWMFVua2E1aXNyaFprCkdq
|
31
|
-
cVNEcVkvNmhGK0cycGJGY2JXVXBqbUM4Tld0QXhlQys3TkdWM2xqZDBlMVNM
|
32
|
-
Zm95Qmo0Z25GdEZtWThxWDQKSzAydGdTWk0wZURWOFRwZ0ZwV1h6SzZMekh2
|
33
|
-
b2FudWFoSExaRXRrLytaODg1bEZlbmUrbkhhZGtlbTFuOWlBQgpjczk2Sk85
|
34
|
-
L0pmRnl1WE0yN3dGQXdtZkhDbUpmUEYwOVI0VnZHSFJBdmI4TUd6U1ZnazJp
|
35
|
-
MDZPSlRxa0JUd3Z2CkpISmRveXczKzhidzlSSitqTGFOb1EreHUrMXBRZFMy
|
36
|
-
YmIzbTd4alpwdWZtbC9tOHpGQ3RqWU0vN3Fna0tSOHoKL1padDhsQ2lLZkZB
|
37
|
-
cnBwUnJaYXlFMkZWc3BzNFg2V3dCZHJLVE1aMENLU1hUUmN0YkVqMUJBWjY3
|
38
|
-
ZW9UdkJCdApycFAwampzMAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
|
39
|
-
date: 2011-12-26 00:00:00.000000000 Z
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-26 00:00:00.000000000 Z
|
40
13
|
dependencies:
|
41
14
|
- !ruby/object:Gem::Dependency
|
42
15
|
name: rubyforge
|
43
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
44
17
|
none: false
|
45
18
|
requirements:
|
46
19
|
- - ! '>='
|
@@ -48,40 +21,60 @@ dependencies:
|
|
48
21
|
version: 2.0.4
|
49
22
|
type: :development
|
50
23
|
prerelease: false
|
51
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.0.4
|
52
30
|
- !ruby/object:Gem::Dependency
|
53
|
-
name:
|
54
|
-
requirement:
|
31
|
+
name: rdoc
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
55
33
|
none: false
|
56
34
|
requirements:
|
57
35
|
- - ~>
|
58
36
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
37
|
+
version: '4.0'
|
60
38
|
type: :development
|
61
39
|
prerelease: false
|
62
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '4.0'
|
63
46
|
- !ruby/object:Gem::Dependency
|
64
|
-
name:
|
65
|
-
requirement:
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
66
49
|
none: false
|
67
50
|
requirements:
|
68
51
|
- - ~>
|
69
52
|
- !ruby/object:Gem::Version
|
70
|
-
version: '2.
|
53
|
+
version: '2.0'
|
71
54
|
type: :development
|
72
55
|
prerelease: false
|
73
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
74
62
|
- !ruby/object:Gem::Dependency
|
75
|
-
name:
|
76
|
-
requirement:
|
63
|
+
name: hoe
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
77
65
|
none: false
|
78
66
|
requirements:
|
79
67
|
- - ~>
|
80
68
|
- !ruby/object:Gem::Version
|
81
|
-
version: '3.
|
69
|
+
version: '3.7'
|
82
70
|
type: :development
|
83
71
|
prerelease: false
|
84
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.7'
|
85
78
|
description: ! 'Ruby client for Rserve, a Binary R server (http://www.rforge.net/Rserve/).
|
86
79
|
|
87
80
|
|
@@ -143,6 +136,8 @@ files:
|
|
143
136
|
- lib/rserve/rlist.rb
|
144
137
|
- lib/rserve/session.rb
|
145
138
|
- lib/rserve/talk.rb
|
139
|
+
- lib/rserve/with2dnames.rb
|
140
|
+
- lib/rserve/with2dsizes.rb
|
146
141
|
- lib/rserve/withattributes.rb
|
147
142
|
- lib/rserve/withnames.rb
|
148
143
|
- spec/rserve_connection_on_unix_spec.rb
|
@@ -162,11 +157,14 @@ files:
|
|
162
157
|
- spec/rserve_session_spec.rb
|
163
158
|
- spec/rserve_spec.rb
|
164
159
|
- spec/rserve_talk_spec.rb
|
160
|
+
- spec/rserve_with2dnames_spec.rb
|
161
|
+
- spec/rserve_with2dsizes_spec.rb
|
165
162
|
- spec/rserve_withnames_spec.rb
|
166
163
|
- spec/spec_helper.rb
|
167
164
|
- .gemtest
|
168
165
|
homepage: http://github.com/clbustos/Rserve-Ruby-client
|
169
|
-
licenses:
|
166
|
+
licenses:
|
167
|
+
- MIT
|
170
168
|
post_install_message:
|
171
169
|
rdoc_options:
|
172
170
|
- --main
|
@@ -187,26 +185,28 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
187
185
|
version: '0'
|
188
186
|
requirements: []
|
189
187
|
rubyforge_project: ruby-statsample
|
190
|
-
rubygems_version: 1.8.
|
188
|
+
rubygems_version: 1.8.25
|
191
189
|
signing_key:
|
192
190
|
specification_version: 3
|
193
191
|
summary: Ruby client for Rserve, a Binary R server (http://www.rforge.net/Rserve/)
|
194
192
|
test_files:
|
195
|
-
- spec/
|
196
|
-
- spec/rserve_rexp_wrapper_spec.rb
|
197
|
-
- spec/rserve_rfactor_spec.rb
|
193
|
+
- spec/rserve_with2dnames_spec.rb
|
198
194
|
- spec/rserve_connection_spec.rb
|
199
|
-
- spec/
|
195
|
+
- spec/rserve_rfactor_spec.rb
|
200
196
|
- spec/rserve_double_spec.rb
|
201
|
-
- spec/
|
197
|
+
- spec/rserve_rexp_to_ruby_spec.rb
|
198
|
+
- spec/rserve_logical_spec.rb
|
199
|
+
- spec/rserve_session_spec.rb
|
200
|
+
- spec/rserve_talk_spec.rb
|
201
|
+
- spec/rserve_rlist_spec.rb
|
202
|
+
- spec/rserve_genericvector_spec.rb
|
203
|
+
- spec/rserve_with2dsizes_spec.rb
|
202
204
|
- spec/rserve_packet_spec.rb
|
203
|
-
- spec/
|
205
|
+
- spec/rserve_integer_spec.rb
|
204
206
|
- spec/rserve_rexpfactory_spec.rb
|
207
|
+
- spec/rserve_withnames_spec.rb
|
208
|
+
- spec/rserve_protocol_spec.rb
|
205
209
|
- spec/rserve_rexp_spec.rb
|
206
|
-
- spec/rserve_integer_spec.rb
|
207
210
|
- spec/rserve_spec.rb
|
208
|
-
- spec/
|
209
|
-
- spec/
|
210
|
-
- spec/rserve_session_spec.rb
|
211
|
-
- spec/rserve_logical_spec.rb
|
212
|
-
- spec/rserve_talk_spec.rb
|
211
|
+
- spec/rserve_rexp_wrapper_spec.rb
|
212
|
+
- spec/rserve_connection_on_unix_spec.rb
|
data.tar.gz.sig
DELETED
Binary file
|
metadata.gz.sig
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
��e�h9hܚ����_�m��f-Mb®L�'������E�*��v�Y��- @��F"XA#MbuÚkrm+�d��-��{��D��e�V>�4�m�����G\
|