changeset 20501:16b9ec39ff46

Return empty matrix when linspace called with 0 points (bug #45820) * NEWS: Announce change. * data.cc (Flinspace): Verify input N is scalar. Verify inputs BASE, LIMIT are scalar/vectors. Add BIST tests for input validation, complex values, class of output, Matlab compatibility. Clarify documentation about vector inputs. * CMatrix.cc, CRowVector.cc, dMatrix.cc, dRowVector.cc, fCMatrix.cc, fCRowVector.cc, fMatrix.cc, fRowVector.cc: Return empty matrix when N < 1.
author Rik <rik@octave.org>
date Thu, 27 Aug 2015 13:12:21 -0700
parents 44eb1102f8a8
children 4bef1880098c
files NEWS libinterp/corefcn/data.cc liboctave/array/CMatrix.cc liboctave/array/CRowVector.cc liboctave/array/dMatrix.cc liboctave/array/dRowVector.cc liboctave/array/fCMatrix.cc liboctave/array/fCRowVector.cc liboctave/array/fMatrix.cc liboctave/array/fRowVector.cc
diffstat 10 files changed, 103 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Wed Aug 26 16:05:49 2015 -0400
+++ b/NEWS	Thu Aug 27 13:12:21 2015 -0700
@@ -22,6 +22,11 @@
  ** mkfifo now interprets the MODE argument as an octal, not decimal, integer.
     This is consistent with the equivalent shell command. 
 
+ ** linspace now returns an empty matrix if the number of requested points
+    is 0 or a negative number.  This change was made to be compatible with
+    Matlab releases newer than 2011.  In addition, Octave no longer supports
+    matrix inputs for A or B.
+
  ** The griddata function no longer plots the interpolated mesh if no output
     argument is requested, instead the vector or array of interpolated values
     is always returned for Matlab compatibility.
--- a/libinterp/corefcn/data.cc	Wed Aug 26 16:05:49 2015 -0400
+++ b/libinterp/corefcn/data.cc	Thu Aug 27 13:12:21 2015 -0700
@@ -5342,9 +5342,11 @@
 than @var{limit}, the elements are stored in decreasing order.  If the\n\
 number of points is not specified, a value of 100 is used.\n\
 \n\
-The @code{linspace} function always returns a row vector if both @var{base}\n\
-and @var{limit} are scalars.  If one, or both, of them are column vectors,\n\
-@code{linspace} returns a matrix.\n\
+The @code{linspace} function returns a row vector when both @var{base}\n\
+and @var{limit} are scalars.  If one, or both, inputs are vectors, then\n\
+@code{linspace} transforms them to column vectors and returns a matrix where\n\
+each row is an independent sequence between\n\
+@w{@code{@var{base}(@var{row_n}), @var{limit}(@var{row_n})}}.\n\
 \n\
 For compatibility with @sc{matlab}, return the second argument (@var{limit})\n\
 if fewer than two values are requested.\n\
@@ -5372,6 +5374,8 @@
 
       if (arg_3.is_numeric_type () && arg_3.is_empty ())
         npoints = 1;
+      else if (! arg_3.is_scalar_type ())
+        error ("linspace: N must be a scalar");
       else
         npoints = arg_3.idx_type_value ();
     }
@@ -5381,7 +5385,14 @@
       octave_value arg_1 = args(0);
       octave_value arg_2 = args(1);
 
-      if (arg_1.is_single_type () || arg_2.is_single_type ())
+      dim_vector sz1 = arg_1.dims ();
+      bool isvector1 = sz1.length () == 2 && (sz1(0) == 1 || sz1(1) == 1);
+      dim_vector sz2 = arg_2.dims ();
+      bool isvector2 = sz2.length () == 2 && (sz2(0) == 1 || sz2(1) == 1);
+
+      if (! isvector1 || ! isvector2)
+        error ("linspace: A, B must be scalars or vectors");
+      else if (arg_1.is_single_type () || arg_2.is_single_type ())
         {
           if (arg_1.is_complex_type () || arg_2.is_complex_type ())
             retval = do_linspace<FloatComplexMatrix> (arg_1, arg_2, npoints);
@@ -5413,12 +5424,32 @@
 %! assert (size (x2) == [1, 10] && x2(1) == 1 && x2(10) == 2);
 %! assert (size (x3) == [1, 10] && x3(1) == 1 && x3(10) == -2);
 
-%! ##assert (linspace ([1, 2; 3, 4], 5, 6), linspace (1, 5, 6))
-
+## Test complex values
+%!test
+%! exp = [1+0i, 2-1.25i, 3-2.5i, 4-3.75i, 5-5i];
+%! obs = linspace (1, 5-5i, 5);
+%! assert (obs, exp);
+
+## Test class of output
+%!assert (class (linspace (1, 2)), "double")
+%!assert (class (linspace (single (1), 2)), "single")
+%!assert (class (linspace (1, single (2))), "single")
+
+## Test obscure Matlab compatibility options
 %!assert (linspace (0, 1, []), 1)
+%!assert (linspace (10, 20, 2), [10 20])
+%!assert (linspace (10, 20, 1), [20])
+%!assert (linspace (10, 20, 0), zeros (1, 0))
+%!assert (linspace (10, 20, -1), zeros (1, 0))
+%!assert (numel (linspace (0, 1, 2+eps)), 2)
+%!assert (numel (linspace (0, 1, 2-eps)), 1)
 
 %!error linspace ()
 %!error linspace (1, 2, 3, 4)
+%!error <N must be a scalar> linspace (1, 2, [3, 4])
+%!error <must be scalars or vectors> linspace (ones (2,2), 2, 3)
+%!error <must be scalars or vectors> linspace (2, ones (2,2), 3)
+%!error <must be scalars or vectors> linspace (1, [], 3)
 */
 
 // FIXME: should accept dimensions as separate args for N-d
--- a/liboctave/array/CMatrix.cc	Wed Aug 26 16:05:49 2015 -0400
+++ b/liboctave/array/CMatrix.cc	Thu Aug 27 13:12:21 2015 -0700
@@ -3999,8 +3999,6 @@
                         octave_idx_type n)
 
 {
-  if (n < 1) n = 1;
-
   octave_idx_type m = x1.numel ();
 
   if (x2.numel () != m)
@@ -4009,11 +4007,17 @@
 
   NoAlias<ComplexMatrix> retval;
 
+  if (n < 1)
+    {
+      retval.clear (m, 0);
+      return retval;
+    }
+
   retval.clear (m, n);
   for (octave_idx_type i = 0; i < m; i++)
     retval(i, 0) = x1(i);
 
-  // The last column is not needed while using delta.
+  // The last column is unused so temporarily store delta there
   Complex *delta = &retval(0, n-1);
   for (octave_idx_type i = 0; i < m; i++)
     delta[i] = (x2(i) - x1(i)) / (n - 1.0);
--- a/liboctave/array/CRowVector.cc	Wed Aug 26 16:05:49 2015 -0400
+++ b/liboctave/array/CRowVector.cc	Thu Aug 27 13:12:21 2015 -0700
@@ -462,14 +462,19 @@
 ComplexRowVector
 linspace (const Complex& x1, const Complex& x2, octave_idx_type n)
 {
-  if (n < 1) n = 1;
+  NoAlias<ComplexRowVector> retval;
 
-  NoAlias<ComplexRowVector> retval (n);
+  if (n < 1)
+    return retval;
+  else
+    retval.clear (n);
+
+  retval(0) = x1;
 
   Complex delta = (x2 - x1) / (n - 1.0);
-  retval(0) = x1;
   for (octave_idx_type i = 1; i < n-1; i++)
     retval(i) = x1 + static_cast<double> (i)*delta;
+
   retval(n-1) = x2;
 
   return retval;
--- a/liboctave/array/dMatrix.cc	Wed Aug 26 16:05:49 2015 -0400
+++ b/liboctave/array/dMatrix.cc	Thu Aug 27 13:12:21 2015 -0700
@@ -3330,8 +3330,6 @@
                  octave_idx_type n)
 
 {
-  if (n < 1) n = 1;
-
   octave_idx_type m = x1.numel ();
 
   if (x2.numel () != m)
@@ -3340,11 +3338,17 @@
 
   NoAlias<Matrix> retval;
 
+  if (n < 1)
+    {
+      retval.clear (m, 0);
+      return retval;
+    }
+
   retval.clear (m, n);
   for (octave_idx_type i = 0; i < m; i++)
     retval(i, 0) = x1(i);
 
-  // The last column is not needed while using delta.
+  // The last column is unused so temporarily store delta there
   double *delta = &retval(0, n-1);
   for (octave_idx_type i = 0; i < m; i++)
     delta[i] = (x2(i) - x1(i)) / (n - 1);
@@ -3359,6 +3363,7 @@
   return retval;
 }
 
+
 MS_CMP_OPS (Matrix, double)
 MS_BOOL_OPS (Matrix, double)
 
--- a/liboctave/array/dRowVector.cc	Wed Aug 26 16:05:49 2015 -0400
+++ b/liboctave/array/dRowVector.cc	Thu Aug 27 13:12:21 2015 -0700
@@ -293,14 +293,19 @@
 RowVector
 linspace (double x1, double x2, octave_idx_type n)
 {
-  if (n < 1) n = 1;
+  NoAlias<RowVector> retval;
 
-  NoAlias<RowVector> retval (n);
+  if (n < 1)
+    return retval;
+  else
+    retval.clear (n);
+
+  retval(0) = x1;
 
   double delta = (x2 - x1) / (n - 1);
-  retval(0) = x1;
   for (octave_idx_type i = 1; i < n-1; i++)
     retval(i) = x1 + i*delta;
+
   retval(n-1) = x2;
 
   return retval;
--- a/liboctave/array/fCMatrix.cc	Wed Aug 26 16:05:49 2015 -0400
+++ b/liboctave/array/fCMatrix.cc	Thu Aug 27 13:12:21 2015 -0700
@@ -4019,8 +4019,6 @@
                              octave_idx_type n)
 
 {
-  if (n < 1) n = 1;
-
   octave_idx_type m = x1.numel ();
 
   if (x2.numel () != m)
@@ -4029,11 +4027,17 @@
 
   NoAlias<FloatComplexMatrix> retval;
 
+  if (n < 1)
+    {
+      retval.clear (m, 0);
+      return retval;
+    }
+
   retval.clear (m, n);
   for (octave_idx_type i = 0; i < m; i++)
     retval(i, 0) = x1(i);
 
-  // The last column is not needed while using delta.
+  // The last column is unused so temporarily store delta there
   FloatComplex *delta = &retval(0, n-1);
   for (octave_idx_type i = 0; i < m; i++)
     delta[i] = (x2(i) - x1(i)) / (n - 1.0f);
--- a/liboctave/array/fCRowVector.cc	Wed Aug 26 16:05:49 2015 -0400
+++ b/liboctave/array/fCRowVector.cc	Thu Aug 27 13:12:21 2015 -0700
@@ -463,14 +463,19 @@
 FloatComplexRowVector
 linspace (const FloatComplex& x1, const FloatComplex& x2, octave_idx_type n)
 {
-  if (n < 1) n = 1;
+  NoAlias<FloatComplexRowVector> retval;
 
-  NoAlias<FloatComplexRowVector> retval (n);
+  if (n < 1)
+    return retval;
+  else
+    retval.clear (n);
+
+  retval(0) = x1;
 
   FloatComplex delta = (x2 - x1) / (n - 1.0f);
-  retval(0) = x1;
   for (octave_idx_type i = 1; i < n-1; i++)
     retval(i) = x1 + static_cast<float> (i)*delta;
+
   retval(n-1) = x2;
 
   return retval;
--- a/liboctave/array/fMatrix.cc	Wed Aug 26 16:05:49 2015 -0400
+++ b/liboctave/array/fMatrix.cc	Thu Aug 27 13:12:21 2015 -0700
@@ -3335,8 +3335,6 @@
                       octave_idx_type n)
 
 {
-  if (n < 1) n = 1;
-
   octave_idx_type m = x1.numel ();
 
   if (x2.numel () != m)
@@ -3345,11 +3343,17 @@
 
   NoAlias<FloatMatrix> retval;
 
+  if (n < 1)
+    {
+      retval.clear (m, 0);
+      return retval;
+    }
+
   retval.clear (m, n);
   for (octave_idx_type i = 0; i < m; i++)
     retval(i, 0) = x1(i);
 
-  // The last column is not needed while using delta.
+  // The last column is unused so temporarily store delta there
   float *delta = &retval(0, n-1);
   for (octave_idx_type i = 0; i < m; i++)
     delta[i] = (x2(i) - x1(i)) / (n - 1);
--- a/liboctave/array/fRowVector.cc	Wed Aug 26 16:05:49 2015 -0400
+++ b/liboctave/array/fRowVector.cc	Thu Aug 27 13:12:21 2015 -0700
@@ -293,14 +293,19 @@
 FloatRowVector
 linspace (float x1, float x2, octave_idx_type n)
 {
-  if (n < 1) n = 1;
+  NoAlias<FloatRowVector> retval;
 
-  NoAlias<FloatRowVector> retval (n);
+  if (n < 1)
+    return retval;
+  else
+    retval.clear (n);
+
+  retval(0) = x1;
 
   float delta = (x2 - x1) / (n - 1);
-  retval(0) = x1;
   for (octave_idx_type i = 1; i < n-1; i++)
     retval(i) = x1 + i*delta;
+
   retval(n-1) = x2;
 
   return retval;