# HG changeset patch
# User David Spies <dnspies@gmail.com>
# Date 1401059613 21600
#      Sun May 25 17:13:33 2014 -0600
# Node ID 0a05ab43bf56ad3c23a811383f27aecf5c4ef281
# Parent  3c3ca5ddd4293f6ebeb93db9e583b3276b10e333
Added a dispatch function in libinterp which can be used to call the proper template instantiation of a function starting from just an octave-value.  The dispatch logic for find has been replaced with a call to this function  This also happens to fix bugs #42424 and #42425 (both of which I've added unit tests for)

diff -r 3c3ca5ddd429 -r 0a05ab43bf56 libinterp/corefcn/find.cc
--- a/libinterp/corefcn/find.cc	Sun May 25 12:20:17 2014 -0600
+++ b/libinterp/corefcn/find.cc	Sun May 25 17:13:33 2014 -0600
@@ -20,8 +20,19 @@
 
 */
 
+/*
+ * The find function should be seen as the canonical example demonstrating
+ * how to properly call dispatch.h
+ * It should always behave properly for all matrix types.
+ */
+
 #include <cassert>
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dispatch.h"
 #include "find.h"
 
 #include "defun.h"
@@ -147,46 +158,60 @@
     return resvec.get_list ();
   }
 
+struct find_info
+  {
+  octave_idx_type n_to_find;
+  int direction;
+  int nargout;
+  };
+
 /*
- * A large annoying plethora of switch statements necessary to
- * transform normal arguments into template arguments. These must
- * be template arguments in order to construct the proper res_type_of
+ * This functor will be called by dispatch.h with the proper type M.
+ * This avoids having to explicitly list the different types find can
+ * handle and instead delegates that duty to the generic "dispatch"
+ * function.
  */
 template<typename M>
-octave_value_list find_templated(const M& v, int nargout,
-		octave_idx_type n_to_find, int direction)
+struct findTemplated
+  {
+  /*
+   * A large annoying plethora of switch statements necessary to
+   * transform normal arguments into template arguments. These must
+   * be template arguments in order to construct the proper res_type_of
+   */
+  octave_value_list operator()(const M& v, const find_info& inf)
     {
-      switch (nargout)
+      switch (inf.nargout)
         {
       case 1:
-        switch (direction)
+        switch (inf.direction)
           {
         case -1:
-          return find_call<-1, 1> (v, n_to_find);
+          return find_call<-1, 1> (v, inf.n_to_find);
         case 1:
-          return find_call<1, 1> (v, n_to_find);
+          return find_call<1, 1> (v, inf.n_to_find);
         default:
           assert(false); //Direction can only be 1 or -1
           }
         break;
       case 2:
-        switch (direction)
+        switch (inf.direction)
           {
         case -1:
-          return find_call<-1, 2> (v, n_to_find);
+          return find_call<-1, 2> (v, inf.n_to_find);
         case 1:
-          return find_call<1, 2> (v, n_to_find);
+          return find_call<1, 2> (v, inf.n_to_find);
         default:
           assert(false); //Direction can only be 1 or -1
           }
         break;
       case 3:
-        switch (direction)
+        switch (inf.direction)
           {
         case -1:
-          return find_call<-1, 3> (v, n_to_find);
+          return find_call<-1, 3> (v, inf.n_to_find);
         case 1:
-          return find_call<1, 3> (v, n_to_find);
+          return find_call<1, 3> (v, inf.n_to_find);
         default:
           assert(false); //Direction can only be 1 or -1
           }
@@ -196,6 +221,7 @@
         }
       return octave_value_list ();
     }
+  };
 
 DEFUN (find, args, nargout,
        "-*- texinfo -*-\n\
@@ -261,179 +287,81 @@
 @seealso{nonzeros}\n\
 @end deftypefn")
 {
-  octave_value_list retval;
+    find_info inf;
 
-  int nargin = args.length ();
+    if(nargout < 1)
+      nargout = 1;
+    else if(nargout > 3)
+      nargout = 3;
 
-  if (nargin > 3 || nargin < 1)
-    {
-      print_usage ();
-      return retval;
-    }
+    inf.nargout = nargout;
 
-  // Setup the default options.
-  octave_idx_type n_to_find = -1;
-  if (nargin > 1)
-    {
-      double val = args(1).scalar_value ();
+    octave_value_list retval;
 
-      if (error_state || (val < 0 || (! xisinf (val) && val != xround (val))))
-        {
-          error ("find: N must be a non-negative integer");
-          return retval;
-        }
-      else if (! xisinf (val))
-        n_to_find = val;
-    }
+    int nargin = args.length ();
 
-  // Direction to do the searching (1 == forward, -1 == reverse).
-  int direction = 1;
-  if (nargin > 2)
-    {
-      direction = 0;
+    if (nargin > 3 || nargin < 1)
+      {
+        print_usage ();
+        return retval;
+      }
 
-      std::string s_arg = args(2).string_value ();
+    // Setup the default options.
+    inf.n_to_find = -1;
+    if (nargin > 1)
+      {
+        double val = args (1).scalar_value ();
 
-      if (! error_state)
-        {
-          if (s_arg == "first")
-            direction = 1;
-          else if (s_arg == "last")
-            direction = -1;
-        }
+        if (error_state || (val < 0 || (!xisinf (val) && val != xround (val))))
+          {
+            error ("find: N must be a non-negative integer");
+            return retval;
+          }
+        else if (!xisinf (val))
+        inf.n_to_find = val;
+      }
 
-      if (direction == 0)
-        {
-          error ("find: DIRECTION must be \"first\" or \"last\"");
-          return retval;
-        }
-    }
+    // Direction to do the searching (1 == forward, -1 == reverse).
+    inf.direction = 1;
+    if (nargin > 2)
+      {
+        inf.direction = 0;
 
-  octave_value arg = args(0);
+        std::string s_arg = args (2).string_value ();
 
-  if (arg.is_bool_type ())
-    {
-      if (arg.is_sparse_type ())
-        {
-          SparseBoolMatrix v = arg.sparse_bool_matrix_value ();
+        if (!error_state)
+          {
+            if (s_arg == "first")
+            inf.direction = 1;
+            else if (s_arg == "last")
+            inf.direction = -1;
+          }
 
-          if (! error_state)
-            retval = find_templated (v, nargout,
-                                            n_to_find, direction);
-        }
-      else if (nargout <= 1 && n_to_find == -1 && direction == 1)
-        {
-          // This case is equivalent to extracting indices from a logical
-          // matrix. Try to reuse the possibly cached index vector.
-          retval(0) = arg.index_vector ().unmask ();
-        }
-      else
-        {
-          boolNDArray v = arg.bool_array_value ();
+        if (inf.direction == 0)
+          {
+            error ("find: DIRECTION must be \"first\" or \"last\"");
+            return retval;
+          }
+      }
 
-          if (! error_state)
-            retval = find_templated (v, nargout,
-                                            n_to_find, direction);
-        }
-    }
-  else if (arg.is_integer_type ())
-    {
-#define DO_INT_BRANCH(INTT) \
-      else if (arg.is_ ## INTT ## _type ()) \
-        { \
-          INTT ## NDArray v = arg.INTT ## _array_value (); \
-          \
-          if (! error_state) \
-            retval = find_templated (v, nargout, \
-                                            n_to_find, direction);\
-        }
+    const octave_value& arg = args(0);
 
-      if (false)
-        ;
-      DO_INT_BRANCH (int8)
-      DO_INT_BRANCH (int16)
-      DO_INT_BRANCH (int32)
-      DO_INT_BRANCH (int64)
-      DO_INT_BRANCH (uint8)
-      DO_INT_BRANCH (uint16)
-      DO_INT_BRANCH (uint32)
-      DO_INT_BRANCH (uint64)
-      else
-        panic_impossible ();
-    }
-  else if (arg.is_sparse_type ())
-    {
-      if (arg.is_real_type ())
-        {
-          SparseMatrix v = arg.sparse_matrix_value ();
+    //For this special case, it's unnecessary to call dispatch because
+    //we already know the types of everything
+    if (arg.is_bool_type() && inf.nargout <= 1 && inf.n_to_find == -1)
+      {
+        // This case is equivalent to extracting indices from a logical
+        // matrix. Try to reuse the possibly cached index vector.
+        retval(0) = arg.index_vector ().unmask ();
+        return retval;
+      }
 
-          if (! error_state)
-            retval = find_templated (v, nargout,
-                                            n_to_find, direction);
-        }
-      else if (arg.is_complex_type ())
-        {
-          SparseComplexMatrix v = arg.sparse_complex_matrix_value ();
+    //Dispatches a call to the proper instantiation of the findTemplated
+    //functor.  This allows us to use the type of "arg" as a template
+    //argument to the find_to_iter function.
+    return dispatch<findTemplated> (arg, inf, "find");
+  }
 
-          if (! error_state)
-            retval = find_templated (v, nargout,
-                                            n_to_find, direction);
-        }
-      else
-        gripe_wrong_type_arg ("find", arg);
-    }
-  else if (arg.is_perm_matrix ())
-    {
-      PermMatrix P = arg.perm_matrix_value ();
-
-      if (! error_state)
-        retval = find_templated (P, nargout, n_to_find, direction);
-    }
-  else if (arg.is_string ())
-    {
-      charNDArray chnda = arg.char_array_value ();
-
-      if (! error_state)
-        retval = find_templated (chnda, nargout, n_to_find, direction);
-    }
-  else if (arg.is_single_type ())
-    {
-      if (arg.is_real_type ())
-        {
-          FloatNDArray nda = arg.float_array_value ();
-
-          if (! error_state)
-            retval = find_templated (nda, nargout, n_to_find,
-                                            direction);
-        }
-      else if (arg.is_complex_type ())
-        {
-          FloatComplexNDArray cnda = arg.float_complex_array_value ();
-
-          if (! error_state)
-            retval = find_templated (cnda, nargout, n_to_find,
-                                            direction);
-        }
-    }
-  else if (arg.is_real_type ())
-    {
-      NDArray nda = arg.array_value ();
-
-      if (! error_state)
-        retval = find_templated (nda, nargout, n_to_find, direction);
-    }
-  else if (arg.is_complex_type ())
-    {
-      ComplexNDArray cnda = arg.complex_array_value ();
-
-      if (! error_state)
-        retval = find_templated (cnda, nargout, n_to_find, direction);
-    }
-  else
-    gripe_wrong_type_arg ("find", arg);
-
-  return retval;
-}
 
 /*
 %!assert (find (char ([0, 97])), 2)
@@ -489,5 +417,11 @@
 %! i = find(x);
 %! assert (i == 3e09);
 
+%!test
+%! fail("[a,b,c,d,e,f] = find(speye(3));")
+
+%!test
+%! [i,j] = find(eye(1000000));
+
 %!error find ()
 */
diff -r 3c3ca5ddd429 -r 0a05ab43bf56 libinterp/dispatch.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/dispatch.h	Sun May 25 17:13:33 2014 -0600
@@ -0,0 +1,176 @@
+/*
+ * dispatch.h
+ *
+ *  Created on: Apr 25, 2014
+ *      Author: dspies
+ */
+
+#ifndef DISPATCH_H_
+#define DISPATCH_H_
+
+#include <string>
+
+#include "gripes.h"
+#include "oct-obj.h"
+
+/*
+ * This function takes a templated functor as a template-template argument
+ * and calls it back with the matrix-type corresponding to arg.
+ *
+ * This is handy when you wish to write a templated function for dealing
+ * with many different matrix types and you don't want to explicitly have
+ * to list out all the different types your function can deal with and
+ * how to call it with each one.
+ *
+ * It is expected that the functor operator() takes two arguments:
+ *
+ * 1. The value being unwrapped from arg
+ * and
+ * 2. A struct containing any other information the function needs
+ * (ie other arguments, nargin, nargout etc.)
+ *
+ * The return value is an octave_value_list containing the return value
+ * for the function
+ *
+ * In addition to the above two parameters, the dispatch function itself
+ * also takes a string argument which is the name of the function being
+ * called.  This argument is only used for error-reporting
+ *
+ * It is highly recommended you always use this function as you can be
+ * sure dispatch will throw a compiler exception if you forget to handle
+ * a particular type.
+ *
+ * For an example of how to call dispatch, see the "find" function in
+ * libinterp/corefcn/find.cc
+ */
+template<template<typename > class fun, typename Inf>
+octave_value_list dispatch(const octave_value& arg, const Inf& info,
+    const std::string& funname)
+  {
+    octave_value_list retval;
+
+    if (arg.is_bool_type ())
+      {
+        if (arg.is_sparse_type ())
+          {
+            SparseBoolMatrix v = arg.sparse_bool_matrix_value ();
+
+            if (!error_state)
+              retval = fun<SparseBoolMatrix> () (v, info);
+          }
+        else
+          {
+            boolNDArray v = arg.bool_array_value ();
+
+            if (!error_state)
+              retval = fun<boolNDArray> () (v, info);
+          }
+      }
+    else if (arg.is_integer_type ())
+      {
+#define DO_INT_BRANCH(INTT) \
+      if (arg.is_ ## INTT ## _type ()) \
+        { \
+          INTT ## NDArray v = arg.INTT ## _array_value (); \
+          \
+          if (! error_state) \
+            retval = fun<INTT ## NDArray> () (v, info);\
+        } else
+
+        DO_INT_BRANCH (int8)
+        DO_INT_BRANCH (int16)
+        DO_INT_BRANCH (int32)
+        DO_INT_BRANCH (int64)
+        DO_INT_BRANCH (uint8)
+        DO_INT_BRANCH (uint16)
+        DO_INT_BRANCH (uint32)
+        DO_INT_BRANCH (uint64)
+          panic_impossible ();
+#undef DO_INT_BRANCH
+      }
+    else if (arg.is_sparse_type ())
+      {
+        if (arg.is_real_type ())
+          {
+            SparseMatrix v = arg.sparse_matrix_value ();
+
+            if (!error_state)
+              retval = fun<SparseMatrix> () (v, info);
+          }
+        else if (arg.is_complex_type ())
+          {
+            SparseComplexMatrix v = arg.sparse_complex_matrix_value ();
+
+            if (!error_state)
+              retval = fun<SparseComplexMatrix> () (v, info);
+          }
+        else
+          gripe_wrong_type_arg (funname, arg);
+      }
+    else if (arg.is_diag_matrix ())
+      {
+        if (arg.is_real_type ())
+          {
+            DiagMatrix v = arg.diag_matrix_value ();
+            if (!error_state)
+              retval = fun<DiagMatrix> () (v, info);
+          }
+        else if (arg.is_complex_type ())
+          {
+            ComplexDiagMatrix v = arg.complex_diag_matrix_value ();
+            if (!error_state)
+              retval = fun<ComplexDiagMatrix> () (v, info);
+          }
+      }
+    else if (arg.is_perm_matrix ())
+      {
+        PermMatrix v = arg.perm_matrix_value ();
+
+        if (!error_state)
+          retval = fun<PermMatrix> () (v, info);
+      }
+    else if (arg.is_string ())
+      {
+        charNDArray v = arg.char_array_value ();
+
+        if (!error_state)
+          retval = fun<charNDArray> () (v, info);
+      }
+    else if (arg.is_single_type ())
+      {
+        if (arg.is_real_type ())
+          {
+            FloatNDArray v = arg.float_array_value ();
+
+            if (!error_state)
+              retval = fun<FloatNDArray> () (v, info);
+          }
+        else if (arg.is_complex_type ())
+          {
+            FloatComplexNDArray v = arg.float_complex_array_value ();
+
+            if (!error_state)
+              retval = fun<FloatComplexNDArray> () (v, info);
+          }
+      }
+    else if (arg.is_real_type ())
+      {
+        NDArray v = arg.array_value ();
+
+        if (!error_state)
+          retval = fun<NDArray> () (v, info);
+      }
+    else if (arg.is_complex_type ())
+      {
+        ComplexNDArray v = arg.complex_array_value ();
+
+        if (!error_state)
+          retval = fun<ComplexNDArray> () (v, info);
+      }
+    else
+      gripe_wrong_type_arg (funname, arg);
+
+    return retval;
+  }
+
+#endif /* DISPATCH_H_ */
diff -r 3c3ca5ddd429 -r 0a05ab43bf56 liboctave/util/find.h
--- a/liboctave/util/find.h	Sun May 25 12:20:17 2014 -0600
+++ b/liboctave/util/find.h	Sun May 25 17:13:33 2014 -0600
@@ -30,11 +30,7 @@
 
  */
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "quit.h"
+#include "Array.h"
 
 #include "interp-idx.h"
 #include "min-with-nnz.h"
