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