nmatrix 0.0.3 → 0.0.4
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.
- data/.gitignore +3 -0
- data/CONTRIBUTING.md +66 -0
- data/Gemfile +1 -1
- data/History.txt +68 -10
- data/LICENSE.txt +2 -2
- data/Manifest.txt +2 -0
- data/README.rdoc +90 -69
- data/Rakefile +18 -9
- data/ext/nmatrix/data/complex.h +7 -7
- data/ext/nmatrix/data/data.cpp +2 -7
- data/ext/nmatrix/data/data.h +7 -4
- data/ext/nmatrix/data/rational.h +2 -2
- data/ext/nmatrix/data/ruby_object.h +3 -10
- data/ext/nmatrix/extconf.rb +79 -54
- data/ext/nmatrix/new_extconf.rb +11 -12
- data/ext/nmatrix/nmatrix.cpp +94 -125
- data/ext/nmatrix/nmatrix.h +38 -17
- data/ext/nmatrix/ruby_constants.cpp +2 -15
- data/ext/nmatrix/ruby_constants.h +2 -14
- data/ext/nmatrix/storage/common.cpp +2 -2
- data/ext/nmatrix/storage/common.h +2 -2
- data/ext/nmatrix/storage/dense.cpp +206 -31
- data/ext/nmatrix/storage/dense.h +5 -2
- data/ext/nmatrix/storage/list.cpp +52 -4
- data/ext/nmatrix/storage/list.h +3 -2
- data/ext/nmatrix/storage/storage.cpp +6 -6
- data/ext/nmatrix/storage/storage.h +2 -2
- data/ext/nmatrix/storage/yale.cpp +202 -49
- data/ext/nmatrix/storage/yale.h +5 -4
- data/ext/nmatrix/ttable_helper.rb +108 -108
- data/ext/nmatrix/types.h +2 -15
- data/ext/nmatrix/util/io.cpp +2 -2
- data/ext/nmatrix/util/io.h +2 -2
- data/ext/nmatrix/util/lapack.h +2 -2
- data/ext/nmatrix/util/math.cpp +14 -14
- data/ext/nmatrix/util/math.h +2 -2
- data/ext/nmatrix/util/sl_list.cpp +2 -2
- data/ext/nmatrix/util/sl_list.h +2 -2
- data/ext/nmatrix/util/util.h +2 -2
- data/lib/nmatrix.rb +13 -35
- data/lib/nmatrix/blas.rb +182 -56
- data/lib/nmatrix/io/market.rb +38 -14
- data/lib/nmatrix/io/mat5_reader.rb +393 -278
- data/lib/nmatrix/io/mat_reader.rb +121 -107
- data/lib/nmatrix/lapack.rb +59 -14
- data/lib/nmatrix/monkeys.rb +32 -30
- data/lib/nmatrix/nmatrix.rb +204 -100
- data/lib/nmatrix/nvector.rb +166 -57
- data/lib/nmatrix/shortcuts.rb +364 -231
- data/lib/nmatrix/version.rb +8 -4
- data/nmatrix.gemspec +5 -3
- data/scripts/mac-brew-gcc.sh +1 -1
- data/spec/blas_spec.rb +80 -2
- data/spec/math_spec.rb +78 -32
- data/spec/nmatrix_list_spec.rb +55 -55
- data/spec/nmatrix_spec.rb +60 -117
- data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
- data/spec/nmatrix_yale_spec.rb +214 -198
- data/spec/nvector_spec.rb +58 -2
- data/spec/shortcuts_spec.rb +156 -32
- data/spec/slice_spec.rb +229 -178
- data/spec/spec_helper.rb +2 -2
- metadata +71 -21
data/lib/nmatrix/blas.rb
CHANGED
@@ -1,70 +1,196 @@
|
|
1
|
-
|
1
|
+
#--
|
2
|
+
# = NMatrix
|
3
|
+
#
|
4
|
+
# A linear algebra library for scientific computation in Ruby.
|
5
|
+
# NMatrix is part of SciRuby.
|
6
|
+
#
|
7
|
+
# NMatrix was originally inspired by and derived from NArray, by
|
8
|
+
# Masahiro Tanaka: http://narray.rubyforge.org
|
9
|
+
#
|
10
|
+
# == Copyright Information
|
11
|
+
#
|
12
|
+
# SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
|
13
|
+
# NMatrix is Copyright (c) 2013, Ruby Science Foundation
|
14
|
+
#
|
15
|
+
# Please see LICENSE.txt for additional copyright notices.
|
16
|
+
#
|
17
|
+
# == Contributing
|
18
|
+
#
|
19
|
+
# By contributing source code to SciRuby, you agree to be bound by
|
20
|
+
# our Contributor Agreement:
|
21
|
+
#
|
22
|
+
# * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
|
23
|
+
#
|
24
|
+
# == blas.rb
|
25
|
+
#
|
26
|
+
# This file contains the safer accessors for the BLAS functions
|
27
|
+
# supported by NMatrix.
|
28
|
+
#++
|
2
29
|
|
30
|
+
module NMatrix::BLAS
|
3
31
|
class << self
|
4
|
-
|
32
|
+
#
|
33
|
+
# call-seq:
|
34
|
+
# gemm(a, b) -> NMatrix
|
35
|
+
# gemm(a, b, c) -> NMatrix
|
36
|
+
# gemm(a, b, c, alpha, beta) -> NMatrix
|
37
|
+
#
|
38
|
+
# Updates the value of C via the matrix multiplication
|
39
|
+
# C = (alpha * A * B) + (beta * C)
|
40
|
+
# where +alpha+ and +beta+ are scalar values.
|
41
|
+
#
|
42
|
+
# * *Arguments* :
|
43
|
+
# - +a+ -> Matrix A.
|
44
|
+
# - +b+ -> Matrix B.
|
45
|
+
# - +c+ -> Matrix C.
|
46
|
+
# - +alpha+ -> A scalar value that multiplies A * B.
|
47
|
+
# - +beta+ -> A scalar value that multiplies C.
|
48
|
+
# - +transpose_a+ ->
|
49
|
+
# - +transpose_b+ ->
|
50
|
+
# - +m+ ->
|
51
|
+
# - +n+ ->
|
52
|
+
# - +k+ ->
|
53
|
+
# - +lda+ ->
|
54
|
+
# - +ldb+ ->
|
55
|
+
# - +ldc+ ->
|
56
|
+
# * *Returns* :
|
57
|
+
# - A NMatrix equal to (alpha * A * B) + (beta * C).
|
58
|
+
# * *Raises* :
|
59
|
+
# - +ArgumentError+ -> +a+ and +b+ must be dense matrices.
|
60
|
+
# - +ArgumentError+ -> +c+ must be +nil+ or a dense matrix.
|
61
|
+
# - +ArgumentError+ -> The dtype of the matrices must be equal.
|
62
|
+
#
|
5
63
|
def gemm(a, b, c = nil, alpha = 1.0, beta = 0.0, transpose_a = false, transpose_b = false, m = nil, n = nil, k = nil, lda = nil, ldb = nil, ldc = nil)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
64
|
+
raise ArgumentError, 'Expected dense NMatrices as first two arguments.' unless a.is_a?(NMatrix) and b.is_a?(NMatrix) and a.stype == :dense and b.stype == :dense
|
65
|
+
raise ArgumentError, 'Expected nil or dense NMatrix as third argument.' unless c.nil? or (c.is_a?(NMatrix) and c.stype == :dense)
|
66
|
+
raise ArgumentError, 'NMatrix dtype mismatch.' unless a.dtype == b.dtype and (c ? a.dtype == c.dtype : true)
|
67
|
+
|
68
|
+
# First, set m, n, and k, which depend on whether we're taking the
|
69
|
+
# transpose of a and b.
|
70
|
+
if c
|
71
|
+
m ||= c.shape[0]
|
72
|
+
n ||= c.shape[1]
|
73
|
+
k ||= transpose_a ? a.shape[0] : a.shape[1]
|
74
|
+
|
75
|
+
else
|
76
|
+
if transpose_a
|
77
|
+
# Either :transpose or :complex_conjugate.
|
78
|
+
m ||= a.shape[1]
|
79
|
+
k ||= a.shape[0]
|
80
|
+
|
81
|
+
else
|
82
|
+
# No transpose.
|
83
|
+
m ||= a.shape[0]
|
84
|
+
k ||= a.shape[1]
|
85
|
+
end
|
86
|
+
|
87
|
+
n ||= transpose_b ? b.shape[0] : b.shape[1]
|
88
|
+
c = NMatrix.new([m, n], a.dtype)
|
89
|
+
end
|
90
|
+
|
91
|
+
# I think these are independent of whether or not a transpose occurs.
|
92
|
+
lda ||= a.shape[1]
|
93
|
+
ldb ||= b.shape[1]
|
94
|
+
ldc ||= c.shape[1]
|
95
|
+
|
96
|
+
# NM_COMPLEX64 and NM_COMPLEX128 both require complex alpha and beta.
|
97
|
+
if a.dtype == :complex64 or a.dtype == :complex128
|
98
|
+
alpha = Complex(1.0, 0.0) if alpha == 1.0
|
99
|
+
beta = Complex(0.0, 0.0) if beta == 0.0
|
100
|
+
end
|
101
|
+
|
102
|
+
# For argument descriptions, see: http://www.netlib.org/blas/dgemm.f
|
103
|
+
::NMatrix::BLAS.cblas_gemm(:row, transpose_a, transpose_b, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc)
|
104
|
+
|
105
|
+
return c
|
48
106
|
end
|
49
107
|
|
108
|
+
#
|
109
|
+
# call-seq:
|
110
|
+
# gemv(a, x) -> NVector
|
111
|
+
# gemv(a, x, y) -> NVector
|
112
|
+
# gemv(a, x, y, alpha, beta) -> NVector
|
113
|
+
#
|
114
|
+
# Implements matrix-vector product via
|
115
|
+
# y = (alpha * A * x) + (beta * y)
|
116
|
+
# where +alpha+ and +beta+ are scalar values.
|
117
|
+
#
|
118
|
+
# * *Arguments* :
|
119
|
+
# - +a+ -> Matrix A.
|
120
|
+
# - +x+ -> Vector x.
|
121
|
+
# - +y+ -> Vector y.
|
122
|
+
# - +alpha+ -> A scalar value that multiplies A * x.
|
123
|
+
# - +beta+ -> A scalar value that multiplies y.
|
124
|
+
# - +transpose_a+ ->
|
125
|
+
# - +m+ ->
|
126
|
+
# - +n+ ->
|
127
|
+
# - +lda+ ->
|
128
|
+
# - +incx+ ->
|
129
|
+
# - +incy+ ->
|
130
|
+
# * *Returns* :
|
131
|
+
# -
|
132
|
+
# * *Raises* :
|
133
|
+
# - ++ ->
|
134
|
+
#
|
50
135
|
def gemv(a, x, y = nil, alpha = 1.0, beta = 0.0, transpose_a = false, m = nil, n = nil, lda = nil, incx = nil, incy = nil)
|
51
|
-
|
52
|
-
|
136
|
+
raise ArgumentError, 'Expected dense NMatrices as first two arguments.' unless a.is_a?(NMatrix) and x.is_a?(NMatrix) and a.stype == :dense and x.stype == :dense
|
137
|
+
raise ArgumentError, 'Expected nil or dense NMatrix as third argument.' unless y.nil? or (y.is_a?(NMatrix) and y.stype == :dense)
|
138
|
+
raise ArgumentError, 'NMatrix dtype mismatch.' unless a.dtype == x.dtype and (y ? a.dtype == y.dtype : true)
|
53
139
|
|
54
|
-
|
55
|
-
|
56
|
-
incy ||= 1
|
140
|
+
m ||= transpose_a ? a.shape[1] : a.shape[0]
|
141
|
+
n ||= transpose_a ? a.shape[0] : a.shape[1]
|
57
142
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
beta = Complex(0.0, 0.0) if beta == 0.0
|
62
|
-
end
|
143
|
+
lda ||= a.shape[1]
|
144
|
+
incx ||= 1
|
145
|
+
incy ||= 1
|
63
146
|
|
64
|
-
|
147
|
+
# NM_COMPLEX64 and NM_COMPLEX128 both require complex alpha and beta.
|
148
|
+
if a.dtype == :complex64 or a.dtype == :complex128
|
149
|
+
alpha = Complex(1.0, 0.0) if alpha == 1.0
|
150
|
+
beta = Complex(0.0, 0.0) if beta == 0.0
|
151
|
+
end
|
65
152
|
|
66
|
-
|
153
|
+
y ||= NMatrix.new([m, n], a.dtype)
|
154
|
+
|
155
|
+
::NMatrix::BLAS.cblas_gemv(transpose_a, m, n, alpha, a, lda, x, incx, beta, y, incy)
|
156
|
+
|
157
|
+
return y
|
67
158
|
end
|
68
159
|
|
160
|
+
#
|
161
|
+
# call-seq:
|
162
|
+
# rot(x, y, c, s)
|
163
|
+
#
|
164
|
+
# Apply plane rotation.
|
165
|
+
#
|
166
|
+
# * *Arguments* :
|
167
|
+
# - +x+ ->
|
168
|
+
# - +y+ ->
|
169
|
+
# - +s+ ->
|
170
|
+
# - +c+ ->
|
171
|
+
# - +incx+ ->
|
172
|
+
# - +incy+ ->
|
173
|
+
# - +n+ ->
|
174
|
+
# * *Returns* :
|
175
|
+
# - Array with the results, in the format [xx, yy]
|
176
|
+
# * *Raises* :
|
177
|
+
# - +ArgumentError+ -> Expected dense NMatrices as first two arguments.
|
178
|
+
# - +ArgumentError+ -> Nmatrix dtype mismatch.
|
179
|
+
# - +ArgumentError+ -> Need to supply n for non-standard incx, incy values.
|
180
|
+
#
|
181
|
+
def rot(x, y, c, s, incx = 1, incy = 1, n = nil)
|
182
|
+
raise ArgumentError, 'Expected dense NMatrices as first two arguments.' unless x.is_a?(NMatrix) and y.is_a?(NMatrix) and x.stype == :dense and y.stype == :dense
|
183
|
+
raise ArgumentError, 'NMatrix dtype mismatch.' unless x.dtype == y.dtype
|
184
|
+
raise ArgumentError, 'Need to supply n for non-standard incx, incy values' if n.nil? && incx != 1 && incx != -1 && incy != 1 && incy != -1
|
185
|
+
|
186
|
+
n ||= x.size > y.size ? y.size : x.size
|
187
|
+
|
188
|
+
xx = x.clone
|
189
|
+
yy = y.clone
|
190
|
+
|
191
|
+
::NMatrix::BLAS.cblas_rot(n, xx, incx, yy, incy, c, s)
|
192
|
+
|
193
|
+
return [xx,yy]
|
194
|
+
end
|
69
195
|
end
|
70
196
|
end
|
data/lib/nmatrix/io/market.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# = NMatrix
|
2
3
|
#
|
3
4
|
# A linear algebra library for scientific computation in Ruby.
|
@@ -24,22 +25,33 @@
|
|
24
25
|
#
|
25
26
|
# MatrixMarket reader and writer.
|
26
27
|
#
|
28
|
+
#++
|
29
|
+
|
27
30
|
module NMatrix::IO::Market
|
28
31
|
CONVERTER_AND_DTYPE = {
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
32
|
+
:real => [:to_f, :float64],
|
33
|
+
:complex => [:to_c, :complex128],
|
34
|
+
:integer => [:to_i, :int64],
|
35
|
+
:pattern => [:to_i, :byte]
|
33
36
|
}
|
34
37
|
|
35
38
|
ENTRY_TYPE = {
|
36
|
-
|
37
|
-
|
39
|
+
:byte => :integer, :int8 => :integer, :int16 => :integer, :int32 => :integer, :int64 => :integer,
|
40
|
+
:float32 => :real, :float64 => :real, :complex64 => :complex, :complex128 => :complex
|
38
41
|
}
|
39
42
|
|
40
43
|
class << self
|
44
|
+
#
|
45
|
+
# call-seq:
|
46
|
+
# load(filename) ->
|
47
|
+
#
|
48
|
+
# * *Arguments* :
|
49
|
+
# - +filename+ -> String with the filename to be saved.
|
50
|
+
# * *Raises* :
|
51
|
+
# - +IOError+ -> expected type code line beginning with '%%MatrixMarket matrix'
|
52
|
+
#
|
41
53
|
# Load a MatrixMarket file. Requires a filename as an argument.
|
42
|
-
def load
|
54
|
+
def load(filename)
|
43
55
|
|
44
56
|
f = File.new(filename, "r")
|
45
57
|
|
@@ -60,12 +72,24 @@ module NMatrix::IO::Market
|
|
60
72
|
end
|
61
73
|
end
|
62
74
|
|
63
|
-
|
64
|
-
#
|
65
|
-
#
|
66
|
-
|
75
|
+
#
|
76
|
+
# call-seq:
|
77
|
+
# save(matrix, filename, options = {}) -> true
|
78
|
+
#
|
79
|
+
# Can optionally set :symmetry to :general, :symmetric, :hermitian; and can
|
80
|
+
# set :pattern => true if you're writing a sparse matrix and don't want
|
81
|
+
# values stored.
|
82
|
+
#
|
83
|
+
# * *Arguments* :
|
84
|
+
# - +matrix+ -> NMatrix with the data to be saved.
|
85
|
+
# - +filename+ -> String with the filename to be saved.
|
86
|
+
# * *Raises* :
|
87
|
+
# - +DataTypeError+ -> MatrixMarket does not support rational or Ruby objects.
|
88
|
+
# - +ArgumentError+ -> Expected two-dimensional NMatrix.
|
89
|
+
#
|
90
|
+
def save(matrix, filename, options = {})
|
67
91
|
options = {:pattern => false,
|
68
|
-
|
92
|
+
:symmetry => :general}.merge(options)
|
69
93
|
|
70
94
|
mode = matrix.stype == :dense ? :array : :coordinate
|
71
95
|
if [:rational32,:rational64,:rational128,:object].include?(matrix.dtype)
|
@@ -91,7 +115,7 @@ module NMatrix::IO::Market
|
|
91
115
|
end
|
92
116
|
|
93
117
|
|
94
|
-
|
118
|
+
protected
|
95
119
|
|
96
120
|
def save_coordinate matrix, file, symmetry, pattern
|
97
121
|
# Convert to a hash in order to store
|
@@ -224,4 +248,4 @@ module NMatrix::IO::Market
|
|
224
248
|
mat
|
225
249
|
end
|
226
250
|
end
|
227
|
-
end
|
251
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# = NMatrix
|
2
3
|
#
|
3
4
|
# A linear algebra library for scientific computation in Ruby.
|
@@ -24,14 +25,16 @@
|
|
24
25
|
#
|
25
26
|
# Matlab version 5 .mat file reader (and eventually writer too).
|
26
27
|
#
|
28
|
+
#++
|
27
29
|
|
28
|
-
|
29
|
-
require_relative 'mat_reader.rb'
|
30
|
+
require_relative './mat_reader.rb'
|
30
31
|
|
31
32
|
module NMatrix::IO::Matlab
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
#
|
34
|
+
# Reader (and eventual writer) for a version 5 .mat file.
|
35
|
+
#
|
36
|
+
class Mat5Reader < MatReader
|
37
|
+
attr_reader :file_header, :first_tag_field, :first_data_field
|
35
38
|
|
36
39
|
class Compressed
|
37
40
|
include Packable
|
@@ -39,6 +42,15 @@ module NMatrix::IO::Matlab
|
|
39
42
|
|
40
43
|
attr_reader :byte_order
|
41
44
|
|
45
|
+
#
|
46
|
+
# call-seq:
|
47
|
+
# new(stream = nil, byte_order = nil, content_or_bytes = nil) -> Mat5Reader::Compressed
|
48
|
+
#
|
49
|
+
# * *Arguments* :
|
50
|
+
# - ++ ->
|
51
|
+
# * *Raises* :
|
52
|
+
# - ++ ->
|
53
|
+
#
|
42
54
|
def initialize(stream = nil, byte_order = nil, content_or_bytes = nil)
|
43
55
|
@stream = stream
|
44
56
|
@byte_order = byte_order
|
@@ -48,35 +60,69 @@ module NMatrix::IO::Matlab
|
|
48
60
|
|
49
61
|
elsif content_or_bytes.is_a?(Fixnum)
|
50
62
|
@padded_bytes = content_or_bytes
|
51
|
-
|
52
|
-
|
63
|
+
#else
|
64
|
+
# raise ArgumentError, "Need a content string or a number of bytes; content_or_bytes is #{content_or_bytes.class.to_s}."
|
53
65
|
end
|
54
66
|
end
|
55
67
|
|
68
|
+
#
|
69
|
+
# call-seq:
|
70
|
+
# compressed ->
|
71
|
+
#
|
56
72
|
def compressed
|
57
73
|
require "zlib"
|
58
74
|
# [2..-5] removes headers
|
59
75
|
@compressed ||= Zlib::Deflate.deflate(content)
|
60
76
|
end
|
61
77
|
|
78
|
+
#
|
79
|
+
# call-seq:
|
80
|
+
# content ->
|
81
|
+
#
|
62
82
|
def content
|
63
83
|
@content ||= extract
|
64
84
|
end
|
65
85
|
|
86
|
+
#
|
87
|
+
# call-seq:
|
88
|
+
# padded_bytes ->
|
89
|
+
#
|
66
90
|
def padded_bytes
|
67
91
|
@padded_bytes ||= content.size % 4 == 0 ? content.size : (content.size / 4 + 1) * 4
|
68
92
|
end
|
69
93
|
|
70
|
-
|
94
|
+
#
|
95
|
+
# call-seq:
|
96
|
+
# write_packed(packedio, options = {}) ->
|
97
|
+
#
|
98
|
+
# * *Arguments* :
|
99
|
+
# - ++ ->
|
100
|
+
# * *Returns* :
|
101
|
+
# -
|
102
|
+
#
|
103
|
+
def write_packed(packedio, options = {})
|
71
104
|
packedio << [compressed, {:bytes => padded_bytes}.merge(options)]
|
72
105
|
end
|
73
106
|
|
107
|
+
#
|
108
|
+
# call-seq:
|
109
|
+
# read_packed(packedio, options = {}) ->
|
110
|
+
#
|
111
|
+
# * *Arguments* :
|
112
|
+
# - ++ ->
|
113
|
+
# * *Returns* :
|
114
|
+
# -
|
115
|
+
#
|
74
116
|
def read_packed(packedio, options)
|
75
117
|
@compressed = (packedio >> [String, options]).first
|
76
118
|
content
|
77
119
|
end
|
78
120
|
|
79
121
|
protected
|
122
|
+
#
|
123
|
+
# call-seq:
|
124
|
+
# extract ->
|
125
|
+
#
|
80
126
|
def extract
|
81
127
|
require 'zlib'
|
82
128
|
|
@@ -90,26 +136,43 @@ module NMatrix::IO::Matlab
|
|
90
136
|
end
|
91
137
|
|
92
138
|
MatrixDataStruct = Struct.new(
|
93
|
-
|
94
|
-
|
95
|
-
|
139
|
+
:cells, :logical, :global, :complex, :nonzero_max,
|
140
|
+
:matlab_class, :dimensions, :matlab_name, :real_part,
|
141
|
+
:imaginary_part, :row_index, :column_index)
|
96
142
|
|
97
143
|
class MatrixData < MatrixDataStruct
|
98
144
|
include Packable
|
99
145
|
|
146
|
+
#
|
147
|
+
# call-seq:
|
148
|
+
# write_packed(packedio, options) ->
|
149
|
+
#
|
150
|
+
# * *Arguments* :
|
151
|
+
# - ++ ->
|
152
|
+
# * *Returns* :
|
153
|
+
# -
|
154
|
+
#
|
100
155
|
def write_packed(packedio, options)
|
101
156
|
raise NotImplementedError
|
102
157
|
packedio << [info, {:bytes => padded_bytes}.merge(options)]
|
103
158
|
end
|
104
159
|
|
105
|
-
#
|
106
|
-
#
|
160
|
+
#
|
161
|
+
# call-seq:
|
162
|
+
# to_ruby -> NMatrix or Array
|
163
|
+
#
|
164
|
+
# Figure out the appropriate Ruby type to convert to, and do it. There
|
165
|
+
# are basically two possible types: +NMatrix+ and +Array+. This method
|
166
|
+
# is recursive, so an +Array+ is going to contain other +Array+s and/or
|
167
|
+
# +NMatrix+ objects.
|
107
168
|
#
|
108
169
|
# mxCELL types (cells) will be converted to the Array type.
|
109
170
|
#
|
110
|
-
# mxSPARSE and other types will be converted to NMatrix, with the
|
171
|
+
# mxSPARSE and other types will be converted to NMatrix, with the
|
172
|
+
# appropriate stype (:yale or :dense, respectively).
|
111
173
|
#
|
112
174
|
# See also to_nm, which is responsible for NMatrix instantiation.
|
175
|
+
#
|
113
176
|
def to_ruby
|
114
177
|
case matlab_class
|
115
178
|
when :mxSPARSE then return to_nm
|
@@ -118,9 +181,15 @@ module NMatrix::IO::Matlab
|
|
118
181
|
end
|
119
182
|
end
|
120
183
|
|
184
|
+
#
|
185
|
+
# call-seq:
|
186
|
+
# guess_dtype_from_mdtype -> Symbol
|
187
|
+
#
|
121
188
|
# Try to determine what dtype and such to use.
|
122
189
|
#
|
123
|
-
# TODO: Needs to be verified that unsigned MATLAB types are being
|
190
|
+
# TODO: Needs to be verified that unsigned MATLAB types are being
|
191
|
+
# converted to the correct NMatrix signed dtypes.
|
192
|
+
#
|
124
193
|
def guess_dtype_from_mdtype
|
125
194
|
dtype = MatReader::MDTYPE_TO_DTYPE[self.real_part.tag.data_type]
|
126
195
|
|
@@ -129,10 +198,16 @@ module NMatrix::IO::Matlab
|
|
129
198
|
dtype == :float32 ? :complex64 : :complex128
|
130
199
|
end
|
131
200
|
|
201
|
+
#
|
202
|
+
# call-seq:
|
203
|
+
# unpacked_data(real_mdtype = nil, imag_mdtype = nil) ->
|
204
|
+
#
|
132
205
|
# Unpacks data without repacking it.
|
133
206
|
#
|
134
|
-
# Used only for dense matrix creation. Yale matrix creation uses
|
135
|
-
|
207
|
+
# Used only for dense matrix creation. Yale matrix creation uses
|
208
|
+
# repacked_data.
|
209
|
+
#
|
210
|
+
def unpacked_data(real_mdtype = nil, imag_mdtype = nil)
|
136
211
|
# Get Matlab data type and unpack args
|
137
212
|
real_mdtype ||= self.real_part.tag.data_type
|
138
213
|
real_unpack_args = MatReader::MDTYPE_UNPACK_ARGS[real_mdtype]
|
@@ -153,22 +228,30 @@ module NMatrix::IO::Matlab
|
|
153
228
|
|
154
229
|
end
|
155
230
|
|
231
|
+
#
|
232
|
+
# call-seq:
|
233
|
+
# repacked_data(to_dtype = nil) ->
|
234
|
+
#
|
156
235
|
# Unpacks and repacks data into the appropriate format for NMatrix.
|
157
236
|
#
|
158
|
-
# If data is already in the appropriate format, does not unpack or
|
237
|
+
# If data is already in the appropriate format, does not unpack or
|
238
|
+
# repack, just returns directly.
|
159
239
|
#
|
160
|
-
# Complex is always unpacked and repacked, as the real and imaginary
|
161
|
-
# stores them separately for
|
240
|
+
# Complex is always unpacked and repacked, as the real and imaginary
|
241
|
+
# components must be merged together (MATLAB stores them separately for
|
242
|
+
# some crazy reason).
|
162
243
|
#
|
163
244
|
# Used only for Yale storage creation. For dense, see unpacked_data.
|
164
245
|
#
|
165
|
-
# This function calls repack and complex_merge, which are both defined in
|
246
|
+
# This function calls repack and complex_merge, which are both defined in
|
247
|
+
# io.cpp.
|
166
248
|
def repacked_data(to_dtype = nil)
|
167
249
|
|
168
250
|
real_mdtype = self.real_part.tag.data_type
|
169
251
|
|
170
|
-
# Figure out what dtype to use based on the MATLAB data-types
|
171
|
-
#
|
252
|
+
# Figure out what dtype to use based on the MATLAB data-types
|
253
|
+
# (mdtypes). They could be different for real and imaginary, so call
|
254
|
+
# upcast to figure out what to use.
|
172
255
|
|
173
256
|
components = [] # real and imaginary parts or just the real part
|
174
257
|
|
@@ -208,10 +291,15 @@ module NMatrix::IO::Matlab
|
|
208
291
|
to_dtype]
|
209
292
|
end
|
210
293
|
|
211
|
-
|
294
|
+
#
|
295
|
+
# call-seq:
|
296
|
+
# repacked_indices(to_itype) ->
|
297
|
+
#
|
212
298
|
# Unpacks and repacks index data into the appropriate format for NMatrix.
|
213
299
|
#
|
214
|
-
# If data is already in the appropriate format, does not unpack or
|
300
|
+
# If data is already in the appropriate format, does not unpack or
|
301
|
+
# repack, just returns directly.
|
302
|
+
#
|
215
303
|
def repacked_indices(to_itype)
|
216
304
|
return [row_index.data, column_index.data] if to_itype == :uint32 # No need to re-pack -- already correct
|
217
305
|
|
@@ -222,18 +310,27 @@ module NMatrix::IO::Matlab
|
|
222
310
|
[repacked_row_indices, repacked_col_indices]
|
223
311
|
end
|
224
312
|
|
225
|
-
|
313
|
+
#
|
314
|
+
# call-seq:
|
315
|
+
# to_nm(dtype = nil) -> NMatrix
|
316
|
+
#
|
226
317
|
# Create an NMatrix from a MATLAB .mat (v5) matrix.
|
227
318
|
#
|
228
|
-
# This function matches the storage type exactly. That is, a regular
|
229
|
-
#
|
319
|
+
# This function matches the storage type exactly. That is, a regular
|
320
|
+
# matrix in MATLAB will be a dense NMatrix, and a sparse (old Yale) one
|
321
|
+
# in MATLAB will be a :yale (new Yale) matrix in NMatrix.
|
322
|
+
#
|
323
|
+
# Note that NMatrix has no old Yale type, so this uses a semi-hidden
|
324
|
+
# version of the NMatrix constructor to pass in --- as directly as
|
325
|
+
# possible -- the stored bytes in a MATLAB sparse matrix. This
|
326
|
+
# constructor should also be used for other IO formats that want to
|
327
|
+
# create sparse matrices from IA and JA vectors (e.g., SciPy).
|
230
328
|
#
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
329
|
+
# This is probably not the fastest code. An ideal solution would be a C
|
330
|
+
# plugin of some sort for reading the MATLAB .mat file. However, .mat v5
|
331
|
+
# is a really complicated format, and lends itself to an object-oriented
|
332
|
+
# solution.
|
234
333
|
#
|
235
|
-
# This is probably not the fastest code. An ideal solution would be a C plugin of some sort for reading the MATLAB
|
236
|
-
# .mat file. However, .mat v5 is a really complicated format, and lends itself to an object-oriented solution.
|
237
334
|
def to_nm(dtype = nil)
|
238
335
|
# Hardest part is figuring out from_dtype, from_index_dtype, and dtype.
|
239
336
|
dtype ||= guess_dtype_from_mdtype
|
@@ -261,6 +358,15 @@ module NMatrix::IO::Matlab
|
|
261
358
|
end
|
262
359
|
end
|
263
360
|
|
361
|
+
#
|
362
|
+
# call-seq:
|
363
|
+
# read_packed(packedio, options) ->
|
364
|
+
#
|
365
|
+
# * *Arguments* :
|
366
|
+
# - ++ ->
|
367
|
+
# * *Returns* :
|
368
|
+
# -
|
369
|
+
#
|
264
370
|
def read_packed(packedio, options)
|
265
371
|
flags_class, self.nonzero_max = packedio.read([Element, options]).data
|
266
372
|
|
@@ -313,255 +419,264 @@ module NMatrix::IO::Matlab
|
|
313
419
|
end
|
314
420
|
end
|
315
421
|
|
316
|
-
|
422
|
+
#
|
423
|
+
# call-seq:
|
424
|
+
# ignore_padding(packedio, bytes) ->
|
425
|
+
#
|
426
|
+
# * *Arguments* :
|
427
|
+
# - ++ ->
|
428
|
+
# * *Returns* :
|
429
|
+
# -
|
430
|
+
#
|
431
|
+
def ignore_padding(packedio, bytes)
|
317
432
|
packedio.read([Integer, {:unsigned => true, :bytes => bytes}]) if bytes > 0
|
318
433
|
end
|
319
434
|
end
|
320
435
|
|
321
436
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
437
|
+
MDTYPE_UNPACK_ARGS =
|
438
|
+
MatReader::MDTYPE_UNPACK_ARGS.merge({
|
439
|
+
:miCOMPRESSED => [Compressed, {}],
|
440
|
+
:miMATRIX => [MatrixData, {}]
|
441
|
+
})
|
442
|
+
# include TaggedDataEnumerable
|
443
|
+
|
444
|
+
FIRST_TAG_FIELD_POS = 128
|
445
|
+
|
446
|
+
####################
|
447
|
+
# Instance Methods #
|
448
|
+
####################
|
449
|
+
|
450
|
+
def initialize(stream, options = {})
|
451
|
+
super(stream, options)
|
452
|
+
@file_header = seek_and_read_file_header
|
453
|
+
end
|
454
|
+
|
455
|
+
def to_a
|
456
|
+
returning(Array.new) do |ary|
|
457
|
+
self.each { |el| ary << el }
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
def to_ruby
|
462
|
+
ary = self.to_a
|
463
|
+
|
464
|
+
if ary.size == 1
|
465
|
+
ary.first.to_ruby
|
466
|
+
else
|
467
|
+
ary.collect { |item| item.to_ruby }
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
def guess_byte_order
|
472
|
+
stream.seek(Header::BYTE_ORDER_POS)
|
473
|
+
mi = stream.read(Header::BYTE_ORDER_LENGTH)
|
474
|
+
stream.seek(0)
|
475
|
+
mi == 'IM' ? :little : :big
|
476
|
+
end
|
477
|
+
|
478
|
+
def seek_and_read_file_header
|
479
|
+
stream.seek(0)
|
480
|
+
stream.read(FIRST_TAG_FIELD_POS).unpack(Header, {:endian => byte_order})
|
481
|
+
end
|
482
|
+
|
483
|
+
def each(&block)
|
484
|
+
stream.each(Element, {:endian => byte_order}) do |element|
|
485
|
+
if element.data.is_a?(Compressed)
|
486
|
+
StringIO.new(element.data.content, 'rb').each(Element, {:endian => byte_order}) do |compressed_element|
|
487
|
+
yield compressed_element.data
|
488
|
+
end
|
489
|
+
|
490
|
+
else
|
491
|
+
yield element.data
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
# Go back to the beginning in case we want to do it again.
|
496
|
+
stream.seek(FIRST_TAG_FIELD_POS)
|
497
|
+
|
498
|
+
self
|
499
|
+
end
|
500
|
+
|
501
|
+
####################
|
502
|
+
# Internal Classes #
|
503
|
+
####################
|
504
|
+
|
505
|
+
class Header < Struct.new(:desc, :data_offset, :version, :endian)
|
506
|
+
|
507
|
+
include Packable
|
508
|
+
|
509
|
+
BYTE_ORDER_LENGTH = 2
|
510
|
+
DESC_LENGTH = 116
|
511
|
+
DATA_OFFSET_LENGTH = 8
|
512
|
+
VERSION_LENGTH = 2
|
513
|
+
BYTE_ORDER_POS = 126
|
514
|
+
|
515
|
+
## TODO: TEST WRITE.
|
516
|
+
def write_packed(packedio, options)
|
517
|
+
packedio << [desc, {:bytes => DESC_LENGTH }] <<
|
518
|
+
[data_offset, {:bytes => DATA_OFFSET_LENGTH }] <<
|
519
|
+
[version, {:bytes => VERSION_LENGTH }] <<
|
520
|
+
[byte_order, {:bytes => BYTE_ORDER_LENGTH }]
|
521
|
+
end
|
522
|
+
|
523
|
+
def read_packed(packedio, options)
|
524
|
+
self.desc, self.data_offset, self.version, self.endian = packedio >>
|
410
525
|
[String, {:bytes => DESC_LENGTH }] >>
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
526
|
+
[String, {:bytes => DATA_OFFSET_LENGTH }] >>
|
527
|
+
[Integer, {:bytes => VERSION_LENGTH, :endian => options[:endian] }] >>
|
528
|
+
[String, {:bytes => 2 }]
|
529
|
+
|
530
|
+
self.desc.strip!
|
531
|
+
self.data_offset.strip!
|
532
|
+
self.data_offset = nil if self.data_offset.empty?
|
533
|
+
|
534
|
+
self.endian == 'IM' ? :little : :big
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
class Tag < Struct.new(:data_type, :raw_data_type, :bytes, :small)
|
539
|
+
include Packable
|
540
|
+
|
541
|
+
DATA_TYPE_OPTS = BYTES_OPTS = {:bytes => 4, :signed => false}
|
542
|
+
LENGTH = DATA_TYPE_OPTS[:bytes] + BYTES_OPTS[:bytes]
|
543
|
+
|
544
|
+
## TODO: TEST WRITE.
|
545
|
+
def write_packed packedio, options
|
546
|
+
packedio << [data_type, DATA_TYPE_OPTS] << [bytes, BYTES_OPTS]
|
547
|
+
end
|
548
|
+
|
549
|
+
def small?
|
550
|
+
self.bytes > 0 and self.bytes <= 4
|
551
|
+
end
|
552
|
+
|
553
|
+
def size
|
554
|
+
small? ? 4 : 8
|
555
|
+
end
|
556
|
+
|
557
|
+
def read_packed packedio, options
|
558
|
+
self.raw_data_type = packedio.read([Integer, DATA_TYPE_OPTS.merge(options)])
|
559
|
+
|
560
|
+
# Borrowed from a SciPy patch
|
561
|
+
upper = self.raw_data_type >> 16
|
562
|
+
lower = self.raw_data_type & 0xFFFF
|
563
|
+
|
564
|
+
if upper > 0
|
565
|
+
# Small data element format
|
566
|
+
raise IOError, 'Small data element format indicated, but length is more than 4 bytes!' if upper > 4
|
567
|
+
|
568
|
+
self.bytes = upper
|
569
|
+
self.raw_data_type = lower
|
570
|
+
|
571
|
+
else
|
572
|
+
self.bytes = packedio.read([Integer, BYTES_OPTS.merge(options)])
|
573
|
+
end
|
574
|
+
|
575
|
+
self.data_type = MatReader::MDTYPES[self.raw_data_type]
|
576
|
+
end
|
577
|
+
|
578
|
+
def inspect
|
579
|
+
"#<#{self.class.to_s} data_type=#{data_type}[#{raw_data_type}][#{raw_data_type.to_s(2)}] bytes=#{bytes} size=#{size}#{small? ? ' small' : ''}>"
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
583
|
+
|
584
|
+
class ElementDataIOError < IOError
|
585
|
+
attr_reader :tag
|
586
|
+
|
587
|
+
def initialize(tag = nil, msg = nil)
|
588
|
+
@tag = tag
|
589
|
+
super msg
|
590
|
+
end
|
591
|
+
|
592
|
+
def to_s
|
593
|
+
@tag.inspect + "\n" + super
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
|
598
|
+
class Element < Struct.new(:tag, :data)
|
599
|
+
include Packable
|
600
|
+
|
601
|
+
def write_packed packedio, options
|
602
|
+
packedio << [tag, {}] << [data, {}]
|
603
|
+
end
|
604
|
+
|
605
|
+
def read_packed(packedio, options)
|
606
|
+
raise(ArgumentError, 'Missing mandatory option :endian.') unless options.has_key?(:endian)
|
607
|
+
|
608
|
+
tag = packedio.read([Tag, {:endian => options[:endian]}])
|
609
|
+
#STDERR.puts tag.inspect
|
610
|
+
data_type = MDTYPE_UNPACK_ARGS[tag.data_type]
|
611
|
+
|
612
|
+
self.tag = tag
|
613
|
+
#STDERR.puts self.tag.inspect
|
614
|
+
|
615
|
+
raise ElementDataIOError.new(tag, "Unrecognized Matlab type #{tag.raw_data_type}") if data_type.nil?
|
616
|
+
|
617
|
+
if tag.bytes == 0
|
618
|
+
self.data = []
|
619
|
+
|
620
|
+
else
|
621
|
+
number_of_reads = data_type[1].has_key?(:bytes) ? tag.bytes / data_type[1][:bytes] : 1
|
622
|
+
data_type[1].merge!({:endian => options[:endian]})
|
623
|
+
|
624
|
+
if number_of_reads == 1
|
625
|
+
self.data = packedio.read(data_type)
|
626
|
+
|
627
|
+
else
|
628
|
+
self.data =
|
629
|
+
returning(Array.new) do |ary|
|
630
|
+
number_of_reads.times { ary << packedio.read(data_type) }
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
begin
|
635
|
+
ignore_padding(packedio, (tag.bytes + tag.size) % 8) unless [:miMATRIX, :miCOMPRESSED].include?(tag.data_type)
|
636
|
+
|
637
|
+
rescue EOFError
|
638
|
+
STDERR.puts self.tag.inspect
|
639
|
+
raise(ElementDataIOError.new(tag, "Ignored too much"))
|
640
|
+
end
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
def ignore_padding(packedio, bytes)
|
645
|
+
if bytes > 0
|
646
|
+
#STDERR.puts "Ignored #{8 - bytes} on #{self.tag.data_type}"
|
647
|
+
ignored = packedio.read(8 - bytes)
|
648
|
+
ignored_unpacked = ignored.unpack("C*")
|
649
|
+
raise(IOError, "Nonzero padding detected: #{ignored_unpacked}") if ignored_unpacked.any? { |i| i != 0 }
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
def to_ruby
|
654
|
+
data.to_ruby
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
# Doesn't unpack the contents of the element, e.g., if we want to handle
|
659
|
+
# manually, or pass the raw string of bytes into NMatrix.
|
660
|
+
class RawElement < Element
|
661
|
+
def read_packed(packedio, options)
|
662
|
+
raise(ArgumentError, 'Missing mandatory option :endian.') unless options.has_key?(:endian)
|
663
|
+
|
664
|
+
self.tag = packedio.read([Tag, {:endian => options[:endian] }])
|
665
|
+
self.data = packedio.read([String, {:endian => options[:endian], :bytes => tag.bytes }])
|
666
|
+
|
667
|
+
begin
|
668
|
+
ignore_padding(packedio, (tag.bytes + tag.size) % 8) unless [:miMATRIX, :miCOMPRESSED].include?(tag.data_type)
|
669
|
+
|
670
|
+
rescue EOFError
|
671
|
+
STDERR.puts self.tag.inspect
|
672
|
+
raise ElementDataIOError.new(tag, 'Ignored too much.')
|
673
|
+
end
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
#####################
|
678
|
+
# End of Mat5Reader #
|
679
|
+
#####################
|
680
|
+
|
681
|
+
end
|
567
682
|
end
|