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
ov-range.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 <iostream>
28 
29 #include "lo-ieee.h"
30 #include "lo-utils.h"
31 
32 #include "defun.h"
33 #include "variables.h"
34 #include "gripes.h"
35 #include "mxarray.h"
36 #include "ops.h"
37 #include "oct-obj.h"
38 #include "ov-range.h"
39 #include "ov-re-mat.h"
40 #include "ov-scalar.h"
41 #include "pr-output.h"
42 
43 #include "byte-swap.h"
44 #include "ls-ascii-helper.h"
45 #include "ls-hdf5.h"
46 #include "ls-utils.h"
47 
48 // If TRUE, allow ranges with non-integer elements as array indices.
50 
52 
54 
55 static octave_base_value *
57 {
58  CAST_CONV_ARG (const octave_range&);
59 
60  return new octave_matrix (v.matrix_value ());
61 }
62 
65 {
68 }
69 
72 {
73  octave_base_value *retval = 0;
74 
75  switch (range.nelem ())
76  {
77  case 1:
78  retval = new octave_scalar (range.base ());
79  break;
80 
81  case 0:
82  retval = new octave_matrix (Matrix (1, 0));
83  break;
84 
85  case -2:
86  retval = new octave_matrix (range.matrix_value ());
87  break;
88 
89  default:
90  break;
91  }
92 
93  return retval;
94 }
95 
97 octave_range::subsref (const std::string& type,
98  const std::list<octave_value_list>& idx)
99 {
100  octave_value retval;
101 
102  switch (type[0])
103  {
104  case '(':
105  retval = do_index_op (idx.front ());
106  break;
107 
108  case '{':
109  case '.':
110  {
111  std::string nm = type_name ();
112  error ("%s cannot be indexed with %c", nm.c_str (), type[0]);
113  }
114  break;
115 
116  default:
117  panic_impossible ();
118  }
119 
120  return retval.next_subsref (type, idx);
121 }
122 
124 octave_range::do_index_op (const octave_value_list& idx, bool resize_ok)
125 {
126  if (idx.length () == 1 && ! resize_ok)
127  {
128  octave_value retval;
129 
130  // The range can handle a single subscript.
131  idx_vector i = idx(0).index_vector ();
132  if (! error_state)
133  {
134  if (i.is_scalar () && i(0) < range.nelem ())
135  retval = range.elem (i(0));
136  else
137  retval = range.index (i);
138  }
139 
140  return retval;
141  }
142  else
143  {
145 
146  return tmp.do_index_op (idx, resize_ok);
147  }
148 }
149 
152 {
153  if (idx_cache)
154  return *idx_cache;
155  else
156  {
159  return set_idx_cache (idx_vector (range));
160  else
161  {
162  warning_with_id ("Octave:noninteger-range-as-index",
163  "non-integer range used as index");
164 
165  return octave_value (matrix_value ()).round ().index_vector ();
166  }
167  }
168 }
169 
170 double
172 {
173  double retval = lo_ieee_nan_value ();
174 
175  octave_idx_type nel = range.nelem ();
176 
177  if (nel > 0)
178  {
179  gripe_implicit_conversion ("Octave:array-to-scalar",
180  "range", "real scalar");
181 
182  retval = range.base ();
183  }
184  else
185  gripe_invalid_conversion ("range", "real scalar");
186 
187  return retval;
188 }
189 
190 float
192 {
193  float retval = lo_ieee_float_nan_value ();
194 
195  octave_idx_type nel = range.nelem ();
196 
197  if (nel > 0)
198  {
199  gripe_implicit_conversion ("Octave:array-to-scalar",
200  "range", "real scalar");
201 
202  retval = range.base ();
203  }
204  else
205  gripe_invalid_conversion ("range", "real scalar");
206 
207  return retval;
208 }
209 
212 {
213  const Matrix matrix = range.matrix_value ();
214  charNDArray retval (dims ());
215 
216  octave_idx_type nel = numel ();
217 
218  for (octave_idx_type i = 0; i < nel; i++)
219  retval.elem (i) = static_cast<char>(matrix.elem (i));
220 
221  return retval;
222 }
223 
225 octave_range::all (int dim) const
226 {
227  // FIXME: this is a potential waste of memory.
228 
229  Matrix m = range.matrix_value ();
230 
231  return m.all (dim);
232 }
233 
235 octave_range::any (int dim) const
236 {
237  // FIXME: this is a potential waste of memory.
238 
239  Matrix m = range.matrix_value ();
240 
241  return m.any (dim);
242 }
243 
246 {
247  return
248  (k == 0
250  : octave_value (range.diag (k)));
251 }
252 
255 {
256  Matrix mat = range.matrix_value ();
257 
258  return mat.diag (m, n);
259 }
260 
261 bool
263 {
264  bool retval = false;
265 
266  if (range.nelem () != 0)
267  {
268  // FIXME: this is a potential waste of memory.
269 
270  Matrix m ((range.matrix_value () . all ()) . all ());
271 
272  retval = (m.rows () == 1 && m.columns () == 1 && m (0, 0) != 0.0);
273  }
274 
275  return retval;
276 }
277 
278 Complex
280 {
281  double tmp = lo_ieee_nan_value ();
282 
283  Complex retval (tmp, tmp);
284 
285  octave_idx_type nel = range.nelem ();
286 
287  if (nel > 0)
288  {
289  gripe_implicit_conversion ("Octave:array-to-scalar",
290  "range", "complex scalar");
291 
292  retval = range.base ();
293  }
294  else
295  gripe_invalid_conversion ("range", "complex scalar");
296 
297  return retval;
298 }
299 
302 {
303  float tmp = lo_ieee_float_nan_value ();
304 
305  FloatComplex retval (tmp, tmp);
306 
307  octave_idx_type nel = range.nelem ();
308 
309  if (nel > 0)
310  {
311  gripe_implicit_conversion ("Octave:array-to-scalar",
312  "range", "complex scalar");
313 
314  retval = range.base ();
315  }
316  else
317  gripe_invalid_conversion ("range", "complex scalar");
318 
319  return retval;
320 }
321 
324 {
325  Matrix m = range.matrix_value ();
326 
327  if (m.any_element_is_nan ())
329  else if (warn && m.any_element_not_one_or_zero ())
331 
332  return boolNDArray (m);
333 }
334 
336 octave_range::resize (const dim_vector& dv, bool fill) const
337 {
338  NDArray retval = array_value ();
339  if (fill)
340  retval.resize (dv, 0);
341  else
342  retval.resize (dv);
343  return retval;
344 }
345 
347 octave_range::convert_to_str_internal (bool pad, bool force, char type) const
348 {
350  return tmp.convert_to_str (pad, force, type);
351 }
352 
353 void
354 octave_range::print (std::ostream& os, bool pr_as_read_syntax) const
355 {
356  print_raw (os, pr_as_read_syntax);
357  newline (os);
358 }
359 
360 void
361 octave_range::print_raw (std::ostream& os, bool pr_as_read_syntax) const
362 {
363  octave_print_internal (os, range, pr_as_read_syntax,
365 }
366 
367 bool
368 octave_range::print_name_tag (std::ostream& os, const std::string& name) const
369 {
370  bool retval = false;
371 
372  octave_idx_type n = range.nelem ();
373 
374  indent (os);
375 
376  if (n == 0 || n == 1)
377  os << name << " = ";
378  else
379  {
380  os << name << " =";
381  newline (os);
382  if (! Vcompact_format)
383  newline (os);
384 
385  retval = true;
386  }
387 
388  return retval;
389 }
390 
391 void
392 octave_range::short_disp (std::ostream& os) const
393 {
394  octave_idx_type len = range.nelem ();
395 
396  if (len == 0)
397  os << "[]";
398  else
399  {
400  os << range.base () << ":";
401 
402  if (len > 1)
403  {
404  if (range.inc () != 1)
405  os << range.inc () << ":";
406 
407  os << range.limit ();
408  }
409  }
410 }
411 
412 // Skip white space and comments on stream IS.
413 
414 static void
415 skip_comments (std::istream& is)
416 {
417  char c = '\0';
418  while (is.get (c))
419  {
420  if (c == ' ' || c == '\t' || c == '\n')
421  ; // Skip whitespace on way to beginning of next line.
422  else
423  break;
424  }
425 
426  skip_until_newline (is, false);
427 }
428 
429 bool
430 octave_range::save_ascii (std::ostream& os)
431 {
432  Range r = range_value ();
433  double base = r.base ();
434  double limit = r.limit ();
435  double inc = r.inc ();
436  octave_idx_type len = r.nelem ();
437 
438  if (inc != 0)
439  os << "# base, limit, increment\n";
440  else
441  os << "# base, length, increment\n";
442 
443  octave_write_double (os, base);
444  os << " ";
445  if (inc != 0)
446  octave_write_double (os, limit);
447  else
448  os << len;
449  os << " ";
450  octave_write_double (os, inc);
451  os << "\n";
452 
453  return true;
454 }
455 
456 bool
457 octave_range::load_ascii (std::istream& is)
458 {
459  // # base, limit, range comment added by save ().
460  skip_comments (is);
461 
462  double base, limit, inc;
463  is >> base >> limit >> inc;
464 
465  if (!is)
466  {
467  error ("load: failed to load range constant");
468  return false;
469  }
470 
471  if (inc != 0)
472  range = Range (base, limit, inc);
473  else
474  range = Range (base, inc, static_cast<octave_idx_type> (limit));
475 
476  return true;
477 }
478 
479 bool
480 octave_range::save_binary (std::ostream& os, bool& /* save_as_floats */)
481 {
482  char tmp = LS_DOUBLE;
483  os.write (reinterpret_cast<char *> (&tmp), 1);
484  Range r = range_value ();
485  double bas = r.base ();
486  double lim = r.limit ();
487  double inc = r.inc ();
488  if (inc == 0)
489  lim = r.nelem ();
490 
491  os.write (reinterpret_cast<char *> (&bas), 8);
492  os.write (reinterpret_cast<char *> (&lim), 8);
493  os.write (reinterpret_cast<char *> (&inc), 8);
494 
495  return true;
496 }
497 
498 bool
499 octave_range::load_binary (std::istream& is, bool swap,
500  oct_mach_info::float_format /* fmt */)
501 {
502  char tmp;
503  if (! is.read (reinterpret_cast<char *> (&tmp), 1))
504  return false;
505  double bas, lim, inc;
506  if (! is.read (reinterpret_cast<char *> (&bas), 8))
507  return false;
508  if (swap)
509  swap_bytes<8> (&bas);
510  if (! is.read (reinterpret_cast<char *> (&lim), 8))
511  return false;
512  if (swap)
513  swap_bytes<8> (&lim);
514  if (! is.read (reinterpret_cast<char *> (&inc), 8))
515  return false;
516  if (swap)
517  swap_bytes<8> (&inc);
518  if (inc != 0)
519  range = Range (bas, lim, inc);
520  else
521  range = Range (bas, inc, static_cast<octave_idx_type> (lim));
522 
523  return true;
524 }
525 
526 #if defined (HAVE_HDF5)
527 
528 // The following subroutines creates an HDF5 representation of the way
529 // we will store Octave range types (triplets of floating-point numbers).
530 // NUM_TYPE is the HDF5 numeric type to use for storage (e.g.
531 // H5T_NATIVE_DOUBLE to save as 'double'). Note that any necessary
532 // conversions are handled automatically by HDF5.
533 
534 static hid_t
535 hdf5_make_range_type (hid_t num_type)
536 {
537  hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 3);
538 
539  H5Tinsert (type_id, "base", 0 * sizeof (double), num_type);
540  H5Tinsert (type_id, "limit", 1 * sizeof (double), num_type);
541  H5Tinsert (type_id, "increment", 2 * sizeof (double), num_type);
542 
543  return type_id;
544 }
545 
546 bool
547 octave_range::save_hdf5 (hid_t loc_id, const char *name,
548  bool /* save_as_floats */)
549 {
550  hsize_t dimens[3];
551  hid_t space_hid = -1, type_hid = -1, data_hid = -1;
552  bool retval = true;
553 
554  space_hid = H5Screate_simple (0, dimens, 0);
555  if (space_hid < 0) return false;
556 
557  type_hid = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
558  if (type_hid < 0)
559  {
560  H5Sclose (space_hid);
561  return false;
562  }
563 #if HAVE_HDF5_18
564  data_hid = H5Dcreate (loc_id, name, type_hid, space_hid,
565  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
566 #else
567  data_hid = H5Dcreate (loc_id, name, type_hid, space_hid, H5P_DEFAULT);
568 #endif
569  if (data_hid < 0)
570  {
571  H5Sclose (space_hid);
572  H5Tclose (type_hid);
573  return false;
574  }
575 
576  Range r = range_value ();
577  double range_vals[3];
578  range_vals[0] = r.base ();
579  range_vals[1] = r.inc () != 0 ? r.limit () : r.nelem ();
580  range_vals[2] = r.inc ();
581 
582  if (H5Dwrite (data_hid, type_hid, H5S_ALL, H5S_ALL, H5P_DEFAULT,
583  range_vals) >= 0)
584  {
585  octave_idx_type nel = r.nelem ();
586  retval = hdf5_add_scalar_attr (data_hid, H5T_NATIVE_IDX,
587  "OCTAVE_RANGE_NELEM", &nel) >= 0;
588  }
589  else
590  retval = false;
591 
592  H5Dclose (data_hid);
593  H5Tclose (type_hid);
594  H5Sclose (space_hid);
595 
596  return retval;
597 }
598 
599 bool
600 octave_range::load_hdf5 (hid_t loc_id, const char *name)
601 {
602  bool retval = false;
603 
604 #if HAVE_HDF5_18
605  hid_t data_hid = H5Dopen (loc_id, name, H5P_DEFAULT);
606 #else
607  hid_t data_hid = H5Dopen (loc_id, name);
608 #endif
609  hid_t type_hid = H5Dget_type (data_hid);
610 
611  hid_t range_type = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
612 
613  if (! hdf5_types_compatible (type_hid, range_type))
614  {
615  H5Tclose (range_type);
616  H5Dclose (data_hid);
617  return false;
618  }
619 
620  hid_t space_hid = H5Dget_space (data_hid);
621  hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
622 
623  if (rank != 0)
624  {
625  H5Tclose (range_type);
626  H5Sclose (space_hid);
627  H5Dclose (data_hid);
628  return false;
629  }
630 
631  double rangevals[3];
632  if (H5Dread (data_hid, range_type, H5S_ALL, H5S_ALL, H5P_DEFAULT,
633  rangevals) >= 0)
634  {
635  retval = true;
636  octave_idx_type nel;
637  if (hdf5_get_scalar_attr (data_hid, H5T_NATIVE_IDX,
638  "OCTAVE_RANGE_NELEM", &nel))
639  range = Range (rangevals[0], rangevals[2], nel);
640  else
641  {
642  if (rangevals[2] != 0)
643  range = Range (rangevals[0], rangevals[1], rangevals[2]);
644  else
645  range = Range (rangevals[0], rangevals[2],
646  static_cast<octave_idx_type> (rangevals[1]));
647  }
648  }
649 
650  H5Tclose (range_type);
651  H5Sclose (space_hid);
652  H5Dclose (data_hid);
653 
654  return retval;
655 }
656 
657 #endif
658 
659 mxArray *
661 {
662  mxArray *retval = new mxArray (mxDOUBLE_CLASS, dims (), mxREAL);
663 
664  double *pr = static_cast<double *> (retval->get_data ());
665 
666  mwSize nel = numel ();
667 
668  Matrix m = matrix_value ();
669 
670  const double *p = m.data ();
671 
672  for (mwSize i = 0; i < nel; i++)
673  pr[i] = p[i];
674 
675  return retval;
676 }
677 
678 DEFUN (allow_noninteger_range_as_index, args, nargout,
679  "-*- texinfo -*-\n\
680 @deftypefn {Built-in Function} {@var{val} =} allow_noninteger_range_as_index ()\n\
681 @deftypefnx {Built-in Function} {@var{old_val} =} allow_noninteger_range_as_index (@var{new_val})\n\
682 @deftypefnx {Built-in Function} {} allow_noninteger_range_as_index (@var{new_val}, \"local\")\n\
683 Query or set the internal variable that controls whether non-integer\n\
684 ranges are allowed as indices. This might be useful for @sc{matlab}\n\
685 compatibility; however, it is still not entirely compatible because\n\
686 @sc{matlab} treats the range expression differently in different contexts.\n\
687 \n\
688 When called from inside a function with the @qcode{\"local\"} option, the\n\
689 variable is changed locally for the function and any subroutines it calls. \n\
690 The original variable value is restored when exiting the function.\n\
691 @end deftypefn")
692 {
693  return SET_INTERNAL_VARIABLE (allow_noninteger_range_as_index);
694 }
695 
696 /*
697 %!test
698 %! x = 0:10;
699 %! save = allow_noninteger_range_as_index ();
700 %! warn_state = warning ("query", "Octave:noninteger-range-as-index");
701 %! unwind_protect
702 %! allow_noninteger_range_as_index (false);
703 %! fail ("x(2.1:5)");
704 %! assert (x(2:5), 1:4);
705 %! allow_noninteger_range_as_index (true);
706 %! warning ("off", "Octave:noninteger-range-as-index");
707 %! assert (x(2.49:5), 1:3);
708 %! assert (x(2.5:5), 2:4);
709 %! assert (x(2.51:5), 2:4);
710 %! unwind_protect_cleanup
711 %! allow_noninteger_range_as_index (save);
712 %! warning (warn_state.state, warn_state.identifier);
713 %! end_unwind_protect
714 */