GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
ls-mat4.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 1996-2013 John W. Eaton
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <cfloat>
28 #include <cstring>
29 #include <cctype>
30 
31 #include <fstream>
32 #include <iomanip>
33 #include <iostream>
34 #include <string>
35 #include <vector>
36 
37 #include "byte-swap.h"
38 #include "data-conv.h"
39 #include "file-ops.h"
40 #include "glob-match.h"
41 #include "lo-mappers.h"
42 #include "mach-info.h"
43 #include "oct-env.h"
44 #include "oct-time.h"
45 #include "quit.h"
46 #include "str-vec.h"
47 #include "oct-locbuf.h"
48 
49 #include "Cell.h"
50 #include "defun.h"
51 #include "error.h"
52 #include "gripes.h"
53 #include "load-save.h"
54 #include "oct-obj.h"
55 #include "oct-map.h"
56 #include "ov-cell.h"
57 #include "pager.h"
58 #include "pt-exp.h"
59 #include "sysdep.h"
60 #include "unwind-prot.h"
61 #include "utils.h"
62 #include "variables.h"
63 #include "version.h"
64 #include "dMatrix.h"
65 #include "dSparse.h"
66 
67 #include "ls-mat4.h"
68 
69 // Read LEN elements of data from IS in the format specified by
70 // PRECISION, placing the result in DATA. If SWAP is TRUE, swap
71 // the bytes of each element before copying to DATA. FLT_FMT
72 // specifies the format of the data if we are reading floating point
73 // numbers.
74 
75 static void
76 read_mat_binary_data (std::istream& is, double *data, int precision,
77  int len, bool swap,
79 {
80  switch (precision)
81  {
82  case 0:
83  read_doubles (is, data, LS_DOUBLE, len, swap, flt_fmt);
84  break;
85 
86  case 1:
87  read_doubles (is, data, LS_FLOAT, len, swap, flt_fmt);
88  break;
89 
90  case 2:
91  read_doubles (is, data, LS_INT, len, swap, flt_fmt);
92  break;
93 
94  case 3:
95  read_doubles (is, data, LS_SHORT, len, swap, flt_fmt);
96  break;
97 
98  case 4:
99  read_doubles (is, data, LS_U_SHORT, len, swap, flt_fmt);
100  break;
101 
102  case 5:
103  read_doubles (is, data, LS_U_CHAR, len, swap, flt_fmt);
104  break;
105 
106  default:
107  break;
108  }
109 }
110 
111 int
112 read_mat_file_header (std::istream& is, bool& swap, int32_t& mopt,
113  int32_t& nr, int32_t& nc,
114  int32_t& imag, int32_t& len,
115  int quiet)
116 {
117  swap = false;
118 
119  // We expect to fail here, at the beginning of a record, so not
120  // being able to read another mopt value should not result in an
121  // error.
122 
123  is.read (reinterpret_cast<char *> (&mopt), 4);
124  if (! is)
125  return 1;
126 
127  if (! is.read (reinterpret_cast<char *> (&nr), 4))
128  goto data_read_error;
129 
130  if (! is.read (reinterpret_cast<char *> (&nc), 4))
131  goto data_read_error;
132 
133  if (! is.read (reinterpret_cast<char *> (&imag), 4))
134  goto data_read_error;
135 
136  if (! is.read (reinterpret_cast<char *> (&len), 4))
137  goto data_read_error;
138 
139 // If mopt is nonzero and the byte order is swapped, mopt will be
140 // bigger than we expect, so we swap bytes.
141 //
142 // If mopt is zero, it means the file was written on a little endian
143 // machine, and we only need to swap if we are running on a big endian
144 // machine.
145 //
146 // Gag me.
147 
148  if (oct_mach_info::words_big_endian () && mopt == 0)
149  swap = true;
150 
151  // mopt is signed, therefore byte swap may result in negative value.
152 
153  if (mopt > 9999 || mopt < 0)
154  swap = true;
155 
156  if (swap)
157  {
158  swap_bytes<4> (&mopt);
159  swap_bytes<4> (&nr);
160  swap_bytes<4> (&nc);
161  swap_bytes<4> (&imag);
162  swap_bytes<4> (&len);
163  }
164 
165  if (mopt > 9999 || mopt < 0 || imag > 1 || imag < 0)
166  {
167  if (! quiet)
168  error ("load: can't read binary file");
169  return -1;
170  }
171 
172  return 0;
173 
174 data_read_error:
175  return -1;
176 }
177 
178 // We don't just use a cast here, because we need to be able to detect
179 // possible errors.
180 
183 {
185 
186  switch (mach)
187  {
188  case 0:
190  break;
191 
192  case 1:
194  break;
195 
196  case 2:
197  case 3:
198  case 4:
199  default:
201  break;
202  }
203 
204  return flt_fmt;
205 }
206 
207 int
209 {
210  int retval = -1;
211 
212  switch (flt_fmt)
213  {
215  retval = 0;
216  break;
217 
219  retval = 1;
220  break;
221 
222  default:
223  break;
224  }
225 
226  return retval;
227 }
228 
229 // Extract one value (scalar, matrix, string, etc.) from stream IS and
230 // place it in TC, returning the name of the variable.
231 //
232 // The data is expected to be in Matlab version 4 .mat format, though
233 // not all the features of that format are supported.
234 //
235 // FILENAME is used for error messages.
236 //
237 // This format provides no way to tag the data as global.
238 
239 std::string
240 read_mat_binary_data (std::istream& is, const std::string& filename,
241  octave_value& tc)
242 {
243  std::string retval;
244 
245  // These are initialized here instead of closer to where they are
246  // first used to avoid errors from gcc about goto crossing
247  // initialization of variable.
248 
249  Matrix re;
251  bool swap = false;
252  int type = 0;
253  int prec = 0;
254  int order = 0;
255  int mach = 0;
256  int dlen = 0;
257 
258  int32_t mopt, nr, nc, imag, len;
259 
260  int err = read_mat_file_header (is, swap, mopt, nr, nc, imag, len);
261  if (err)
262  {
263  if (err < 0)
264  goto data_read_error;
265  else
266  return retval;
267  }
268 
269  type = mopt % 10; // Full, sparse, etc.
270  mopt /= 10; // Eliminate first digit.
271  prec = mopt % 10; // double, float, int, etc.
272  mopt /= 10; // Eliminate second digit.
273  order = mopt % 10; // Row or column major ordering.
274  mopt /= 10; // Eliminate third digit.
275  mach = mopt % 10; // IEEE, VAX, etc.
276 
277  flt_fmt = mopt_digit_to_float_format (mach);
278 
279  if (flt_fmt == oct_mach_info::flt_fmt_unknown)
280  {
281  error ("load: unrecognized binary format!");
282  return retval;
283  }
284 
285  if (imag && type == 1)
286  {
287  error ("load: encountered complex matrix with string flag set!");
288  return retval;
289  }
290 
291  // LEN includes the terminating character, and the file is also
292  // supposed to include it, but apparently not all files do. Either
293  // way, I think this should work.
294 
295  {
296  OCTAVE_LOCAL_BUFFER (char, name, len+1);
297  name[len] = '\0';
298  if (! is.read (name, len))
299  goto data_read_error;
300  retval = name;
301 
302  dlen = nr * nc;
303  if (dlen < 0)
304  goto data_read_error;
305 
306  if (order)
307  {
308  octave_idx_type tmp = nr;
309  nr = nc;
310  nc = tmp;
311  }
312 
313  if (type == 2)
314  {
315  if (nc == 4)
316  {
317  octave_idx_type nr_new, nc_new;
318  Array<Complex> data (dim_vector (1, nr - 1));
319  Array<octave_idx_type> c (dim_vector (1, nr - 1));
320  Array<octave_idx_type> r (dim_vector (1, nr - 1));
321  OCTAVE_LOCAL_BUFFER (double, dtmp, nr);
322  OCTAVE_LOCAL_BUFFER (double, ctmp, nr);
323 
324  read_mat_binary_data (is, dtmp, prec, nr, swap, flt_fmt);
325  for (octave_idx_type i = 0; i < nr - 1; i++)
326  r.xelem (i) = dtmp[i] - 1;
327  nr_new = dtmp[nr - 1];
328  read_mat_binary_data (is, dtmp, prec, nr, swap, flt_fmt);
329  for (octave_idx_type i = 0; i < nr - 1; i++)
330  c.xelem (i) = dtmp[i] - 1;
331  nc_new = dtmp[nr - 1];
332  read_mat_binary_data (is, dtmp, prec, nr - 1, swap, flt_fmt);
333  read_mat_binary_data (is, ctmp, prec, 1, swap, flt_fmt);
334  read_mat_binary_data (is, ctmp, prec, nr - 1, swap, flt_fmt);
335 
336  for (octave_idx_type i = 0; i < nr - 1; i++)
337  data.xelem (i) = Complex (dtmp[i], ctmp[i]);
338  read_mat_binary_data (is, ctmp, prec, 1, swap, flt_fmt);
339 
340  SparseComplexMatrix smc = SparseComplexMatrix (data, r, c,
341  nr_new, nc_new);
342 
343  tc = order ? smc.transpose () : smc;
344  }
345  else
346  {
347  octave_idx_type nr_new, nc_new;
348  Array<double> data (dim_vector (1, nr - 1));
349  Array<octave_idx_type> c (dim_vector (1, nr - 1));
350  Array<octave_idx_type> r (dim_vector (1, nr - 1));
351  OCTAVE_LOCAL_BUFFER (double, dtmp, nr);
352 
353  read_mat_binary_data (is, dtmp, prec, nr, swap, flt_fmt);
354  for (octave_idx_type i = 0; i < nr - 1; i++)
355  r.xelem (i) = dtmp[i] - 1;
356  nr_new = dtmp[nr - 1];
357  read_mat_binary_data (is, dtmp, prec, nr, swap, flt_fmt);
358  for (octave_idx_type i = 0; i < nr - 1; i++)
359  c.xelem (i) = dtmp[i] - 1;
360  nc_new = dtmp[nr - 1];
361  read_mat_binary_data (is, data.fortran_vec (), prec, nr - 1,
362  swap, flt_fmt);
363  read_mat_binary_data (is, dtmp, prec, 1, swap, flt_fmt);
364 
365  SparseMatrix sm = SparseMatrix (data, r, c, nr_new, nc_new);
366 
367  tc = order ? sm.transpose () : sm;
368  }
369  }
370  else
371  {
372  re.resize (nr, nc);
373 
374  read_mat_binary_data (is, re.fortran_vec (), prec, dlen, swap, flt_fmt);
375 
376  if (! is || error_state)
377  {
378  error ("load: reading matrix data for '%s'", name);
379  goto data_read_error;
380  }
381 
382  if (imag)
383  {
384  Matrix im (nr, nc);
385 
386  read_mat_binary_data (is, im.fortran_vec (), prec, dlen, swap,
387  flt_fmt);
388 
389  if (! is || error_state)
390  {
391  error ("load: reading imaginary matrix data for '%s'", name);
392  goto data_read_error;
393  }
394 
395  ComplexMatrix ctmp (nr, nc);
396 
397  for (octave_idx_type j = 0; j < nc; j++)
398  for (octave_idx_type i = 0; i < nr; i++)
399  ctmp (i, j) = Complex (re (i, j), im (i, j));
400 
401  tc = order ? ctmp.transpose () : ctmp;
402  }
403  else
404  tc = order ? re.transpose () : re;
405 
406  if (type == 1)
407  tc = tc.convert_to_str (false, true, '\'');
408  }
409 
410  return retval;
411  }
412 
413 data_read_error:
414  error ("load: trouble reading binary file '%s'", filename.c_str ());
415  return retval;
416 }
417 
418 // Save the data from TC along with the corresponding NAME on stream OS
419 // in the MatLab version 4 binary format.
420 
421 bool
422 save_mat_binary_data (std::ostream& os, const octave_value& tc,
423  const std::string& name)
424 {
425  int32_t mopt = 0;
426 
427  mopt += tc.is_sparse_type () ? 2 : tc.is_string () ? 1 : 0;
428 
431 
432  mopt += 1000 * float_format_to_mopt_digit (flt_fmt);
433 
434  os.write (reinterpret_cast<char *> (&mopt), 4);
435 
436  octave_idx_type len;
437  int32_t nr = tc.rows ();
438 
439  int32_t nc = tc.columns ();
440 
441  if (tc.is_sparse_type ())
442  {
443  len = tc.nnz ();
444  uint32_t nnz = len + 1;
445  os.write (reinterpret_cast<char *> (&nnz), 4);
446 
447  uint32_t iscmplx = tc.is_complex_type () ? 4 : 3;
448  os.write (reinterpret_cast<char *> (&iscmplx), 4);
449 
450  uint32_t tmp = 0;
451  os.write (reinterpret_cast<char *> (&tmp), 4);
452  }
453  else
454  {
455  os.write (reinterpret_cast<char *> (&nr), 4);
456  os.write (reinterpret_cast<char *> (&nc), 4);
457 
458  int32_t imag = tc.is_complex_type () ? 1 : 0;
459  os.write (reinterpret_cast<char *> (&imag), 4);
460 
461  len = nr * nc;
462  }
463 
464 
465  // LEN includes the terminating character, and the file is also
466  // supposed to include it.
467 
468  int32_t name_len = name.length () + 1;
469 
470  os.write (reinterpret_cast<char *> (&name_len), 4);
471  os << name << '\0';
472 
473  if (tc.is_string ())
474  {
475  unwind_protect frame;
476 
477  charMatrix chm = tc.char_matrix_value ();
478 
479  octave_idx_type nrow = chm.rows ();
480  octave_idx_type ncol = chm.cols ();
481 
482  OCTAVE_LOCAL_BUFFER (double, buf, ncol*nrow);
483 
484  for (octave_idx_type i = 0; i < nrow; i++)
485  {
486  std::string tstr = chm.row_as_string (i);
487  const char *s = tstr.data ();
488 
489  for (octave_idx_type j = 0; j < ncol; j++)
490  buf[j*nrow+i] = static_cast<double> (*s++ & 0x00FF);
491  }
492  os.write (reinterpret_cast<char *> (buf), nrow*ncol*sizeof (double));
493  }
494  else if (tc.is_range ())
495  {
496  Range r = tc.range_value ();
497  double base = r.base ();
498  double inc = r.inc ();
499  octave_idx_type nel = r.nelem ();
500  for (octave_idx_type i = 0; i < nel; i++)
501  {
502  double x = base + i * inc;
503  os.write (reinterpret_cast<char *> (&x), 8);
504  }
505  }
506  else if (tc.is_real_scalar ())
507  {
508  double tmp = tc.double_value ();
509  os.write (reinterpret_cast<char *> (&tmp), 8);
510  }
511  else if (tc.is_sparse_type ())
512  {
513  double ds;
514  OCTAVE_LOCAL_BUFFER (double, dtmp, len);
515  if (tc.is_complex_matrix ())
516  {
518 
519  for (octave_idx_type i = 0; i < len; i++)
520  dtmp[i] = m.ridx (i) + 1;
521  os.write (reinterpret_cast<const char *> (dtmp), 8 * len);
522  ds = nr;
523  os.write (reinterpret_cast<const char *> (&ds), 8);
524 
525  octave_idx_type ii = 0;
526  for (octave_idx_type j = 0; j < nc; j++)
527  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++)
528  dtmp[ii++] = j + 1;
529  os.write (reinterpret_cast<const char *> (dtmp), 8 * len);
530  ds = nc;
531  os.write (reinterpret_cast<const char *> (&ds), 8);
532 
533  for (octave_idx_type i = 0; i < len; i++)
534  dtmp[i] = std::real (m.data (i));
535  os.write (reinterpret_cast<const char *> (dtmp), 8 * len);
536  ds = 0.;
537  os.write (reinterpret_cast<const char *> (&ds), 8);
538 
539  for (octave_idx_type i = 0; i < len; i++)
540  dtmp[i] = std::imag (m.data (i));
541  os.write (reinterpret_cast<const char *> (dtmp), 8 * len);
542  os.write (reinterpret_cast<const char *> (&ds), 8);
543  }
544  else
545  {
547 
548  for (octave_idx_type i = 0; i < len; i++)
549  dtmp[i] = m.ridx (i) + 1;
550  os.write (reinterpret_cast<const char *> (dtmp), 8 * len);
551  ds = nr;
552  os.write (reinterpret_cast<const char *> (&ds), 8);
553 
554  octave_idx_type ii = 0;
555  for (octave_idx_type j = 0; j < nc; j++)
556  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++)
557  dtmp[ii++] = j + 1;
558  os.write (reinterpret_cast<const char *> (dtmp), 8 * len);
559  ds = nc;
560  os.write (reinterpret_cast<const char *> (&ds), 8);
561 
562  os.write (reinterpret_cast<const char *> (m.data ()), 8 * len);
563  ds = 0.;
564  os.write (reinterpret_cast<const char *> (&ds), 8);
565  }
566  }
567  else if (tc.is_real_matrix ())
568  {
569  Matrix m = tc.matrix_value ();
570  os.write (reinterpret_cast<const char *> (m.data ()), 8 * len);
571  }
572  else if (tc.is_complex_scalar ())
573  {
574  Complex tmp = tc.complex_value ();
575  os.write (reinterpret_cast<char *> (&tmp), 16);
576  }
577  else if (tc.is_complex_matrix ())
578  {
579  ComplexMatrix m_cmplx = tc.complex_matrix_value ();
580  Matrix m = ::real (m_cmplx);
581  os.write (reinterpret_cast<const char *> (m.data ()), 8 * len);
582  m = ::imag (m_cmplx);
583  os.write (reinterpret_cast<const char *> (m.data ()), 8 * len);
584  }
585  else
586  gripe_wrong_type_arg ("save", tc, false);
587 
588  return os;
589 }