ov-range.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 1996-2012 John W. Eaton
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include <iostream>
00028 
00029 #include "lo-ieee.h"
00030 #include "lo-utils.h"
00031 
00032 #include "defun.h"
00033 #include "variables.h"
00034 #include "gripes.h"
00035 #include "ops.h"
00036 #include "oct-obj.h"
00037 #include "ov-range.h"
00038 #include "ov-re-mat.h"
00039 #include "ov-scalar.h"
00040 #include "pr-output.h"
00041 
00042 #include "byte-swap.h"
00043 #include "ls-ascii-helper.h"
00044 #include "ls-hdf5.h"
00045 #include "ls-utils.h"
00046 
00047 // If TRUE, allow ranges with non-integer elements as array indices.
00048 bool Vallow_noninteger_range_as_index = false;
00049 
00050 DEFINE_OCTAVE_ALLOCATOR (octave_range);
00051 
00052 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_range, "range", "double");
00053 
00054 static octave_base_value *
00055 default_numeric_conversion_function (const octave_base_value& a)
00056 {
00057   CAST_CONV_ARG (const octave_range&);
00058 
00059   return new octave_matrix (v.matrix_value ());
00060 }
00061 
00062 octave_base_value::type_conv_info
00063 octave_range::numeric_conversion_function (void) const
00064 {
00065   return octave_base_value::type_conv_info (default_numeric_conversion_function,
00066                                             octave_matrix::static_type_id ());
00067 }
00068 
00069 octave_base_value *
00070 octave_range::try_narrowing_conversion (void)
00071 {
00072   octave_base_value *retval = 0;
00073 
00074   switch (range.nelem ())
00075     {
00076     case 1:
00077       retval = new octave_scalar (range.base ());
00078       break;
00079 
00080     case 0:
00081       retval = new octave_matrix (Matrix (1, 0));
00082       break;
00083 
00084     case -2:
00085       retval = new octave_matrix (range.matrix_value ());
00086       break;
00087 
00088     default:
00089       break;
00090     }
00091 
00092   return retval;
00093 }
00094 
00095 octave_value
00096 octave_range::subsref (const std::string& type,
00097                        const std::list<octave_value_list>& idx)
00098 {
00099   octave_value retval;
00100 
00101   switch (type[0])
00102     {
00103     case '(':
00104       retval = do_index_op (idx.front ());
00105       break;
00106 
00107     case '{':
00108     case '.':
00109       {
00110         std::string nm = type_name ();
00111         error ("%s cannot be indexed with %c", nm.c_str (), type[0]);
00112       }
00113       break;
00114 
00115     default:
00116       panic_impossible ();
00117     }
00118 
00119   return retval.next_subsref (type, idx);
00120 }
00121 
00122 octave_value
00123 octave_range::do_index_op (const octave_value_list& idx, bool resize_ok)
00124 {
00125   if (idx.length () == 1 && ! resize_ok)
00126     {
00127       octave_value retval;
00128 
00129       // The range can handle a single subscript.
00130       idx_vector i = idx(0).index_vector ();
00131       if (! error_state)
00132         {
00133           if (i.is_scalar () && i(0) < range.nelem ())
00134             retval = range.elem (i(0));
00135           else
00136             retval = range.index (i);
00137         }
00138 
00139       return retval;
00140     }
00141   else
00142     {
00143       octave_value tmp (new octave_matrix (range.matrix_value ()));
00144 
00145       return tmp.do_index_op (idx, resize_ok);
00146     }
00147 }
00148 
00149 idx_vector
00150 octave_range::index_vector (void) const
00151 {
00152   if (idx_cache)
00153     return *idx_cache;
00154   else
00155     {
00156       if (! Vallow_noninteger_range_as_index
00157           || range.all_elements_are_ints ())
00158         return set_idx_cache (idx_vector (range));
00159       else
00160         {
00161           warning_with_id ("Octave:noninteger-range-as-index",
00162                            "non-integer range used as index");
00163 
00164           return octave_value (matrix_value ()).round ().index_vector ();
00165         }
00166     }
00167 }
00168 
00169 double
00170 octave_range::double_value (bool) const
00171 {
00172   double retval = lo_ieee_nan_value ();
00173 
00174   octave_idx_type nel = range.nelem ();
00175 
00176   if (nel > 0)
00177     {
00178       gripe_implicit_conversion ("Octave:array-as-scalar",
00179                                  "range", "real scalar");
00180 
00181       retval = range.base ();
00182     }
00183   else
00184     gripe_invalid_conversion ("range", "real scalar");
00185 
00186   return retval;
00187 }
00188 
00189 float
00190 octave_range::float_value (bool) const
00191 {
00192   float retval = lo_ieee_float_nan_value ();
00193 
00194   octave_idx_type nel = range.nelem ();
00195 
00196   if (nel > 0)
00197     {
00198       gripe_implicit_conversion ("Octave:array-as-scalar",
00199                                  "range", "real scalar");
00200 
00201       retval = range.base ();
00202     }
00203   else
00204     gripe_invalid_conversion ("range", "real scalar");
00205 
00206   return retval;
00207 }
00208 
00209 charNDArray
00210 octave_range::char_array_value (bool) const
00211 {
00212   const Matrix matrix = range.matrix_value ();
00213   charNDArray retval (dims ());
00214 
00215   octave_idx_type nel = numel ();
00216 
00217   for (octave_idx_type i = 0; i < nel; i++)
00218     retval.elem (i) = static_cast<char>(matrix.elem (i));
00219 
00220   return retval;
00221 }
00222 
00223 octave_value
00224 octave_range::all (int dim) const
00225 {
00226   // FIXME -- this is a potential waste of memory.
00227 
00228   Matrix m = range.matrix_value ();
00229 
00230   return m.all (dim);
00231 }
00232 
00233 octave_value
00234 octave_range::any (int dim) const
00235 {
00236   // FIXME -- this is a potential waste of memory.
00237 
00238   Matrix m = range.matrix_value ();
00239 
00240   return m.any (dim);
00241 }
00242 
00243 octave_value
00244 octave_range::diag (octave_idx_type k) const
00245 {
00246   return (k == 0
00247           ? octave_value (DiagMatrix (DiagArray2<double> (range.matrix_value ())))
00248           : octave_value (range.diag (k)));
00249 }
00250 
00251 
00252 bool
00253 octave_range::is_true (void) const
00254 {
00255   bool retval = false;
00256 
00257   if (range.nelem () != 0)
00258     {
00259       // FIXME -- this is a potential waste of memory.
00260 
00261       Matrix m ((range.matrix_value () . all ()) . all ());
00262 
00263       retval = (m.rows () == 1 && m.columns () == 1 && m (0, 0) != 0.0);
00264     }
00265 
00266   return retval;
00267 }
00268 
00269 Complex
00270 octave_range::complex_value (bool) const
00271 {
00272   double tmp = lo_ieee_nan_value ();
00273 
00274   Complex retval (tmp, tmp);
00275 
00276   octave_idx_type nel = range.nelem ();
00277 
00278   if (nel > 0)
00279     {
00280       gripe_implicit_conversion ("Octave:array-as-scalar",
00281                                  "range", "complex scalar");
00282 
00283       retval = range.base ();
00284     }
00285   else
00286     gripe_invalid_conversion ("range", "complex scalar");
00287 
00288   return retval;
00289 }
00290 
00291 FloatComplex
00292 octave_range::float_complex_value (bool) const
00293 {
00294   float tmp = lo_ieee_float_nan_value ();
00295 
00296   FloatComplex retval (tmp, tmp);
00297 
00298   octave_idx_type nel = range.nelem ();
00299 
00300   if (nel > 0)
00301     {
00302       gripe_implicit_conversion ("Octave:array-as-scalar",
00303                                  "range", "complex scalar");
00304 
00305       retval = range.base ();
00306     }
00307   else
00308     gripe_invalid_conversion ("range", "complex scalar");
00309 
00310   return retval;
00311 }
00312 
00313 boolNDArray
00314 octave_range::bool_array_value (bool warn) const
00315 {
00316   Matrix m = range.matrix_value ();
00317 
00318   if (m.any_element_is_nan ())
00319     gripe_nan_to_logical_conversion ();
00320   else if (warn && m.any_element_not_one_or_zero ())
00321     gripe_logical_conversion ();
00322 
00323   return boolNDArray (m);
00324 }
00325 
00326 octave_value
00327 octave_range::resize (const dim_vector& dv, bool fill) const
00328 {
00329   NDArray retval = array_value ();
00330   if (fill)
00331     retval.resize (dv, NDArray::resize_fill_value ());
00332   else
00333     retval.resize (dv);
00334   return retval;
00335 }
00336 
00337 octave_value
00338 octave_range::convert_to_str_internal (bool pad, bool force, char type) const
00339 {
00340   octave_value tmp (range.matrix_value ());
00341   return tmp.convert_to_str (pad, force, type);
00342 }
00343 
00344 void
00345 octave_range::print (std::ostream& os, bool pr_as_read_syntax) const
00346 {
00347   print_raw (os, pr_as_read_syntax);
00348   newline (os);
00349 }
00350 
00351 void
00352 octave_range::print_raw (std::ostream& os, bool pr_as_read_syntax) const
00353 {
00354   octave_print_internal (os, range, pr_as_read_syntax,
00355                          current_print_indent_level ());
00356 }
00357 
00358 bool
00359 octave_range::print_name_tag (std::ostream& os, const std::string& name) const
00360 {
00361   bool retval = false;
00362 
00363   octave_idx_type n = range.nelem ();
00364 
00365   indent (os);
00366 
00367   if (n == 0 || n == 1)
00368     os << name << " = ";
00369   else
00370     {
00371       os << name << " =";
00372       newline (os);
00373       if (! Vcompact_format)
00374         newline (os);
00375 
00376       retval = true;
00377     }
00378 
00379   return retval;
00380 }
00381 
00382 // Skip white space and comments on stream IS.
00383 
00384 static void
00385 skip_comments (std::istream& is)
00386 {
00387   char c = '\0';
00388   while (is.get (c))
00389     {
00390       if (c == ' ' || c == '\t' || c == '\n')
00391         ; // Skip whitespace on way to beginning of next line.
00392       else
00393         break;
00394     }
00395 
00396   skip_until_newline (is, false);
00397 }
00398 
00399 bool
00400 octave_range::save_ascii (std::ostream& os)
00401 {
00402   Range r = range_value ();
00403   double base = r.base ();
00404   double limit = r.limit ();
00405   double inc = r.inc ();
00406   octave_idx_type len = r.nelem ();
00407 
00408   if (inc != 0)
00409     os << "# base, limit, increment\n";
00410   else
00411     os << "# base, length, increment\n";
00412 
00413   octave_write_double (os, base);
00414   os << " ";
00415   if (inc != 0)
00416     octave_write_double (os, limit);
00417   else
00418     os << len;
00419   os << " ";
00420   octave_write_double (os, inc);
00421   os << "\n";
00422 
00423   return true;
00424 }
00425 
00426 bool
00427 octave_range::load_ascii (std::istream& is)
00428 {
00429   // # base, limit, range comment added by save ().
00430   skip_comments (is);
00431 
00432   double base, limit, inc;
00433   is >> base >> limit >> inc;
00434 
00435   if (!is)
00436     {
00437       error ("load: failed to load range constant");
00438       return false;
00439     }
00440 
00441   if (inc != 0)
00442     range = Range (base, limit, inc);
00443   else
00444     range = Range (base, inc, static_cast<octave_idx_type> (limit));
00445 
00446   return true;
00447 }
00448 
00449 bool
00450 octave_range::save_binary (std::ostream& os, bool& /* save_as_floats */)
00451 {
00452   char tmp = LS_DOUBLE;
00453   os.write (reinterpret_cast<char *> (&tmp), 1);
00454   Range r = range_value ();
00455   double bas = r.base ();
00456   double lim = r.limit ();
00457   double inc = r.inc ();
00458   if (inc == 0)
00459     lim = r.nelem ();
00460 
00461   os.write (reinterpret_cast<char *> (&bas), 8);
00462   os.write (reinterpret_cast<char *> (&lim), 8);
00463   os.write (reinterpret_cast<char *> (&inc), 8);
00464 
00465   return true;
00466 }
00467 
00468 bool
00469 octave_range::load_binary (std::istream& is, bool swap,
00470                            oct_mach_info::float_format /* fmt */)
00471 {
00472   char tmp;
00473   if (! is.read (reinterpret_cast<char *> (&tmp), 1))
00474     return false;
00475   double bas, lim, inc;
00476   if (! is.read (reinterpret_cast<char *> (&bas), 8))
00477     return false;
00478   if (swap)
00479     swap_bytes<8> (&bas);
00480   if (! is.read (reinterpret_cast<char *> (&lim), 8))
00481     return false;
00482   if (swap)
00483     swap_bytes<8> (&lim);
00484   if (! is.read (reinterpret_cast<char *> (&inc), 8))
00485     return false;
00486   if (swap)
00487     swap_bytes<8> (&inc);
00488   if (inc != 0)
00489     range = Range (bas, lim, inc);
00490   else
00491     range = Range (bas, inc, static_cast<octave_idx_type> (lim));
00492 
00493   return true;
00494 }
00495 
00496 #if defined (HAVE_HDF5)
00497 
00498 // The following subroutines creates an HDF5 representation of the way
00499 // we will store Octave range types (triplets of floating-point numbers).
00500 // NUM_TYPE is the HDF5 numeric type to use for storage (e.g.
00501 // H5T_NATIVE_DOUBLE to save as 'double'). Note that any necessary
00502 // conversions are handled automatically by HDF5.
00503 
00504 static hid_t
00505 hdf5_make_range_type (hid_t num_type)
00506 {
00507   hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 3);
00508 
00509   H5Tinsert (type_id, "base", 0 * sizeof (double), num_type);
00510   H5Tinsert (type_id, "limit", 1 * sizeof (double), num_type);
00511   H5Tinsert (type_id, "increment", 2 * sizeof (double), num_type);
00512 
00513   return type_id;
00514 }
00515 
00516 bool
00517 octave_range::save_hdf5 (hid_t loc_id, const char *name,
00518                          bool /* save_as_floats */)
00519 {
00520   hsize_t dimens[3];
00521   hid_t space_hid = -1, type_hid = -1, data_hid = -1;
00522   bool retval = true;
00523 
00524   space_hid = H5Screate_simple (0, dimens, 0);
00525   if (space_hid < 0) return false;
00526 
00527   type_hid = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
00528   if (type_hid < 0)
00529     {
00530       H5Sclose (space_hid);
00531       return false;
00532     }
00533 #if HAVE_HDF5_18
00534   data_hid = H5Dcreate (loc_id, name, type_hid, space_hid,
00535                         H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
00536 #else
00537   data_hid = H5Dcreate (loc_id, name, type_hid, space_hid, H5P_DEFAULT);
00538 #endif
00539   if (data_hid < 0)
00540     {
00541       H5Sclose (space_hid);
00542       H5Tclose (type_hid);
00543       return false;
00544     }
00545 
00546   Range r = range_value ();
00547   double range_vals[3];
00548   range_vals[0] = r.base ();
00549   range_vals[1] = r.inc () != 0 ? r.limit () : r.nelem ();
00550   range_vals[2] = r.inc ();
00551 
00552   if (H5Dwrite (data_hid, type_hid, H5S_ALL, H5S_ALL, H5P_DEFAULT,
00553                 range_vals) >= 0)
00554     {
00555       octave_idx_type nel = r.nelem ();
00556       retval = hdf5_add_scalar_attr (data_hid, H5T_NATIVE_IDX,
00557                                      "OCTAVE_RANGE_NELEM", &nel) >= 0;
00558     }
00559   else
00560     retval = false;
00561 
00562   H5Dclose (data_hid);
00563   H5Tclose (type_hid);
00564   H5Sclose (space_hid);
00565 
00566   return retval;
00567 }
00568 
00569 bool
00570 octave_range::load_hdf5 (hid_t loc_id, const char *name)
00571 {
00572   bool retval = false;
00573 
00574 #if HAVE_HDF5_18
00575   hid_t data_hid = H5Dopen (loc_id, name, H5P_DEFAULT);
00576 #else
00577   hid_t data_hid = H5Dopen (loc_id, name);
00578 #endif
00579   hid_t type_hid = H5Dget_type (data_hid);
00580 
00581   hid_t range_type = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
00582 
00583   if (! hdf5_types_compatible (type_hid, range_type))
00584     {
00585       H5Tclose (range_type);
00586       H5Dclose (data_hid);
00587       return false;
00588     }
00589 
00590   hid_t space_hid = H5Dget_space (data_hid);
00591   hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
00592 
00593   if (rank != 0)
00594     {
00595       H5Tclose (range_type);
00596       H5Sclose (space_hid);
00597       H5Dclose (data_hid);
00598       return false;
00599     }
00600 
00601   double rangevals[3];
00602   if (H5Dread (data_hid, range_type, H5S_ALL, H5S_ALL, H5P_DEFAULT,
00603                rangevals) >= 0)
00604     {
00605       retval = true;
00606       octave_idx_type nel;
00607       if (hdf5_get_scalar_attr (data_hid, H5T_NATIVE_IDX,
00608                                 "OCTAVE_RANGE_NELEM", &nel))
00609         range = Range (rangevals[0], rangevals[2], nel);
00610       else
00611         {
00612           if (rangevals[2] != 0)
00613             range = Range (rangevals[0], rangevals[1], rangevals[2]);
00614           else
00615             range = Range (rangevals[0], rangevals[2],
00616                            static_cast<octave_idx_type> (rangevals[1]));
00617         }
00618     }
00619 
00620   H5Tclose (range_type);
00621   H5Sclose (space_hid);
00622   H5Dclose (data_hid);
00623 
00624   return retval;
00625 }
00626 
00627 #endif
00628 
00629 mxArray *
00630 octave_range::as_mxArray (void) const
00631 {
00632   mxArray *retval = new mxArray (mxDOUBLE_CLASS, dims (), mxREAL);
00633 
00634   double *pr = static_cast<double *> (retval->get_data ());
00635 
00636   mwSize nel = numel ();
00637 
00638   Matrix m = matrix_value ();
00639 
00640   const double *p = m.data ();
00641 
00642   for (mwSize i = 0; i < nel; i++)
00643     pr[i] = p[i];
00644 
00645   return retval;
00646 }
00647 
00648 DEFUN (allow_noninteger_range_as_index, args, nargout,
00649   "-*- texinfo -*-\n\
00650 @deftypefn  {Built-in Function} {@var{val} =} allow_noninteger_range_as_index ()\n\
00651 @deftypefnx {Built-in Function} {@var{old_val} =} allow_noninteger_range_as_index (@var{new_val})\n\
00652 @deftypefnx {Built-in Function} {} allow_noninteger_range_as_index (@var{new_val}, \"local\")\n\
00653 Query or set the internal variable that controls whether non-integer\n\
00654 ranges are allowed as indices.  This might be useful for @sc{matlab}\n\
00655 compatibility; however, it is still not entirely compatible because\n\
00656 @sc{matlab} treats the range expression differently in different contexts.\n\
00657 \n\
00658 When called from inside a function with the \"local\" option, the variable is\n\
00659 changed locally for the function and any subroutines it calls.  The original\n\
00660 variable value is restored when exiting the function.\n\
00661 @end deftypefn")
00662 {
00663   return SET_INTERNAL_VARIABLE (allow_noninteger_range_as_index);
00664 }
00665 
00666 /*
00667 %!test
00668 %! x = 0:10;
00669 %! save = allow_noninteger_range_as_index ();
00670 %! warn_state = warning ("query", "Octave:noninteger-range-as-index");
00671 %! unwind_protect
00672 %!   allow_noninteger_range_as_index (false);
00673 %!   fail ('x(2.1:5)');
00674 %!   assert (x(2:5), 1:4);
00675 %!   allow_noninteger_range_as_index (true);
00676 %!   warning ("off", "Octave:noninteger-range-as-index");
00677 %!   assert (x(2.49:5), 1:3);
00678 %!   assert (x(2.5:5), 2:4);
00679 %!   assert (x(2.51:5), 2:4);
00680 %! unwind_protect_cleanup
00681 %!   allow_noninteger_range_as_index (save);
00682 %!   warning (warn_state.state, warn_state.identifier);
00683 %! end_unwind_protect
00684 */
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines