Range.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 1993-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 <cfloat>
00028 
00029 #include <iostream>
00030 #include <limits>
00031 
00032 #include "Range.h"
00033 #include "lo-error.h"
00034 #include "lo-mappers.h"
00035 #include "lo-math.h"
00036 #include "lo-utils.h"
00037 #include "Array-util.h"
00038 
00039 Range::Range (double b, double i, octave_idx_type n)
00040   : rng_base (b), rng_limit (b + (n-1) * i), rng_inc (i),
00041     rng_nelem (n), cache ()
00042 {
00043   if (! xfinite (b) || ! xfinite (i))
00044     rng_nelem = -2;
00045 }
00046 
00047 bool
00048 Range::all_elements_are_ints (void) const
00049 {
00050   // If the base and increment are ints, the final value in the range
00051   // will also be an integer, even if the limit is not. If there is one
00052   // or fewer elements only the base needs to be an integer
00053 
00054   return (! (xisnan (rng_base) || xisnan (rng_inc))
00055           && (NINTbig (rng_base) == rng_base || rng_nelem < 1)
00056           && (NINTbig (rng_inc) == rng_inc || rng_nelem <= 1));
00057 }
00058 
00059 Matrix
00060 Range::matrix_value (void) const
00061 {
00062   if (rng_nelem > 0 && cache.nelem () == 0)
00063     {
00064       cache.resize (1, rng_nelem);
00065       double b = rng_base;
00066       double increment = rng_inc;
00067       for (octave_idx_type i = 0; i < rng_nelem; i++)
00068         cache(i) = b + i * increment;
00069 
00070       // On some machines (x86 with extended precision floating point
00071       // arithmetic, for example) it is possible that we can overshoot
00072       // the limit by approximately the machine precision even though
00073       // we were very careful in our calculation of the number of
00074       // elements.
00075 
00076       if ((rng_inc > 0 && cache(rng_nelem-1) > rng_limit)
00077           || (rng_inc < 0 && cache(rng_nelem-1) < rng_limit))
00078         cache(rng_nelem-1) = rng_limit;
00079     }
00080 
00081   return cache;
00082 }
00083 
00084 double
00085 Range::checkelem (octave_idx_type i) const
00086 {
00087   if (i < 0 || i >= rng_nelem)
00088     gripe_index_out_of_range (1, 1, i+1, rng_nelem);
00089 
00090   return rng_base + rng_inc * i;
00091 }
00092 
00093 struct _rangeidx_helper
00094 {
00095   double *array, base, inc;
00096   _rangeidx_helper (double *a, double b, double i)
00097     : array (a), base (b), inc (i) { }
00098   void operator () (octave_idx_type i)
00099     { *array++ = base + i * inc; }
00100 };
00101 
00102 Array<double>
00103 Range::index (const idx_vector& i) const
00104 {
00105   Array<double> retval;
00106 
00107   octave_idx_type n = rng_nelem;
00108 
00109   if (i.is_colon ())
00110     {
00111       retval = matrix_value ().reshape (dim_vector (rng_nelem, 1));
00112     }
00113   else
00114     {
00115       if (i.extent (n) != n)
00116         gripe_index_out_of_range (1, 1, i.extent (n), n); // throws
00117 
00118       dim_vector rd = i.orig_dimensions ();
00119       octave_idx_type il = i.length (n);
00120 
00121       // taken from Array.cc.
00122 
00123       if (n != 1 && rd.is_vector ())
00124         rd = dim_vector (1, il);
00125 
00126       retval.clear (rd);
00127 
00128       i.loop (n, _rangeidx_helper (retval.fortran_vec (), rng_base, rng_inc));
00129     }
00130 
00131   return retval;
00132 }
00133 
00134 // NOTE: max and min only return useful values if nelem > 0.
00135 
00136 double
00137 Range::min (void) const
00138 {
00139   double retval = 0.0;
00140   if (rng_nelem > 0)
00141     {
00142       if (rng_inc > 0)
00143         retval = rng_base;
00144       else
00145         {
00146           retval = rng_base + (rng_nelem - 1) * rng_inc;
00147 
00148           // See the note in the matrix_value method above.
00149 
00150           if (retval < rng_limit)
00151             retval = rng_limit;
00152         }
00153 
00154     }
00155   return retval;
00156 }
00157 
00158 double
00159 Range::max (void) const
00160 {
00161   double retval = 0.0;
00162   if (rng_nelem > 0)
00163     {
00164       if (rng_inc > 0)
00165         {
00166           retval = rng_base + (rng_nelem - 1) * rng_inc;
00167 
00168           // See the note in the matrix_value method above.
00169 
00170           if (retval > rng_limit)
00171             retval = rng_limit;
00172         }
00173       else
00174         retval = rng_base;
00175     }
00176   return retval;
00177 }
00178 
00179 void
00180 Range::sort_internal (bool ascending)
00181 {
00182   if (ascending && rng_base > rng_limit && rng_inc < 0.0)
00183     {
00184       double tmp = rng_base;
00185       rng_base = min ();
00186       rng_limit = tmp;
00187       rng_inc = -rng_inc;
00188       clear_cache ();
00189     }
00190   else if (! ascending && rng_base < rng_limit && rng_inc > 0.0)
00191     {
00192       double tmp = rng_limit;
00193       rng_limit = min ();
00194       rng_base = tmp;
00195       rng_inc = -rng_inc;
00196       clear_cache ();
00197     }
00198 }
00199 
00200 void
00201 Range::sort_internal (Array<octave_idx_type>& sidx, bool ascending)
00202 {
00203   octave_idx_type nel = nelem ();
00204 
00205   sidx.resize (dim_vector (1, nel));
00206 
00207   octave_idx_type *psidx = sidx.fortran_vec ();
00208 
00209   bool reverse = false;
00210 
00211   if (ascending && rng_base > rng_limit && rng_inc < 0.0)
00212     {
00213       double tmp = rng_base;
00214       rng_base = min ();
00215       rng_limit = tmp;
00216       rng_inc = -rng_inc;
00217       clear_cache ();
00218       reverse = true;
00219     }
00220   else if (! ascending && rng_base < rng_limit && rng_inc > 0.0)
00221     {
00222       double tmp = rng_limit;
00223       rng_limit = min ();
00224       rng_base = tmp;
00225       rng_inc = -rng_inc;
00226       clear_cache ();
00227       reverse = true;
00228     }
00229 
00230   octave_idx_type tmp = reverse ? nel - 1 : 0;
00231   octave_idx_type stp = reverse ? -1 : 1;
00232 
00233   for (octave_idx_type i = 0; i < nel; i++, tmp += stp)
00234     psidx[i] = tmp;
00235 
00236 }
00237 
00238 Matrix
00239 Range::diag (octave_idx_type k) const
00240 {
00241   return matrix_value ().diag (k);
00242 }
00243 
00244 Range
00245 Range::sort (octave_idx_type dim, sortmode mode) const
00246 {
00247   Range retval = *this;
00248 
00249   if (dim == 1)
00250     {
00251       if (mode == ASCENDING)
00252         retval.sort_internal (true);
00253       else if (mode == DESCENDING)
00254         retval.sort_internal (false);
00255     }
00256   else if (dim != 0)
00257     (*current_liboctave_error_handler) ("Range::sort: invalid dimension");
00258 
00259   return retval;
00260 }
00261 
00262 Range
00263 Range::sort (Array<octave_idx_type>& sidx, octave_idx_type dim,
00264              sortmode mode) const
00265 {
00266   Range retval = *this;
00267 
00268   if (dim == 1)
00269     {
00270       if (mode == ASCENDING)
00271           retval.sort_internal (sidx, true);
00272       else if (mode == DESCENDING)
00273         retval.sort_internal (sidx, false);
00274     }
00275   else if (dim != 0)
00276     (*current_liboctave_error_handler) ("Range::sort: invalid dimension");
00277 
00278   return retval;
00279 }
00280 
00281 sortmode
00282 Range::is_sorted (sortmode mode) const
00283 {
00284   if (rng_nelem > 1 && rng_inc < 0)
00285     mode = (mode == ASCENDING) ? UNSORTED : DESCENDING;
00286   else if (rng_nelem > 1 && rng_inc > 0)
00287     mode = (mode == DESCENDING) ? UNSORTED : ASCENDING;
00288   else
00289     mode = mode ? mode : ASCENDING;
00290 
00291   return mode;
00292 }
00293 
00294 std::ostream&
00295 operator << (std::ostream& os, const Range& a)
00296 {
00297   double b = a.base ();
00298   double increment = a.inc ();
00299   octave_idx_type num_elem = a.nelem ();
00300 
00301   for (octave_idx_type i = 0; i < num_elem-1; i++)
00302     os << b + i * increment << " ";
00303 
00304   // Prevent overshoot.  See comment in the matrix_value method
00305   // above.
00306 
00307   os << (increment > 0 ? a.max () : a.min ()) << "\n";
00308 
00309   return os;
00310 }
00311 
00312 std::istream&
00313 operator >> (std::istream& is, Range& a)
00314 {
00315   is >> a.rng_base;
00316   if (is)
00317     {
00318       is >> a.rng_limit;
00319       if (is)
00320         {
00321           is >> a.rng_inc;
00322           a.rng_nelem = a.nelem_internal ();
00323         }
00324     }
00325 
00326   return is;
00327 }
00328 
00329 Range
00330 operator - (const Range& r)
00331 {
00332   return Range (-r.base (), -r.inc (), r.nelem ());
00333 }
00334 
00335 Range operator + (double x, const Range& r)
00336 {
00337   Range result (x + r.base (), r.inc (), r.nelem ());
00338   if (result.rng_nelem < 0)
00339     result.cache = x + r.matrix_value ();
00340 
00341   return result;
00342 }
00343 
00344 Range operator + (const Range& r, double x)
00345 {
00346   Range result (r.base () + x, r.inc (), r.nelem ());
00347   if (result.rng_nelem < 0)
00348     result.cache = r.matrix_value () + x;
00349 
00350   return result;
00351 }
00352 
00353 Range operator - (double x, const Range& r)
00354 {
00355   Range result (x - r.base (), -r.inc (), r.nelem ());
00356   if (result.rng_nelem < 0)
00357     result.cache = x - r.matrix_value ();
00358 
00359   return result;
00360 }
00361 
00362 Range operator - (const Range& r, double x)
00363 {
00364   Range result (r.base () - x, r.inc (), r.nelem ());
00365   if (result.rng_nelem < 0)
00366     result.cache = r.matrix_value () - x;
00367 
00368   return result;
00369 }
00370 
00371 Range operator * (double x, const Range& r)
00372 {
00373   Range result (x * r.base (), x * r.inc (), r.nelem ());
00374   if (result.rng_nelem < 0)
00375     result.cache = x * r.matrix_value ();
00376 
00377   return result;
00378 }
00379 
00380 Range operator * (const Range& r, double x)
00381 {
00382   Range result (r.base () * x, r.inc () * x, r.nelem ());
00383   if (result.rng_nelem < 0)
00384     result.cache = r.matrix_value () * x;
00385 
00386   return result;
00387 }
00388 
00389 
00390 // C  See Knuth, Art Of Computer Programming, Vol. 1, Problem 1.2.4-5.
00391 // C
00392 // C===Tolerant FLOOR function.
00393 // C
00394 // C    X  -  is given as a Double Precision argument to be operated on.
00395 // C          It is assumed that X is represented with M mantissa bits.
00396 // C    CT -  is   given   as   a   Comparison   Tolerance   such   that
00397 // C          0.LT.CT.LE.3-SQRT(5)/2. If the relative difference between
00398 // C          X and A whole number is  less  than  CT,  then  TFLOOR  is
00399 // C          returned   as   this   whole   number.   By  treating  the
00400 // C          floating-point numbers as a finite ordered set  note  that
00401 // C          the  heuristic  EPS=2.**(-(M-1))   and   CT=3*EPS   causes
00402 // C          arguments  of  TFLOOR/TCEIL to be treated as whole numbers
00403 // C          if they are  exactly  whole  numbers  or  are  immediately
00404 // C          adjacent to whole number representations.  Since EPS,  the
00405 // C          "distance"  between  floating-point  numbers  on  the unit
00406 // C          interval, and M, the number of bits in X'S mantissa, exist
00407 // C          on  every  floating-point   computer,   TFLOOR/TCEIL   are
00408 // C          consistently definable on every floating-point computer.
00409 // C
00410 // C          For more information see the following references:
00411 // C    (1) P. E. Hagerty, "More On Fuzzy Floor And Ceiling," APL  QUOTE
00412 // C        QUAD 8(4):20-24, June 1978. Note that TFLOOR=FL5.
00413 // C    (2) L. M. Breed, "Definitions For Fuzzy Floor And Ceiling",  APL
00414 // C        QUOTE QUAD 8(3):16-23, March 1978. This paper cites FL1 through
00415 // C        FL5, the history of five years of evolutionary development of
00416 // C        FL5 - the seven lines of code below - by open collaboration
00417 // C        and corroboration of the mathematical-computing community.
00418 // C
00419 // C  Penn State University Center for Academic Computing
00420 // C  H. D. Knoble - August, 1978.
00421 
00422 static inline double
00423 tfloor (double x, double ct)
00424 {
00425 // C---------FLOOR(X) is the largest integer algebraically less than
00426 // C         or equal to X; that is, the unfuzzy FLOOR function.
00427 
00428 //  DINT (X) = X - DMOD (X, 1.0);
00429 //  FLOOR (X) = DINT (X) - DMOD (2.0 + DSIGN (1.0, X), 3.0);
00430 
00431 // C---------Hagerty's FL5 function follows...
00432 
00433   double q = 1.0;
00434 
00435   if (x < 0.0)
00436     q = 1.0 - ct;
00437 
00438   double rmax = q / (2.0 - ct);
00439 
00440   double t1 = 1.0 + gnulib::floor (x);
00441   t1 = (ct / q) * (t1 < 0.0 ? -t1 : t1);
00442   t1 = rmax < t1 ? rmax : t1;
00443   t1 = ct > t1 ? ct : t1;
00444   t1 = gnulib::floor (x + t1);
00445 
00446   if (x <= 0.0 || (t1 - x) < rmax)
00447     return t1;
00448   else
00449     return t1 - 1.0;
00450 }
00451 
00452 static inline double
00453 tceil (double x, double ct)
00454 {
00455   return -tfloor (-x, ct);
00456 }
00457 
00458 static inline bool
00459 teq (double u, double v, double ct = 3.0 * DBL_EPSILON)
00460 {
00461   double tu = fabs (u);
00462   double tv = fabs (v);
00463 
00464   return fabs (u - v) < ((tu > tv ? tu : tv) * ct);
00465 }
00466 
00467 octave_idx_type
00468 Range::nelem_internal (void) const
00469 {
00470   octave_idx_type retval = -1;
00471 
00472   if (rng_inc == 0
00473       || (rng_limit > rng_base && rng_inc < 0)
00474       || (rng_limit < rng_base && rng_inc > 0))
00475     {
00476       retval = 0;
00477     }
00478   else
00479     {
00480       double ct = 3.0 * DBL_EPSILON;
00481 
00482       double tmp = tfloor ((rng_limit - rng_base + rng_inc) / rng_inc, ct);
00483 
00484       octave_idx_type n_elt = (tmp > 0.0 ? static_cast<octave_idx_type> (tmp) : 0);
00485 
00486       // If the final element that we would compute for the range is
00487       // equal to the limit of the range, or is an adjacent floating
00488       // point number, accept it.  Otherwise, try a range with one
00489       // fewer element.  If that fails, try again with one more
00490       // element.
00491       //
00492       // I'm not sure this is very good, but it seems to work better than
00493       // just using tfloor as above.  For example, without it, the
00494       // expression 1.8:0.05:1.9 fails to produce the expected result of
00495       // [1.8, 1.85, 1.9].
00496 
00497       if (! teq (rng_base + (n_elt - 1) * rng_inc, rng_limit))
00498         {
00499           if (teq (rng_base + (n_elt - 2) * rng_inc, rng_limit))
00500             n_elt--;
00501           else if (teq (rng_base + n_elt * rng_inc, rng_limit))
00502             n_elt++;
00503         }
00504 
00505       retval = (n_elt >= std::numeric_limits<octave_idx_type>::max () - 1) ? -1 : n_elt;
00506     }
00507 
00508   return retval;
00509 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines