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.
Files changed (63) hide show
  1. data/.gitignore +3 -0
  2. data/CONTRIBUTING.md +66 -0
  3. data/Gemfile +1 -1
  4. data/History.txt +68 -10
  5. data/LICENSE.txt +2 -2
  6. data/Manifest.txt +2 -0
  7. data/README.rdoc +90 -69
  8. data/Rakefile +18 -9
  9. data/ext/nmatrix/data/complex.h +7 -7
  10. data/ext/nmatrix/data/data.cpp +2 -7
  11. data/ext/nmatrix/data/data.h +7 -4
  12. data/ext/nmatrix/data/rational.h +2 -2
  13. data/ext/nmatrix/data/ruby_object.h +3 -10
  14. data/ext/nmatrix/extconf.rb +79 -54
  15. data/ext/nmatrix/new_extconf.rb +11 -12
  16. data/ext/nmatrix/nmatrix.cpp +94 -125
  17. data/ext/nmatrix/nmatrix.h +38 -17
  18. data/ext/nmatrix/ruby_constants.cpp +2 -15
  19. data/ext/nmatrix/ruby_constants.h +2 -14
  20. data/ext/nmatrix/storage/common.cpp +2 -2
  21. data/ext/nmatrix/storage/common.h +2 -2
  22. data/ext/nmatrix/storage/dense.cpp +206 -31
  23. data/ext/nmatrix/storage/dense.h +5 -2
  24. data/ext/nmatrix/storage/list.cpp +52 -4
  25. data/ext/nmatrix/storage/list.h +3 -2
  26. data/ext/nmatrix/storage/storage.cpp +6 -6
  27. data/ext/nmatrix/storage/storage.h +2 -2
  28. data/ext/nmatrix/storage/yale.cpp +202 -49
  29. data/ext/nmatrix/storage/yale.h +5 -4
  30. data/ext/nmatrix/ttable_helper.rb +108 -108
  31. data/ext/nmatrix/types.h +2 -15
  32. data/ext/nmatrix/util/io.cpp +2 -2
  33. data/ext/nmatrix/util/io.h +2 -2
  34. data/ext/nmatrix/util/lapack.h +2 -2
  35. data/ext/nmatrix/util/math.cpp +14 -14
  36. data/ext/nmatrix/util/math.h +2 -2
  37. data/ext/nmatrix/util/sl_list.cpp +2 -2
  38. data/ext/nmatrix/util/sl_list.h +2 -2
  39. data/ext/nmatrix/util/util.h +2 -2
  40. data/lib/nmatrix.rb +13 -35
  41. data/lib/nmatrix/blas.rb +182 -56
  42. data/lib/nmatrix/io/market.rb +38 -14
  43. data/lib/nmatrix/io/mat5_reader.rb +393 -278
  44. data/lib/nmatrix/io/mat_reader.rb +121 -107
  45. data/lib/nmatrix/lapack.rb +59 -14
  46. data/lib/nmatrix/monkeys.rb +32 -30
  47. data/lib/nmatrix/nmatrix.rb +204 -100
  48. data/lib/nmatrix/nvector.rb +166 -57
  49. data/lib/nmatrix/shortcuts.rb +364 -231
  50. data/lib/nmatrix/version.rb +8 -4
  51. data/nmatrix.gemspec +5 -3
  52. data/scripts/mac-brew-gcc.sh +1 -1
  53. data/spec/blas_spec.rb +80 -2
  54. data/spec/math_spec.rb +78 -32
  55. data/spec/nmatrix_list_spec.rb +55 -55
  56. data/spec/nmatrix_spec.rb +60 -117
  57. data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
  58. data/spec/nmatrix_yale_spec.rb +214 -198
  59. data/spec/nvector_spec.rb +58 -2
  60. data/spec/shortcuts_spec.rb +156 -32
  61. data/spec/slice_spec.rb +229 -178
  62. data/spec/spec_helper.rb +2 -2
  63. metadata +71 -21
@@ -1,70 +1,196 @@
1
- module NMatrix::BLAS
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
- 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
7
- raise ArgumentError, 'Expected nil or dense NMatrix as third argument.' unless c.nil? or (c.is_a?(NMatrix) and c.stype == :dense)
8
- raise ArgumentError, 'NMatrix dtype mismatch.' unless a.dtype == b.dtype and (c ? a.dtype == c.dtype : true)
9
-
10
- # First, set m, n, and k, which depend on whether we're taking the
11
- # transpose of a and b.
12
- if c
13
- m ||= c.shape[0]
14
- n ||= c.shape[1]
15
- k ||= transpose_a ? a.shape[0] : a.shape[1]
16
-
17
- else
18
- if transpose_a
19
- # Either :transpose or :complex_conjugate.
20
- m ||= a.shape[1]
21
- k ||= a.shape[0]
22
-
23
- else
24
- # No transpose.
25
- m ||= a.shape[0]
26
- k ||= a.shape[1]
27
- end
28
-
29
- n ||= transpose_b ? b.shape[0] : b.shape[1]
30
- c = NMatrix.new([m, n], a.dtype)
31
- end
32
-
33
- # I think these are independent of whether or not a transpose occurs.
34
- lda ||= a.shape[1]
35
- ldb ||= b.shape[1]
36
- ldc ||= c.shape[1]
37
-
38
- # NM_COMPLEX64 and NM_COMPLEX128 both require complex alpha and beta.
39
- if a.dtype == :complex64 or a.dtype == :complex128
40
- alpha = Complex(1.0, 0.0) if alpha == 1.0
41
- beta = Complex(0.0, 0.0) if beta == 0.0
42
- end
43
-
44
- # For argument descriptions, see: http://www.netlib.org/blas/dgemm.f
45
- ::NMatrix::BLAS.cblas_gemm(:row, transpose_a, transpose_b, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc)
46
-
47
- return c
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
- m ||= transpose_a ? a.shape[1] : a.shape[0]
52
- n ||= transpose_a ? a.shape[0] : a.shape[1]
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
- lda ||= a.shape[1]
55
- incx ||= 1
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
- # NM_COMPLEX64 and NM_COMPLEX128 both require complex alpha and beta.
59
- if a.dtype == :complex64 or a.dtype == :complex128
60
- alpha = Complex(1.0, 0.0) if alpha == 1.0
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
- ::NMatrix::BLAS.cblas_gemv(transpose_a, m, n, alpha, a, lda, x, incx, beta, y, incy)
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
- return y
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
@@ -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
- :real => [:to_f, :float64],
30
- :complex => [:to_c, :complex128],
31
- :integer => [:to_i, :int64],
32
- :pattern => [:to_i, :byte]
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
- :byte => :integer, :int8 => :integer, :int16 => :integer, :int32 => :integer, :int64 => :integer,
37
- :float32 => :real, :float64 => :real, :complex64 => :complex, :complex128 => :complex
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 filename
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
- # Can optionally set :symmetry to :general, :symmetric, :hermitian; and can set :pattern => true if you're writing
65
- # a sparse matrix and don't want values stored.
66
- def save matrix, filename, options = {}
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
- :symmetry => :general}.merge(options)
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
- protected
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
- # Reader (and eventual writer) for a version 5 .mat file.
33
- class Mat5Reader < MatReader
34
- attr_reader :file_header, :first_tag_field, :first_data_field
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
- #else
52
- # raise ArgumentError, "Need a content string or a number of bytes; content_or_bytes is #{content_or_bytes.class.to_s}."
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
- def write_packed(packedio, options)
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
- :cells, :logical, :global, :complex, :nonzero_max,
94
- :matlab_class, :dimensions, :matlab_name, :real_part,
95
- :imaginary_part, :row_index, :column_index)
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
- # Figure out the appropriate Ruby type to convert to, and do it. There are basically two possible types: NMatrix
106
- # and Ruby Array. This function is recursive, so an Array is going to contain other Arrays and/or NMatrix objects.
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 appropriate stype (:yale or :dense, respectively).
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 converted to the correct NMatrix signed dtypes.
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 repacked_data.
135
- def unpacked_data real_mdtype=nil, imag_mdtype=nil
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 repack, just returns directly.
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 components must be merged together (MATLAB
161
- # stores them separately for some crazy reason).
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 io.cpp.
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 (mdtypes). They could be different for real and
171
- # imaginary, so call upcast to figure out what to use.
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 repack, just returns directly.
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 matrix in MATLAB will be a dense NMatrix, and
229
- # a sparse (old Yale) matrix in MATLAB will be a :yale (new Yale) matrix in NMatrix.
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
- # Note that NMatrix has no old Yale type, so this uses a semi-hidden version of the NMatrix constructor to pass in
232
- # -- as directly as possible -- the stored bytes in a MATLAB sparse matrix. This constructor should also be used
233
- # for other IO formats that want to create sparse matrices from IA and JA vectors (e.g., SciPy).
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
- def ignore_padding packedio, bytes
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
- MDTYPE_UNPACK_ARGS =
323
- MatReader::MDTYPE_UNPACK_ARGS.merge({
324
- :miCOMPRESSED => [Compressed, {}],
325
- :miMATRIX => [MatrixData, {}]
326
- })
327
- # include TaggedDataEnumerable
328
-
329
- FIRST_TAG_FIELD_POS = 128
330
-
331
- ####################
332
- # Instance Methods #
333
- ####################
334
-
335
- def initialize(stream, options = {})
336
- super(stream, options)
337
- @file_header = seek_and_read_file_header
338
- end
339
-
340
- def to_a
341
- returning(Array.new) do |ary|
342
- self.each { |el| ary << el }
343
- end
344
- end
345
-
346
- def to_ruby
347
- ary = self.to_a
348
-
349
- if ary.size == 1
350
- ary.first.to_ruby
351
- else
352
- ary.collect { |item| item.to_ruby }
353
- end
354
- end
355
-
356
- def guess_byte_order
357
- stream.seek(Header::BYTE_ORDER_POS)
358
- mi = stream.read(Header::BYTE_ORDER_LENGTH)
359
- stream.seek(0)
360
- mi == 'IM' ? :little : :big
361
- end
362
-
363
- def seek_and_read_file_header
364
- stream.seek(0)
365
- stream.read(FIRST_TAG_FIELD_POS).unpack(Header, {:endian => byte_order})
366
- end
367
-
368
- def each(&block)
369
- stream.each(Element, {:endian => byte_order}) do |element|
370
- if element.data.is_a?(Compressed)
371
- StringIO.new(element.data.content, 'rb').each(Element, {:endian => byte_order}) do |compressed_element|
372
- yield compressed_element.data
373
- end
374
-
375
- else
376
- yield element.data
377
- end
378
- end
379
-
380
- # Go back to the beginning in case we want to do it again.
381
- stream.seek(FIRST_TAG_FIELD_POS)
382
-
383
- self
384
- end
385
-
386
- ####################
387
- # Internal Classes #
388
- ####################
389
-
390
- class Header < Struct.new(:desc, :data_offset, :version, :endian)
391
-
392
- include Packable
393
-
394
- BYTE_ORDER_LENGTH = 2
395
- DESC_LENGTH = 116
396
- DATA_OFFSET_LENGTH = 8
397
- VERSION_LENGTH = 2
398
- BYTE_ORDER_POS = 126
399
-
400
- ## TODO: TEST WRITE.
401
- def write_packed(packedio, options)
402
- packedio << [desc, {:bytes => DESC_LENGTH }] <<
403
- [data_offset, {:bytes => DATA_OFFSET_LENGTH }] <<
404
- [version, {:bytes => VERSION_LENGTH }] <<
405
- [byte_order, {:bytes => BYTE_ORDER_LENGTH }]
406
- end
407
-
408
- def read_packed(packedio, options)
409
- self.desc, self.data_offset, self.version, self.endian = packedio >>
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
- [String, {:bytes => DATA_OFFSET_LENGTH }] >>
412
- [Integer, {:bytes => VERSION_LENGTH, :endian => options[:endian] }] >>
413
- [String, {:bytes => 2 }]
414
-
415
- self.desc.strip!
416
- self.data_offset.strip!
417
- self.data_offset = nil if self.data_offset.empty?
418
-
419
- self.endian == 'IM' ? :little : :big
420
- end
421
- end
422
-
423
- class Tag < Struct.new(:data_type, :raw_data_type, :bytes, :small)
424
- include Packable
425
-
426
- DATA_TYPE_OPTS = BYTES_OPTS = {:bytes => 4, :signed => false}
427
- LENGTH = DATA_TYPE_OPTS[:bytes] + BYTES_OPTS[:bytes]
428
-
429
- ## TODO: TEST WRITE.
430
- def write_packed packedio, options
431
- packedio << [data_type, DATA_TYPE_OPTS] << [bytes, BYTES_OPTS]
432
- end
433
-
434
- def small?
435
- self.bytes > 0 and self.bytes <= 4
436
- end
437
-
438
- def size
439
- small? ? 4 : 8
440
- end
441
-
442
- def read_packed packedio, options
443
- self.raw_data_type = packedio.read([Integer, DATA_TYPE_OPTS.merge(options)])
444
-
445
- # Borrowed from a SciPy patch
446
- upper = self.raw_data_type >> 16
447
- lower = self.raw_data_type & 0xFFFF
448
-
449
- if upper > 0
450
- # Small data element format
451
- raise IOError, 'Small data element format indicated, but length is more than 4 bytes!' if upper > 4
452
-
453
- self.bytes = upper
454
- self.raw_data_type = lower
455
-
456
- else
457
- self.bytes = packedio.read([Integer, BYTES_OPTS.merge(options)])
458
- end
459
-
460
- self.data_type = MatReader::MDTYPES[self.raw_data_type]
461
- end
462
-
463
- def inspect
464
- "#<#{self.class.to_s} data_type=#{data_type}[#{raw_data_type}][#{raw_data_type.to_s(2)}] bytes=#{bytes} size=#{size}#{small? ? ' small' : ''}>"
465
- end
466
- end
467
-
468
-
469
- class ElementDataIOError < IOError
470
- attr_reader :tag
471
-
472
- def initialize(tag = nil, msg = nil)
473
- @tag = tag
474
- super msg
475
- end
476
-
477
- def to_s
478
- @tag.inspect + "\n" + super
479
- end
480
- end
481
-
482
-
483
- class Element < Struct.new(:tag, :data)
484
- include Packable
485
-
486
- def write_packed packedio, options
487
- packedio << [tag, {}] << [data, {}]
488
- end
489
-
490
- def read_packed(packedio, options)
491
- raise(ArgumentError, 'Missing mandatory option :endian.') unless options.has_key?(:endian)
492
-
493
- tag = packedio.read([Tag, {:endian => options[:endian]}])
494
- #STDERR.puts tag.inspect
495
- data_type = MDTYPE_UNPACK_ARGS[tag.data_type]
496
-
497
- self.tag = tag
498
- #STDERR.puts self.tag.inspect
499
-
500
- raise ElementDataIOError.new(tag, "Unrecognized Matlab type #{tag.raw_data_type}") if data_type.nil?
501
-
502
- if tag.bytes == 0
503
- self.data = []
504
-
505
- else
506
- number_of_reads = data_type[1].has_key?(:bytes) ? tag.bytes / data_type[1][:bytes] : 1
507
- data_type[1].merge!({:endian => options[:endian]})
508
-
509
- if number_of_reads == 1
510
- self.data = packedio.read(data_type)
511
-
512
- else
513
- self.data =
514
- returning(Array.new) do |ary|
515
- number_of_reads.times { ary << packedio.read(data_type) }
516
- end
517
- end
518
-
519
- begin
520
- ignore_padding(packedio, (tag.bytes + tag.size) % 8) unless [:miMATRIX, :miCOMPRESSED].include?(tag.data_type)
521
-
522
- rescue EOFError
523
- STDERR.puts self.tag.inspect
524
- raise(ElementDataIOError.new(tag, "Ignored too much"))
525
- end
526
- end
527
- end
528
-
529
- def ignore_padding(packedio, bytes)
530
- if bytes > 0
531
- #STDERR.puts "Ignored #{8 - bytes} on #{self.tag.data_type}"
532
- ignored = packedio.read(8 - bytes)
533
- ignored_unpacked = ignored.unpack("C*")
534
- raise(IOError, "Nonzero padding detected: #{ignored_unpacked}") if ignored_unpacked.any? { |i| i != 0 }
535
- end
536
- end
537
-
538
- def to_ruby
539
- data.to_ruby
540
- end
541
- end
542
-
543
- # Doesn't unpack the contents of the element, e.g., if we want to handle
544
- # manually, or pass the raw string of bytes into NMatrix.
545
- class RawElement < Element
546
- def read_packed(packedio, options)
547
- raise(ArgumentError, 'Missing mandatory option :endian.') unless options.has_key?(:endian)
548
-
549
- self.tag = packedio.read([Tag, {:endian => options[:endian] }])
550
- self.data = packedio.read([String, {:endian => options[:endian], :bytes => tag.bytes }])
551
-
552
- begin
553
- ignore_padding(packedio, (tag.bytes + tag.size) % 8) unless [:miMATRIX, :miCOMPRESSED].include?(tag.data_type)
554
-
555
- rescue EOFError
556
- STDERR.puts self.tag.inspect
557
- raise ElementDataIOError.new(tag, 'Ignored too much.')
558
- end
559
- end
560
- end
561
-
562
- #####################
563
- # End of Mat5Reader #
564
- #####################
565
-
566
- end
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