1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29
30 #include <istream>
31 #include <ostream>
32 #include <sstream>
33
34 #include "dNDArray.h"
35 #include "fNDArray.h"
36 #include "int8NDArray.h"
37 #include "int16NDArray.h"
38 #include "int32NDArray.h"
39 #include "int64NDArray.h"
40 #include "uint8NDArray.h"
41 #include "uint16NDArray.h"
42 #include "uint32NDArray.h"
43 #include "uint64NDArray.h"
44
45 #include "lo-ieee.h"
46 #include "lo-utils.h"
47
48 #include "defun.h"
49 #include "variables.h"
50 #include "errwarn.h"
51 #include "mxarray.h"
52 #include "ops.h"
53 #include "ovl.h"
54 #include "oct-hdf5.h"
55 #include "ov-range.h"
56 #include "ov-re-mat.h"
57 #include "ov-scalar.h"
58 #include "pr-output.h"
59
60 #include "byte-swap.h"
61 #include "ls-ascii-helper.h"
62 #include "ls-hdf5.h"
63 #include "ls-utils.h"
64
65
66 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_range, "range", "double");
67
68 static octave_base_value *
default_numeric_conversion_function(const octave_base_value & a)69 default_numeric_conversion_function (const octave_base_value& a)
70 {
71 const octave_range& v = dynamic_cast<const octave_range&> (a);
72
73 return new octave_matrix (v.matrix_value ());
74 }
75
76 octave_base_value::type_conv_info
numeric_conversion_function(void) const77 octave_range::numeric_conversion_function (void) const
78 {
79 return octave_base_value::type_conv_info (default_numeric_conversion_function,
80 octave_matrix::static_type_id ());
81 }
82
83 octave_base_value *
try_narrowing_conversion(void)84 octave_range::try_narrowing_conversion (void)
85 {
86 octave_base_value *retval = nullptr;
87
88 switch (range.numel ())
89 {
90 case 1:
91 retval = new octave_scalar (range.base ());
92 break;
93
94 case 0:
95 retval = new octave_matrix (Matrix (1, 0));
96 break;
97
98 case -2:
99 retval = new octave_matrix (range.matrix_value ());
100 break;
101
102 default:
103 break;
104 }
105
106 return retval;
107 }
108
109 octave_value
subsref(const std::string & type,const std::list<octave_value_list> & idx)110 octave_range::subsref (const std::string& type,
111 const std::list<octave_value_list>& idx)
112 {
113 octave_value retval;
114
115 switch (type[0])
116 {
117 case '(':
118 retval = do_index_op (idx.front ());
119 break;
120
121 case '{':
122 case '.':
123 {
124 std::string nm = type_name ();
125 error ("%s cannot be indexed with %c", nm.c_str (), type[0]);
126 }
127 break;
128
129 default:
130 panic_impossible ();
131 }
132
133 return retval.next_subsref (type, idx);
134 }
135
136 octave_value
do_index_op(const octave_value_list & idx,bool resize_ok)137 octave_range::do_index_op (const octave_value_list& idx, bool resize_ok)
138 {
139 if (idx.length () == 1 && ! resize_ok)
140 {
141 octave_value retval;
142
143 // The range can handle a single subscript.
144
145 try
146 {
147 idx_vector i = idx(0).index_vector ();
148
149 if (i.is_scalar () && i(0) < range.numel ())
150 retval = range.elem (i(0));
151 else
152 retval = range.index (i);
153 }
154 catch (octave::index_exception& e)
155 {
156 // More info may be added later before displaying error.
157
158 e.set_pos_if_unset (1, 1);
159 throw;
160 }
161
162 return retval;
163 }
164 else
165 {
166 octave_value tmp (new octave_matrix (range.matrix_value ()));
167
168 return tmp.do_index_op (idx, resize_ok);
169 }
170 }
171
172 idx_vector
index_vector(bool require_integers) const173 octave_range::index_vector (bool require_integers) const
174 {
175 if (idx_cache)
176 return *idx_cache;
177 else
178 {
179 if (require_integers || range.all_elements_are_ints ())
180 return set_idx_cache (idx_vector (range));
181 else
182 {
183 warning_with_id ("Octave:noninteger-range-as-index",
184 "non-integer range used as index");
185
186 return octave_value (matrix_value ()).round ().index_vector ();
187 }
188 }
189 }
190
191 double
double_value(bool) const192 octave_range::double_value (bool) const
193 {
194 octave_idx_type nel = range.numel ();
195
196 if (nel == 0)
197 err_invalid_conversion ("range", "real scalar");
198
199 warn_implicit_conversion ("Octave:array-to-scalar",
200 "range", "real scalar");
201
202 return range.base ();
203 }
204
205 float
float_value(bool) const206 octave_range::float_value (bool) const
207 {
208 octave_idx_type nel = range.numel ();
209
210 if (nel == 0)
211 err_invalid_conversion ("range", "real scalar");
212
213 warn_implicit_conversion ("Octave:array-to-scalar",
214 "range", "real scalar");
215
216 return range.base ();
217 }
218
219 charNDArray
char_array_value(bool) const220 octave_range::char_array_value (bool) const
221 {
222 const Matrix matrix = range.matrix_value ();
223 charNDArray retval (dims ());
224
225 octave_idx_type nel = numel ();
226
227 for (octave_idx_type i = 0; i < nel; i++)
228 retval.elem (i) = static_cast<char> (matrix.elem (i));
229
230 return retval;
231 }
232
233 octave_value
all(int dim) const234 octave_range::all (int dim) const
235 {
236 // FIXME: this is a potential waste of memory.
237
238 Matrix m = range.matrix_value ();
239
240 return m.all (dim);
241 }
242
243 octave_value
any(int dim) const244 octave_range::any (int dim) const
245 {
246 // FIXME: this is a potential waste of memory.
247
248 Matrix m = range.matrix_value ();
249
250 return m.any (dim);
251 }
252
253 octave_value
diag(octave_idx_type k) const254 octave_range::diag (octave_idx_type k) const
255 {
256 return
257 (k == 0
258 ? octave_value (DiagMatrix (DiagArray2<double> (range.matrix_value ())))
259 : octave_value (range.diag (k)));
260 }
261
262 octave_value
diag(octave_idx_type m,octave_idx_type n) const263 octave_range::diag (octave_idx_type m, octave_idx_type n) const
264 {
265 Matrix mat = range.matrix_value ();
266
267 return mat.diag (m, n);
268 }
269
270 // Return true if this range has all true elements (non-zero, not NaN/NA).
271 // A range cannot have NaN/NA.
272 bool
is_true(void) const273 octave_range::is_true (void) const
274 {
275 bool retval = false;
276
277 if (! range.isempty ())
278 {
279 if (dims ().numel () > 1)
280 warn_array_as_logical (dims ());
281
282 Range r = range_value ();
283 double base = r.base ();
284 double limit = r.limit ();
285
286 // Can't be zero if we start and finish on the same size of 0
287 if (((base > 0 && limit > 0) || (base < 0 && limit < 0)) && numel () > 0)
288 retval = true;
289 else
290 {
291 /*
292 // This tells us whether one element is 0, if arithmetic is exact.
293 double steps_to_zero = base / r.inc ();
294
295 retval = (steps_to_zero != floor (steps_to_zero));
296 */
297
298 // FIXME: this is a waste of memory.
299 Matrix m ((range.matrix_value ().all ()).all ());
300
301 retval = ! m.isempty () && m(0, 0) != 0.0;
302 }
303 }
304
305 return retval;
306 }
307
308 Complex
complex_value(bool) const309 octave_range::complex_value (bool) const
310 {
311 octave_idx_type nel = range.numel ();
312
313 if (nel == 0)
314 err_invalid_conversion ("range", "complex scalar");
315
316 warn_implicit_conversion ("Octave:array-to-scalar",
317 "range", "complex scalar");
318
319 return Complex (range.base (), 0);
320 }
321
322 FloatComplex
float_complex_value(bool) const323 octave_range::float_complex_value (bool) const
324 {
325 float tmp = lo_ieee_float_nan_value ();
326
327 FloatComplex retval (tmp, tmp);
328
329 octave_idx_type nel = range.numel ();
330
331 if (nel == 0)
332 err_invalid_conversion ("range", "complex scalar");
333
334 warn_implicit_conversion ("Octave:array-to-scalar",
335 "range", "complex scalar");
336
337 retval = range.base ();
338
339 return retval;
340 }
341
342 boolNDArray
bool_array_value(bool warn) const343 octave_range::bool_array_value (bool warn) const
344 {
345 Matrix m = range.matrix_value ();
346
347 if (m.any_element_is_nan ())
348 octave::err_nan_to_logical_conversion ();
349 if (warn && m.any_element_not_one_or_zero ())
350 warn_logical_conversion ();
351
352 return boolNDArray (m);
353 }
354
355 octave_value
resize(const dim_vector & dv,bool fill) const356 octave_range::resize (const dim_vector& dv, bool fill) const
357 {
358 NDArray retval = array_value ();
359 if (fill)
360 retval.resize (dv, 0);
361 else
362 retval.resize (dv);
363 return retval;
364 }
365
366 octave_value
convert_to_str_internal(bool pad,bool force,char type) const367 octave_range::convert_to_str_internal (bool pad, bool force, char type) const
368 {
369 octave_value tmp (range.matrix_value ());
370 return tmp.convert_to_str (pad, force, type);
371 }
372
373 octave_value
as_double(void) const374 octave_range::as_double (void) const
375 {
376 return range;
377 }
378
379 octave_value
as_single(void) const380 octave_range::as_single (void) const
381 {
382 return FloatMatrix (range.matrix_value ());
383 }
384
385 octave_value
as_int8(void) const386 octave_range::as_int8 (void) const
387 {
388 return int8NDArray (range.matrix_value ());
389 }
390
391 octave_value
as_int16(void) const392 octave_range::as_int16 (void) const
393 {
394 return int16NDArray (range.matrix_value ());
395 }
396
397 octave_value
as_int32(void) const398 octave_range::as_int32 (void) const
399 {
400 return int32NDArray (range.matrix_value ());
401 }
402
403 octave_value
as_int64(void) const404 octave_range::as_int64 (void) const
405 {
406 return int64NDArray (range.matrix_value ());
407 }
408
409 octave_value
as_uint8(void) const410 octave_range::as_uint8 (void) const
411 {
412 return uint8NDArray (range.matrix_value ());
413 }
414
415 octave_value
as_uint16(void) const416 octave_range::as_uint16 (void) const
417 {
418 return uint16NDArray (range.matrix_value ());
419 }
420
421 octave_value
as_uint32(void) const422 octave_range::as_uint32 (void) const
423 {
424 return uint32NDArray (range.matrix_value ());
425 }
426
427 octave_value
as_uint64(void) const428 octave_range::as_uint64 (void) const
429 {
430 return uint64NDArray (range.matrix_value ());
431 }
432
433 void
print(std::ostream & os,bool pr_as_read_syntax)434 octave_range::print (std::ostream& os, bool pr_as_read_syntax)
435 {
436 print_raw (os, pr_as_read_syntax);
437 newline (os);
438 }
439
440 void
print_raw(std::ostream & os,bool pr_as_read_syntax) const441 octave_range::print_raw (std::ostream& os, bool pr_as_read_syntax) const
442 {
443 octave_print_internal (os, range, pr_as_read_syntax,
444 current_print_indent_level ());
445 }
446
447 bool
print_name_tag(std::ostream & os,const std::string & name) const448 octave_range::print_name_tag (std::ostream& os, const std::string& name) const
449 {
450 bool retval = false;
451
452 octave_idx_type n = range.numel ();
453
454 indent (os);
455
456 if (n == 0 || n == 1)
457 os << name << " = ";
458 else
459 {
460 os << name << " =";
461 newline (os);
462 if (! Vcompact_format)
463 newline (os);
464
465 retval = true;
466 }
467
468 return retval;
469 }
470
471 void
short_disp(std::ostream & os) const472 octave_range::short_disp (std::ostream& os) const
473 {
474 octave_idx_type len = range.numel ();
475
476 if (len == 0)
477 os << "[]";
478 else
479 {
480 os << range.base () << ':';
481
482 if (len > 1)
483 {
484 if (range.inc () != 1)
485 os << range.inc () << ':';
486
487 os << range.limit ();
488 }
489 }
490 }
491
492 // Skip white space and comments on stream IS.
493
494 static void
skip_comments(std::istream & is)495 skip_comments (std::istream& is)
496 {
497 char c = '\0';
498 while (is.get (c))
499 {
500 if (c == ' ' || c == '\t' || c == '\n')
501 ; // Skip whitespace on way to beginning of next line.
502 else
503 break;
504 }
505
506 skip_until_newline (is, false);
507 }
508
509 float_display_format
get_edit_display_format(void) const510 octave_range::get_edit_display_format (void) const
511 {
512 return make_format (range_value ());
513 }
514
515 std::string
edit_display(const float_display_format & fmt,octave_idx_type,octave_idx_type j) const516 octave_range::edit_display (const float_display_format& fmt,
517 octave_idx_type, octave_idx_type j) const
518 {
519 std::ostringstream buf;
520 octave_print_internal (buf, fmt, range.elem (j));
521 return buf.str ();
522 }
523
524 bool
save_ascii(std::ostream & os)525 octave_range::save_ascii (std::ostream& os)
526 {
527 Range r = range_value ();
528 double base = r.base ();
529 double limit = r.limit ();
530 double inc = r.inc ();
531 octave_idx_type len = r.numel ();
532
533 if (inc != 0)
534 os << "# base, limit, increment\n";
535 else
536 os << "# base, length, increment\n";
537
538 octave_write_double (os, base);
539 os << ' ';
540 if (inc != 0)
541 octave_write_double (os, limit);
542 else
543 os << len;
544 os << ' ';
545 octave_write_double (os, inc);
546 os << "\n";
547
548 return true;
549 }
550
551 bool
load_ascii(std::istream & is)552 octave_range::load_ascii (std::istream& is)
553 {
554 // # base, limit, range comment added by save ().
555 skip_comments (is);
556
557 double base, limit, inc;
558 is >> base >> limit >> inc;
559
560 if (! is)
561 error ("load: failed to load range constant");
562
563 if (inc != 0)
564 range = Range (base, limit, inc);
565 else
566 range = Range (base, inc, static_cast<octave_idx_type> (limit));
567
568 return true;
569 }
570
571 bool
save_binary(std::ostream & os,bool)572 octave_range::save_binary (std::ostream& os, bool /* save_as_floats */)
573 {
574 char tmp = LS_DOUBLE;
575 os.write (reinterpret_cast<char *> (&tmp), 1);
576 Range r = range_value ();
577 double bas = r.base ();
578 double lim = r.limit ();
579 double inc = r.inc ();
580 if (inc == 0)
581 lim = r.numel ();
582
583 os.write (reinterpret_cast<char *> (&bas), 8);
584 os.write (reinterpret_cast<char *> (&lim), 8);
585 os.write (reinterpret_cast<char *> (&inc), 8);
586
587 return true;
588 }
589
590 bool
load_binary(std::istream & is,bool swap,octave::mach_info::float_format)591 octave_range::load_binary (std::istream& is, bool swap,
592 octave::mach_info::float_format /* fmt */)
593 {
594 char tmp;
595 if (! is.read (reinterpret_cast<char *> (&tmp), 1))
596 return false;
597 double bas, lim, inc;
598 if (! is.read (reinterpret_cast<char *> (&bas), 8))
599 return false;
600 if (swap)
601 swap_bytes<8> (&bas);
602 if (! is.read (reinterpret_cast<char *> (&lim), 8))
603 return false;
604 if (swap)
605 swap_bytes<8> (&lim);
606 if (! is.read (reinterpret_cast<char *> (&inc), 8))
607 return false;
608 if (swap)
609 swap_bytes<8> (&inc);
610 if (inc != 0)
611 range = Range (bas, lim, inc);
612 else
613 range = Range (bas, inc, static_cast<octave_idx_type> (lim));
614
615 return true;
616 }
617
618 #if defined (HAVE_HDF5)
619
620 // The following subroutines creates an HDF5 representation of the way
621 // we will store Octave range types (triplets of floating-point numbers).
622 // NUM_TYPE is the HDF5 numeric type to use for storage (e.g.
623 // H5T_NATIVE_DOUBLE to save as 'double'). Note that any necessary
624 // conversions are handled automatically by HDF5.
625
626 static hid_t
hdf5_make_range_type(hid_t num_type)627 hdf5_make_range_type (hid_t num_type)
628 {
629 hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 3);
630
631 H5Tinsert (type_id, "base", 0 * sizeof (double), num_type);
632 H5Tinsert (type_id, "limit", 1 * sizeof (double), num_type);
633 H5Tinsert (type_id, "increment", 2 * sizeof (double), num_type);
634
635 return type_id;
636 }
637
638 #endif
639
640 bool
save_hdf5(octave_hdf5_id loc_id,const char * name,bool)641 octave_range::save_hdf5 (octave_hdf5_id loc_id, const char *name,
642 bool /* save_as_floats */)
643 {
644 bool retval = false;
645
646 #if defined (HAVE_HDF5)
647
648 hsize_t dimens[3];
649 hid_t space_hid, type_hid, data_hid;
650 space_hid = type_hid = data_hid = -1;
651
652 space_hid = H5Screate_simple (0, dimens, nullptr);
653 if (space_hid < 0) return false;
654
655 type_hid = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
656 if (type_hid < 0)
657 {
658 H5Sclose (space_hid);
659 return false;
660 }
661 #if defined (HAVE_HDF5_18)
662 data_hid = H5Dcreate (loc_id, name, type_hid, space_hid,
663 octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
664 #else
665 data_hid = H5Dcreate (loc_id, name, type_hid, space_hid, octave_H5P_DEFAULT);
666 #endif
667 if (data_hid < 0)
668 {
669 H5Sclose (space_hid);
670 H5Tclose (type_hid);
671 return false;
672 }
673
674 Range r = range_value ();
675 double range_vals[3];
676 range_vals[0] = r.base ();
677 range_vals[1] = (r.inc () != 0 ? r.limit () : r.numel ());
678 range_vals[2] = r.inc ();
679
680 if (H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
681 octave_H5P_DEFAULT, range_vals)
682 >= 0)
683 {
684 octave_idx_type nel = r.numel ();
685 retval = hdf5_add_scalar_attr (data_hid, H5T_NATIVE_IDX,
686 "OCTAVE_RANGE_NELEM", &nel) >= 0;
687 }
688 else
689 retval = false;
690
691 H5Dclose (data_hid);
692 H5Tclose (type_hid);
693 H5Sclose (space_hid);
694
695 #else
696 octave_unused_parameter (loc_id);
697 octave_unused_parameter (name);
698
699 warn_save ("hdf5");
700 #endif
701
702 return retval;
703 }
704
705 bool
load_hdf5(octave_hdf5_id loc_id,const char * name)706 octave_range::load_hdf5 (octave_hdf5_id loc_id, const char *name)
707 {
708 bool retval = false;
709
710 #if defined (HAVE_HDF5)
711
712 #if defined (HAVE_HDF5_18)
713 hid_t data_hid = H5Dopen (loc_id, name, octave_H5P_DEFAULT);
714 #else
715 hid_t data_hid = H5Dopen (loc_id, name);
716 #endif
717 hid_t type_hid = H5Dget_type (data_hid);
718
719 hid_t range_type = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
720
721 if (! hdf5_types_compatible (type_hid, range_type))
722 {
723 H5Tclose (range_type);
724 H5Dclose (data_hid);
725 return false;
726 }
727
728 hid_t space_hid = H5Dget_space (data_hid);
729 hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
730
731 if (rank != 0)
732 {
733 H5Tclose (range_type);
734 H5Sclose (space_hid);
735 H5Dclose (data_hid);
736 return false;
737 }
738
739 double rangevals[3];
740 if (H5Dread (data_hid, range_type, octave_H5S_ALL, octave_H5S_ALL,
741 octave_H5P_DEFAULT, rangevals)
742 >= 0)
743 {
744 retval = true;
745 octave_idx_type nel;
746 if (hdf5_get_scalar_attr (data_hid, H5T_NATIVE_IDX,
747 "OCTAVE_RANGE_NELEM", &nel))
748 range = Range (rangevals[0], rangevals[2], nel);
749 else
750 {
751 if (rangevals[2] != 0)
752 range = Range (rangevals[0], rangevals[1], rangevals[2]);
753 else
754 range = Range (rangevals[0], rangevals[2],
755 static_cast<octave_idx_type> (rangevals[1]));
756 }
757 }
758
759 H5Tclose (range_type);
760 H5Sclose (space_hid);
761 H5Dclose (data_hid);
762
763 #else
764 octave_unused_parameter (loc_id);
765 octave_unused_parameter (name);
766
767 warn_load ("hdf5");
768 #endif
769
770 return retval;
771 }
772
773 mxArray *
as_mxArray(void) const774 octave_range::as_mxArray (void) const
775 {
776 mxArray *retval = new mxArray (mxDOUBLE_CLASS, dims (), mxREAL);
777
778 double *pr = static_cast<double *> (retval->get_data ());
779
780 mwSize nel = numel ();
781
782 Matrix m = matrix_value ();
783
784 const double *p = m.data ();
785
786 for (mwSize i = 0; i < nel; i++)
787 pr[i] = p[i];
788
789 return retval;
790 }
791
792 octave_value
fast_elem_extract(octave_idx_type n) const793 octave_range::fast_elem_extract (octave_idx_type n) const
794 {
795 return (n < range.numel ()) ? octave_value (range.elem (n))
796 : octave_value ();
797 }
798