nmatrix 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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