Mercurial > octave
diff src/corefcn/cellfun.cc @ 15039:e753177cde93
maint: Move non-dynamically linked functions from DLD-FUNCTIONS/ to corefcn/ directory
* __contourc__.cc, __dispatch__.cc, __lin_interpn__.cc, __pchip_deriv__.cc,
__qp__.cc, balance.cc, besselj.cc, betainc.cc, bsxfun.cc, cellfun.cc,
colloc.cc, conv2.cc, daspk.cc, dasrt.cc, dassl.cc, det.cc, dlmread.cc, dot.cc,
eig.cc, fft.cc, fft2.cc, fftn.cc, filter.cc, find.cc, gammainc.cc, gcd.cc,
getgrent.cc, getpwent.cc, getrusage.cc, givens.cc, hess.cc, hex2num.cc, inv.cc,
kron.cc, lookup.cc, lsode.cc, lu.cc, luinc.cc, matrix_type.cc, max.cc,
md5sum.cc, mgorth.cc, nproc.cc, pinv.cc, quad.cc, quadcc.cc, qz.cc,
rand.cc, rcond.cc, regexp.cc, schur.cc, spparms.cc, sqrtm.cc, str2double.cc,
strfind.cc, sub2ind.cc, svd.cc, syl.cc, time.cc, tril.cc, typecast.cc:
Move functions from DLD-FUNCTIONS/ to corefcn/ directory. Include "defun.h",
not "defun-dld.h". Change docstring to refer to these as "Built-in Functions".
* build-aux/mk-opts.pl: Generate options code with '#include "defun.h"'. Change
option docstrings to refer to these as "Built-in Functions".
* corefcn/module.mk: List of functions to build in corefcn/ dir.
* DLD-FUNCTIONS/config-module.awk: Update to new build system.
* DLD-FUNCTIONS/module-files: Remove functions which are now in corefcn/ directory.
* src/Makefile.am: Update to build "convenience library" in corefcn/. Octave
program now links against all other libraries + corefcn libary.
* src/find-defun-files.sh: Strip $srcdir from filename.
* src/link-deps.mk: Add REGEX and FFTW link dependencies for liboctinterp.
* type.m, which.m: Change failing tests to use 'amd', still a dynamic function,
rather than 'dot', which isn't.
author | Rik <rik@octave.org> |
---|---|
date | Fri, 27 Jul 2012 15:35:00 -0700 |
parents | src/DLD-FUNCTIONS/cellfun.cc@5ae9f0f77635 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/corefcn/cellfun.cc Fri Jul 27 15:35:00 2012 -0700 @@ -0,0 +1,2472 @@ +/* + +Copyright (C) 2005-2012 Mohamed Kamoun +Copyright (C) 2006-2012 Bill Denney +Copyright (C) 2009 Jaroslav Hajek +Copyright (C) 2010 VZLU Prague + +This file is part of Octave. + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +Octave is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with Octave; see the file COPYING. If not, see +<http://www.gnu.org/licenses/>. + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string> +#include <vector> +#include <list> +#include <memory> + +#include "caseless-str.h" +#include "lo-mappers.h" +#include "oct-locbuf.h" + +#include "Cell.h" +#include "oct-map.h" +#include "defun.h" +#include "parse.h" +#include "variables.h" +#include "ov-colon.h" +#include "unwind-prot.h" +#include "gripes.h" +#include "utils.h" + +#include "ov-class.h" +#include "ov-scalar.h" +#include "ov-float.h" +#include "ov-complex.h" +#include "ov-flt-complex.h" +#include "ov-bool.h" +#include "ov-int8.h" +#include "ov-int16.h" +#include "ov-int32.h" +#include "ov-int64.h" +#include "ov-uint8.h" +#include "ov-uint16.h" +#include "ov-uint32.h" +#include "ov-uint64.h" + +#include "ov-fcn-handle.h" + +static octave_value_list +get_output_list (octave_idx_type count, octave_idx_type nargout, + const octave_value_list& inputlist, + octave_value& func, + octave_value& error_handler) +{ + octave_value_list tmp = func.do_multi_index_op (nargout, inputlist); + + if (error_state) + { + if (error_handler.is_defined ()) + { + octave_scalar_map msg; + msg.assign ("identifier", last_error_id ()); + msg.assign ("message", last_error_message ()); + msg.assign ("index", static_cast<double> (count + static_cast<octave_idx_type>(1))); + + octave_value_list errlist = inputlist; + errlist.prepend (msg); + + buffer_error_messages--; + + error_state = 0; + + tmp = error_handler.do_multi_index_op (nargout, errlist); + + buffer_error_messages++; + + if (error_state) + tmp.clear (); + } + else + tmp.clear (); + } + + return tmp; +} + +static octave_value_list +try_cellfun_internal_ops (const octave_value_list& args, int nargin) +{ + octave_value_list retval; + + std::string name = args(0).string_value (); + + const Cell f_args = args(1).cell_value (); + + octave_idx_type k = f_args.numel (); + + if (name == "isempty") + { + boolNDArray result (f_args.dims ()); + for (octave_idx_type count = 0; count < k; count++) + result(count) = f_args.elem (count).is_empty (); + retval(0) = result; + } + else if (name == "islogical") + { + boolNDArray result (f_args.dims ()); + for (octave_idx_type count= 0; count < k; count++) + result(count) = f_args.elem (count).is_bool_type (); + retval(0) = result; + } + else if (name == "isreal") + { + boolNDArray result (f_args.dims ()); + for (octave_idx_type count= 0; count < k; count++) + result(count) = f_args.elem (count).is_real_type (); + retval(0) = result; + } + else if (name == "length") + { + NDArray result (f_args.dims ()); + for (octave_idx_type count= 0; count < k; count++) + result(count) = static_cast<double> (f_args.elem (count).length ()); + retval(0) = result; + } + else if (name == "ndims") + { + NDArray result (f_args.dims ()); + for (octave_idx_type count = 0; count < k; count++) + result(count) = static_cast<double> (f_args.elem (count).ndims ()); + retval(0) = result; + } + else if (name == "prodofsize" || name == "numel") + { + NDArray result (f_args.dims ()); + for (octave_idx_type count = 0; count < k; count++) + result(count) = static_cast<double> (f_args.elem (count).numel ()); + retval(0) = result; + } + else if (name == "size") + { + if (nargin == 3) + { + int d = args(2).nint_value () - 1; + + if (d < 0) + error ("cellfun: K must be a positive integer"); + + if (! error_state) + { + NDArray result (f_args.dims ()); + for (octave_idx_type count = 0; count < k; count++) + { + dim_vector dv = f_args.elem (count).dims (); + if (d < dv.length ()) + result(count) = static_cast<double> (dv(d)); + else + result(count) = 1.0; + } + retval(0) = result; + } + } + else + error ("cellfun: not enough arguments for \"size\""); + } + else if (name == "isclass") + { + if (nargin == 3) + { + std::string class_name = args(2).string_value (); + boolNDArray result (f_args.dims ()); + for (octave_idx_type count = 0; count < k; count++) + result(count) = (f_args.elem (count).class_name () == class_name); + + retval(0) = result; + } + else + error ("cellfun: not enough arguments for \"isclass\""); + } + + return retval; +} + +static void +get_mapper_fun_options (const octave_value_list& args, int& nargin, + bool& uniform_output, octave_value& error_handler) +{ + while (nargin > 3 && args(nargin-2).is_string ()) + { + caseless_str arg = args(nargin-2).string_value (); + + size_t compare_len = std::max (arg.length (), static_cast<size_t> (2)); + + if (arg.compare ("uniformoutput", compare_len)) + uniform_output = args(nargin-1).bool_value (); + else if (arg.compare ("errorhandler", compare_len)) + { + if (args(nargin-1).is_function_handle () + || args(nargin-1).is_inline_function ()) + { + error_handler = args(nargin-1); + } + else if (args(nargin-1).is_string ()) + { + std::string err_name = args(nargin-1).string_value (); + + error_handler = symbol_table::find_function (err_name); + + if (error_handler.is_undefined ()) + { + error ("cellfun: invalid function NAME: %s", + err_name.c_str ()); + break; + } + } + else + { + error ("cellfun: invalid value for 'ErrorHandler' function"); + break; + } + } + else + { + error ("cellfun: unrecognized parameter %s", + arg.c_str ()); + break; + } + + nargin -= 2; + } + + nargin -= 1; +} + +DEFUN (cellfun, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} cellfun (@var{name}, @var{C})\n\ +@deftypefnx {Built-in Function} {} cellfun (\"size\", @var{C}, @var{k})\n\ +@deftypefnx {Built-in Function} {} cellfun (\"isclass\", @var{C}, @var{class})\n\ +@deftypefnx {Built-in Function} {} cellfun (@var{func}, @var{C})\n\ +@deftypefnx {Built-in Function} {} cellfun (@var{func}, @var{C}, @var{D})\n\ +@deftypefnx {Built-in Function} {[@var{a}, @dots{}] =} cellfun (@dots{})\n\ +@deftypefnx {Built-in Function} {} cellfun (@dots{}, \"ErrorHandler\", @var{errfunc})\n\ +@deftypefnx {Built-in Function} {} cellfun (@dots{}, \"UniformOutput\", @var{val})\n\ +\n\ +Evaluate the function named @var{name} on the elements of the cell array\n\ +@var{C}. Elements in @var{C} are passed on to the named function\n\ +individually. The function @var{name} can be one of the functions\n\ +\n\ +@table @code\n\ +@item isempty\n\ +Return 1 for empty elements.\n\ +\n\ +@item islogical\n\ +Return 1 for logical elements.\n\ +\n\ +@item isreal\n\ +Return 1 for real elements.\n\ +\n\ +@item length\n\ +Return a vector of the lengths of cell elements.\n\ +\n\ +@item ndims\n\ +Return the number of dimensions of each element.\n\ +\n\ +@item numel\n\ +@itemx prodofsize\n\ +Return the number of elements contained within each cell element. The\n\ +number is the product of the dimensions of the object at each cell element.\n\ +\n\ +@item size\n\ +Return the size along the @var{k}-th dimension.\n\ +\n\ +@item isclass\n\ +Return 1 for elements of @var{class}.\n\ +@end table\n\ +\n\ +Additionally, @code{cellfun} accepts an arbitrary function @var{func}\n\ +in the form of an inline function, function handle, or the name of a\n\ +function (in a character string). In the case of a character string\n\ +argument, the function must accept a single argument named @var{x}, and\n\ +it must return a string value. The function can take one or more arguments,\n\ +with the inputs arguments given by @var{C}, @var{D}, etc. Equally the\n\ +function can return one or more output arguments. For example:\n\ +\n\ +@example\n\ +@group\n\ +cellfun (\"atan2\", @{1, 0@}, @{0, 1@})\n\ + @result{} [ 1.57080 0.00000 ]\n\ +@end group\n\ +@end example\n\ +\n\ +The number of output arguments of @code{cellfun} matches the number of output\n\ +arguments of the function. The outputs of the function will be collected\n\ +into the output arguments of @code{cellfun} like this:\n\ +\n\ +@example\n\ +@group\n\ +function [a, b] = twoouts (x)\n\ + a = x;\n\ + b = x*x;\n\ +endfunction\n\ +[aa, bb] = cellfun (@@twoouts, @{1, 2, 3@})\n\ + @result{}\n\ + aa =\n\ + 1 2 3\n\ + bb =\n\ + 1 4 9\n\ +@end group\n\ +@end example\n\ +\n\ +Note that per default the output argument(s) are arrays of the same size as\n\ +the input arguments. Input arguments that are singleton (1x1) cells will be\n\ +automatically expanded to the size of the other arguments.\n\ +\n\ +If the parameter \"UniformOutput\" is set to true (the default), then the\n\ +function must return scalars which will be concatenated into the return\n\ +array(s). If \"UniformOutput\" is false, the outputs are concatenated into a\n\ +cell array (or cell arrays). For example:\n\ +\n\ +@example\n\ +@group\n\ +cellfun (\"tolower\", @{\"Foo\", \"Bar\", \"FooBar\"@},\n\ + \"UniformOutput\", false)\n\ +@result{} @{\"foo\", \"bar\", \"foobar\"@}\n\ +@end group\n\ +@end example\n\ +\n\ +Given the parameter \"ErrorHandler\", then @var{errfunc} defines a function\n\ +to call in case @var{func} generates an error. The form of the function is\n\ +\n\ +@example\n\ +function [@dots{}] = errfunc (@var{s}, @dots{})\n\ +@end example\n\ +\n\ +@noindent\n\ +where there is an additional input argument to @var{errfunc} relative to\n\ +@var{func}, given by @var{s}. This is a structure with the elements\n\ +'identifier', 'message' and 'index', giving respectively the error\n\ +identifier, the error message, and the index into the input arguments\n\ +of the element that caused the error. For example:\n\ +\n\ +@example\n\ +@group\n\ +function y = foo (s, x), y = NaN; endfunction\n\ +cellfun (\"factorial\", @{-1,2@}, \"ErrorHandler\", @@foo)\n\ +@result{} [NaN 2]\n\ +@end group\n\ +@end example\n\ +\n\ +Use @code{cellfun} intelligently. The @code{cellfun} function is a\n\ +useful tool for avoiding loops. It is often used with anonymous\n\ +function handles; however, calling an anonymous function involves an\n\ +overhead quite comparable to the overhead of an m-file function.\n\ +Passing a handle to a built-in function is faster, because the\n\ +interpreter is not involved in the internal loop. For example:\n\ +\n\ +@example\n\ +@group\n\ +a = @{@dots{}@}\n\ +v = cellfun (@@(x) det (x), a); # compute determinants\n\ +v = cellfun (@@det, a); # faster\n\ +@end group\n\ +@end example\n\ +\n\ +@seealso{arrayfun, structfun, spfun}\n\ +@end deftypefn") +{ + octave_value_list retval; + int nargin = args.length (); + int nargout1 = (nargout < 1 ? 1 : nargout); + + if (nargin < 2) + { + error ("cellfun: function requires at least 2 arguments"); + print_usage (); + return retval; + } + + octave_value func = args(0); + + if (! args(1).is_cell ()) + { + error ("cellfun: C must be a cell array"); + + return retval; + } + + if (func.is_string ()) + { + retval = try_cellfun_internal_ops (args, nargin); + + if (error_state || ! retval.empty ()) + return retval; + + // See if we can convert the string into a function. + + std::string name = args(0).string_value (); + + if (! valid_identifier (name)) + { + std::string fcn_name = unique_symbol_name ("__cellfun_fcn_"); + std::string fname = "function y = " + fcn_name + "(x) y = "; + + octave_function *ptr_func + = extract_function (args(0), "cellfun", fcn_name, + fname, "; endfunction"); + + if (ptr_func && ! error_state) + func = octave_value (ptr_func, true); + } + else + { + func = symbol_table::find_function (name); + + if (func.is_undefined ()) + error ("cellfun: invalid function NAME: %s", name.c_str ()); + } + + if (error_state || ! retval.empty ()) + return retval; + } + + if (func.is_function_handle () || func.is_inline_function () + || func.is_function ()) + { + + // The following is an optimisation because the symbol table can + // give a more specific function class, so this can result in + // fewer polymorphic function calls as the function gets called + // for each value of the array. + { + if (func.is_function_handle ()) + { + octave_fcn_handle* f = func.fcn_handle_value (); + + // Overloaded function handles need to check the type of the + // arguments for each element of the array, so they cannot + // be optimised this way. + if (f -> is_overloaded ()) + goto nevermind; + } + + std::string name = func.function_value () -> name (); + octave_value f = symbol_table::find_function (name); + + if (f.is_defined ()) + { + //Except for these two which are special cases... + if (name != "size" && name != "class") + { + //Try first the optimised code path for built-in functions + octave_value_list tmp_args = args; + tmp_args(0) = name; + retval = try_cellfun_internal_ops (tmp_args, nargin); + if (error_state || ! retval.empty ()) + return retval; + } + + //Okay, we tried, doesn't work, let's do the best we can + //instead and avoid polymorphic calls for each element of + //the array. + func = f; + } + } + nevermind: + + bool uniform_output = true; + octave_value error_handler; + + get_mapper_fun_options (args, nargin, uniform_output, error_handler); + + if (error_state) + return octave_value_list (); + + // Extract cell arguments. + + octave_value_list inputlist (nargin, octave_value ()); + + OCTAVE_LOCAL_BUFFER (Cell, inputs, nargin); + OCTAVE_LOCAL_BUFFER (bool, mask, nargin); + + // This is to prevent copy-on-write. + const Cell *cinputs = inputs; + + octave_idx_type k = 1; + + dim_vector fdims (1, 1); + + // Collect arguments. Pre-fill scalar elements of inputlist + // array. + + for (int j = 0; j < nargin; j++) + { + if (! args(j+1).is_cell ()) + { + error ("cellfun: arguments must be cells"); + return octave_value_list (); + } + + inputs[j] = args(j+1).cell_value (); + mask[j] = inputs[j].numel () != 1; + if (! mask[j]) + inputlist(j) = cinputs[j](0); + } + + for (int j = 0; j < nargin; j++) + { + if (mask[j]) + { + fdims = inputs[j].dims (); + k = inputs[j].numel (); + for (int i = j+1; i < nargin; i++) + { + if (mask[i] && inputs[i].dims () != fdims) + { + error ("cellfun: dimensions mismatch"); + return octave_value_list (); + } + } + break; + } + } + + unwind_protect frame; + frame.protect_var (buffer_error_messages); + + if (error_handler.is_defined ()) + buffer_error_messages++; + + // Apply functions. + + if (uniform_output) + { + std::list<octave_value_list> idx_list (1); + idx_list.front ().resize (1); + std::string idx_type = "("; + + OCTAVE_LOCAL_BUFFER (octave_value, retv, nargout1); + + for (octave_idx_type count = 0; count < k; count++) + { + for (int j = 0; j < nargin; j++) + { + if (mask[j]) + inputlist.xelem (j) = cinputs[j](count); + } + + const octave_value_list tmp + = get_output_list (count, nargout, inputlist, func, + error_handler); + + if (error_state) + return retval; + + if (nargout > 0 && tmp.length () < nargout) + { + error ("cellfun: function returned fewer than nargout values"); + return retval; + } + + if (nargout > 0 + || (nargout == 0 + && tmp.length () > 0 && tmp(0).is_defined ())) + { + int num_to_copy = tmp.length (); + + if (num_to_copy > nargout1) + num_to_copy = nargout1; + + if (count == 0) + { + for (int j = 0; j < num_to_copy; j++) + { + if (tmp(j).is_defined ()) + { + octave_value val = tmp(j); + + if (val.numel () == 1) + retv[j] = val.resize (fdims); + else + { + error ("cellfun: all values must be scalars when UniformOutput = true"); + break; + } + } + } + } + else + { + for (int j = 0; j < num_to_copy; j++) + { + if (tmp(j).is_defined ()) + { + octave_value val = tmp(j); + + if (! retv[j].fast_elem_insert (count, val)) + { + if (val.numel () == 1) + { + idx_list.front ()(0) = count + 1.0; + retv[j].assign (octave_value::op_asn_eq, + idx_type, idx_list, val); + + if (error_state) + break; + } + else + { + error ("cellfun: all values must be scalars when UniformOutput = true"); + break; + } + } + } + } + } + } + + if (error_state) + break; + } + + retval.resize (nargout1); + + for (int j = 0; j < nargout1; j++) + { + if (nargout > 0 && retv[j].is_undefined ()) + retval(j) = NDArray (fdims); + else + retval(j) = retv[j]; + } + } + else + { + OCTAVE_LOCAL_BUFFER (Cell, results, nargout1); + + for (int j = 0; j < nargout1; j++) + results[j].resize (fdims, Matrix ()); + + bool have_some_output = false; + + for (octave_idx_type count = 0; count < k; count++) + { + for (int j = 0; j < nargin; j++) + { + if (mask[j]) + inputlist.xelem (j) = cinputs[j](count); + } + + const octave_value_list tmp + = get_output_list (count, nargout, inputlist, func, + error_handler); + + if (error_state) + return retval; + + if (nargout > 0 && tmp.length () < nargout) + { + error ("cellfun: function returned fewer than nargout values"); + return retval; + } + + if (nargout > 0 + || (nargout == 0 + && tmp.length () > 0 && tmp(0).is_defined ())) + { + int num_to_copy = tmp.length (); + + if (num_to_copy > nargout1) + num_to_copy = nargout1; + + if (num_to_copy > 0) + have_some_output = true; + + for (int j = 0; j < num_to_copy; j++) + results[j](count) = tmp(j); + } + } + + if (have_some_output || fdims.any_zero ()) + { + retval.resize (nargout1); + + for (int j = 0; j < nargout1; j++) + retval(j) = results[j]; + } + } + } + else + error ("cellfun: argument NAME must be a string or function handle"); + + return retval; +} + +/* + +%!function r = __f11 (x) +%! global __cellfun_test_num_outputs__; +%! __cellfun_test_num_outputs__ = nargout; +%! r = x; +%!endfunction + +%!function __f01 (x) +%! global __cellfun_test_num_outputs__; +%! __cellfun_test_num_outputs__ = nargout; +%!endfunction + +%!test +%! global __cellfun_test_num_outputs__; +%! cellfun (@__f11, {1}); +%! assert (__cellfun_test_num_outputs__, 0); +%! x = cellfun (@__f11, {1}); +%! assert (__cellfun_test_num_outputs__, 1); + +%!test +%! global __cellfun_test_num_outputs__; +%! cellfun (@__f01, {1}); +%! assert (__cellfun_test_num_outputs__, 0); + +%!error x = cellfun (@__f01, {1, 2}); + +%!test +%! assert (cellfun (@__f11, {1, 2}), [1, 2]); +%! assert (cellfun (@__f11, {1, 2}, 'uniformoutput', false), {1, 2}); + +%!test +%! [a,b] = cellfun (@(x) x, cell (2, 0)); +%! assert (a, zeros (2, 0)); +%! assert (b, zeros (2, 0)); + +%!test +%! [a,b] = cellfun (@(x) x, cell (2, 0), "uniformoutput", false); +%! assert (a, cell (2, 0)); +%! assert (b, cell (2, 0)); + +%% Test function to check the "Errorhandler" option +%!function z = __cellfunerror (S, varargin) +%! z = S; +%!endfunction + +%% First input argument can be a string, an inline function, +%% a function_handle or an anonymous function +%!test +%! A = cellfun ("islogical", {true, 0.1, false, i*2}); +%! assert (A, [true, false, true, false]); +%!test +%! A = cellfun (inline ("islogical (x)", "x"), {true, 0.1, false, i*2}); +%! assert (A, [true, false, true, false]); +%!test +%! A = cellfun (@islogical, {true, 0.1, false, i*2}); +%! assert (A, [true, false, true, false]); +%!test +%! A = cellfun (@(x) islogical (x), {true, 0.1, false, i*2}); +%! assert (A, [true, false, true, false]); + +%% First input argument can be the special string "isreal", +%% "isempty", "islogical", "length", "ndims" or "prodofsize" +%!test +%! A = cellfun ("isreal", {true, 0.1, {}, i*2, [], "abc"}); +%! assert (A, [true, true, false, false, true, true]); +%!test +%! A = cellfun ("isempty", {true, 0.1, false, i*2, [], "abc"}); +%! assert (A, [false, false, false, false, true, false]); +%!test +%! A = cellfun ("islogical", {true, 0.1, false, i*2, [], "abc"}); +%! assert (A, [true, false, true, false, false, false]); +%!test +%! A = cellfun ("length", {true, 0.1, false, i*2, [], "abc"}); +%! assert (A, [1, 1, 1, 1, 0, 3]); +%!test +%! A = cellfun ("ndims", {[1, 2; 3, 4]; (cell (1,2,3,4))}); +%! assert (A, [2; 4]); +%!test +%! A = cellfun ("prodofsize", {[1, 2; 3, 4], (cell (1,2,3,4))}); +%! assert (A, [4, 24]); + +%% Number of input and output arguments may not be limited to one +%!test +%! A = cellfun (@(x,y,z) x + y + z, {1, 1, 1}, {2, 2, 2}, {3, 4, 5}); +%! assert (A, [6, 7, 8]); +%!test +%! A = cellfun (@(x,y,z) x + y + z, {1, 1, 1}, {2, 2, 2}, {3, 4, 5}, \ +%! "UniformOutput", false); +%! assert (A, {6, 7, 8}); +%!test %% Two input arguments of different types +%! A = cellfun (@(x,y) islogical (x) && ischar (y), {false, true}, {"a", 3}); +%! assert (A, [true, false]); +%!test %% Pass another variable to the anonymous function +%! y = true; +%! A = cellfun (@(x) islogical (x) && y, {false, 0.3}); +%! assert (A, [true, false]); +%!test %% Three ouptut arguments of different type +%! [A, B, C] = cellfun (@find, {10, 11; 0, 12}, "UniformOutput", false); +%! assert (isequal (A, {true, true; [], true})); +%! assert (isequal (B, {true, true; [], true})); +%! assert (isequal (C, {10, 11; [], 12})); + +%% Input arguments can be of type cell array of logical +%!test +%! A = cellfun (@(x,y) x == y, {false, true}, {true, true}); +%! assert (A, [false, true]); +%!test +%! A = cellfun (@(x,y) x == y, {false; true}, {true; true}, \ +%! "UniformOutput", true); +%! assert (A, [false; true]); +%!test +%! A = cellfun (@(x) x, {false, true; false, true}, "UniformOutput", false); +%! assert (A, {false, true; false, true}); +%!test %% Three ouptut arguments of same type +%! [A, B, C] = cellfun (@find, {true, false; false, true}, \ +%! "UniformOutput", false); +%! assert (isequal (A, {true, []; [], true})); +%! assert (isequal (B, {true, []; [], true})); +%! assert (isequal (C, {true, []; [], true})); +%!test +%! A = cellfun (@(x,y) cell2str (x,y), {true}, {true}, \ +%! "ErrorHandler", @__cellfunerror); +%! assert (isfield (A, "identifier"), true); +%! assert (isfield (A, "message"), true); +%! assert (isfield (A, "index"), true); +%! assert (isempty (A.message), false); +%! assert (A.index, 1); +%!test %% Overwriting setting of "UniformOutput" true +%! A = cellfun (@(x,y) cell2str (x,y), {true}, {true}, \ +%! "UniformOutput", true, "ErrorHandler", @__cellfunerror); +%! assert (isfield (A, "identifier"), true); +%! assert (isfield (A, "message"), true); +%! assert (isfield (A, "index"), true); +%! assert (isempty (A.message), false); +%! assert (A.index, 1); + +%% Input arguments can be of type cell array of numeric +%!test +%! A = cellfun (@(x,y) x>y, {1.1, 4.2}, {3.1, 2+3*i}); +%! assert (A, [false, true]); +%!test +%! A = cellfun (@(x,y) x>y, {1.1, 4.2; 2, 4}, {3.1, 2; 2, 4+2*i}, \ +%! "UniformOutput", true); +%! assert (A, [false, true; false, false]); +%!test +%! A = cellfun (@(x,y) x:y, {1.1, 4}, {3.1, 6}, "UniformOutput", false); +%! assert (isequal (A{1}, [1.1, 2.1, 3.1])); +%! assert (isequal (A{2}, [4, 5, 6])); +%!test %% Three ouptut arguments of different type +%! [A, B, C] = cellfun (@find, {10, 11; 0, 12}, "UniformOutput", false); +%! assert (isequal (A, {true, true; [], true})); +%! assert (isequal (B, {true, true; [], true})); +%! assert (isequal (C, {10, 11; [], 12})); +%!test +%! A = cellfun (@(x,y) cell2str (x,y), {1.1, 4}, {3.1, 6}, \ +%! "ErrorHandler", @__cellfunerror); +%! B = isfield (A(1), "message") && isfield (A(1), "index"); +%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]); +%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]); +%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]); +%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]); +%! assert ([A(1).index, A(2).index], [1, 2]); +%!test %% Overwriting setting of "UniformOutput" true +%! A = cellfun (@(x,y) cell2str (x,y), {1.1, 4}, {3.1, 6}, \ +%! "UniformOutput", true, "ErrorHandler", @__cellfunerror); +%! B = isfield (A(1), "message") && isfield (A(1), "index"); +%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]); +%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]); +%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]); +%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]); +%! assert ([A(1).index, A(2).index], [1, 2]); + +%% Input arguments can be of type cell arrays of character or strings +%!error %% "UniformOutput" false should be used +%! A = cellfun (@(x,y) x>y, {"ad", "c", "ghi"}, {"cc", "d", "fgh"}); +%!test +%! A = cellfun (@(x,y) x>y, {"a"; "f"}, {"c"; "d"}, "UniformOutput", true); +%! assert (A, [false; true]); +%!test +%! A = cellfun (@(x,y) x:y, {"a", "d"}, {"c", "f"}, "UniformOutput", false); +%! assert (A, {"abc", "def"}); +%!test +%! A = cellfun (@(x,y) cell2str (x,y), {"a", "d"}, {"c", "f"}, \ +%! "ErrorHandler", @__cellfunerror); +%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]); +%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]); +%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]); +%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]); +%! assert ([A(1).index, A(2).index], [1, 2]); +%!test %% Overwriting setting of "UniformOutput" true +%! A = cellfun (@(x,y) cell2str (x,y), {"a", "d"}, {"c", "f"}, \ +%! "UniformOutput", true, "ErrorHandler", @__cellfunerror); +%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]); +%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]); +%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]); +%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]); +%! assert ([A(1).index, A(2).index], [1, 2]); + +%% Structures cannot be handled by cellfun +%!error +%! vst1.a = 1.1; vst1.b = 4.2; vst2.a = 3.1; vst2.b = 2; +%! A = cellfun (@(x,y) (x.a < y.a) && (x.b > y.b), vst1, vst2); + +%% Input arguments can be of type cell array of cell arrays +%!test +%! A = cellfun (@(x,y) x{1} < y{1}, {{1.1}, {4.2}}, {{3.1}, {2}}); +%! assert (A, [1, 0], 1e-16); +%!test +%! A = cellfun (@(x,y) x{1} < y{1}, {{1.1}; {4.2}}, {{3.1}; {2}}, \ +%! "UniformOutput", true); +%! assert (A, [1; 0], 1e-16); +%!test +%! A = cellfun (@(x,y) x{1} < y{1}, {{1.1}, {4.2}}, {{3.1}, {2}}, \ +%! "UniformOutput", false); +%! assert (A, {true, false}); +%!test +%! A = cellfun (@(x,y) mat2str (x,y), {{1.1}, {4.2}}, {{3.1}, {2}}, \ +%! "ErrorHandler", @__cellfunerror); +%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]); +%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]); +%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]); +%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]); +%! assert ([A(1).index, A(2).index], [1, 2]); +%!test %% Overwriting setting of "UniformOutput" true +%! A = cellfun (@(x,y) mat2str (x,y), {{1.1}, {4.2}}, {{3.1}, {2}}, \ +%! "UniformOutput", true, "ErrorHandler", @__cellfunerror); +%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]); +%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]); +%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]); +%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]); +%! assert ([A(1).index, A(2).index], [1, 2]); + +%% Input arguments can be of type cell array of structure arrays +%!test +%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3); +%! A = cellfun (@(x,y) (x.a == y.a) && (x.b < y.b), {a}, {b}); +%! assert (A, true); +%!test +%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3); +%! A = cellfun (@(x,y) (x.a == y.a) && (x.b < y.b) , {a}, {b}, \ +%! "UniformOutput", true); +%! assert (A, true); +%!test +%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3); +%! A = cellfun (@(x,y) (x.a == y.a) && (x.b < y.b) , {a}, {b}, \ +%! "UniformOutput", false); +%! assert (A, {true}); +%!test +%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3); +%! A = cellfun (@(x,y) cell2str (x.a, y.a), {a}, {b}, \ +%! "ErrorHandler", @__cellfunerror); +%! assert (isfield (A, "identifier"), true); +%! assert (isfield (A, "message"), true); +%! assert (isfield (A, "index"), true); +%! assert (isempty (A.message), false); +%! assert (A.index, 1); +%!test %% Overwriting setting of "UniformOutput" true +%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3); +%! A = cellfun (@(x,y) cell2str (x.a, y.a), {a}, {b}, \ +%! "UniformOutput", true, "ErrorHandler", @__cellfunerror); +%! assert (isfield (A, "identifier"), true); +%! assert (isfield (A, "message"), true); +%! assert (isfield (A, "index"), true); +%! assert (isempty (A.message), false); +%! assert (A.index, 1); + +%% A lot of other tests +%!assert (cellfun (@sin, {0,1}), sin ([0,1])) +%!assert (cellfun (inline ("sin (x)"), {0,1}), sin ([0,1])) +%!assert (cellfun ("sin", {0,1}), sin ([0,1])) +%!assert (cellfun ("isempty", {1,[]}), [false,true]) +%!assert (cellfun ("islogical", {false,pi}), [true,false]) +%!assert (cellfun ("isreal", {1i,1}), [false,true]) +%!assert (cellfun ("length", {zeros(2,2),1}), [2,1]) +%!assert (cellfun ("prodofsize", {zeros(2,2),1}), [4,1]) +%!assert (cellfun ("ndims", {zeros([2,2,2]),1}), [3,2]) +%!assert (cellfun ("isclass", {zeros([2,2,2]),"test"}, "double"), [true,false]) +%!assert (cellfun ("size", {zeros([1,2,3]),1}, 1), [1,1]) +%!assert (cellfun ("size", {zeros([1,2,3]),1}, 2), [2,1]) +%!assert (cellfun ("size", {zeros([1,2,3]),1}, 3), [3,1]) +%!assert (cellfun (@atan2, {1,1}, {1,2}), [atan2(1,1), atan2(1,2)]) +%!assert (cellfun (@atan2, {1,1}, {1,2},"UniformOutput", false), {atan2(1,1), atan2(1,2)}) +%!assert (cellfun (@sin, {1,2;3,4}), sin ([1,2;3,4])) +%!assert (cellfun (@atan2, {1,1;1,1}, {1,2;1,2}), atan2 ([1,1;1,1],[1,2;1,2])) +%!error cellfun (@factorial, {-1,3}) +%!assert (cellfun (@factorial,{-1,3},"ErrorHandler",@(x,y) NaN), [NaN,6]) +%!test +%! [a,b,c] = cellfun (@fileparts, {fullfile("a","b","c.d"), fullfile("e","f","g.h")}, "UniformOutput", false); +%! assert (a, {fullfile("a","b"), fullfile("e","f")}); +%! assert (b, {"c", "g"}); +%! assert (c, {".d", ".h"}); + +%!error cellfun (1) +%!error cellfun ("isclass", 1) +%!error cellfun ("size", 1) +%!error cellfun (@sin, {[]}, "BadParam", false) +%!error cellfun (@sin, {[]}, "UniformOuput") +%!error cellfun (@sin, {[]}, "ErrorHandler") +*/ + +// Arrayfun was originally a .m file written by Bill Denney and Jaroslav +// Hajek. It was converted to C++ by jwe so that it could properly +// handle the nargout = 0 case. + +DEFUN (arrayfun, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Function File} {} arrayfun (@var{func}, @var{A})\n\ +@deftypefnx {Function File} {@var{x} =} arrayfun (@var{func}, @var{A})\n\ +@deftypefnx {Function File} {@var{x} =} arrayfun (@var{func}, @var{A}, @var{b}, @dots{})\n\ +@deftypefnx {Function File} {[@var{x}, @var{y}, @dots{}] =} arrayfun (@var{func}, @var{A}, @dots{})\n\ +@deftypefnx {Function File} {} arrayfun (@dots{}, \"UniformOutput\", @var{val})\n\ +@deftypefnx {Function File} {} arrayfun (@dots{}, \"ErrorHandler\", @var{errfunc})\n\ +\n\ +Execute a function on each element of an array. This is useful for\n\ +functions that do not accept array arguments. If the function does\n\ +accept array arguments it is better to call the function directly.\n\ +\n\ +The first input argument @var{func} can be a string, a function\n\ +handle, an inline function, or an anonymous function. The input\n\ +argument @var{A} can be a logic array, a numeric array, a string\n\ +array, a structure array, or a cell array. By a call of the function\n\ +@command{arrayfun} all elements of @var{A} are passed on to the named\n\ +function @var{func} individually.\n\ +\n\ +The named function can also take more than two input arguments, with\n\ +the input arguments given as third input argument @var{b}, fourth\n\ +input argument @var{c}, @dots{} If given more than one array input\n\ +argument then all input arguments must have the same sizes, for\n\ +example:\n\ +\n\ +@example\n\ +@group\n\ +arrayfun (@@atan2, [1, 0], [0, 1])\n\ + @result{} [ 1.5708 0.0000 ]\n\ +@end group\n\ +@end example\n\ +\n\ +If the parameter @var{val} after a further string input argument\n\ +\"UniformOutput\" is set @code{true} (the default), then the named\n\ +function @var{func} must return a single element which then will be\n\ +concatenated into the return value and is of type matrix. Otherwise,\n\ +if that parameter is set to @code{false}, then the outputs are\n\ +concatenated in a cell array. For example:\n\ +\n\ +@example\n\ +@group\n\ +arrayfun (@@(x,y) x:y, \"abc\", \"def\", \"UniformOutput\", false)\n\ +@result{}\n\ + @{\n\ + [1,1] = abcd\n\ + [1,2] = bcde\n\ + [1,3] = cdef\n\ + @}\n\ +@end group\n\ +@end example\n\ +\n\ +If more than one output arguments are given then the named function\n\ +must return the number of return values that also are expected, for\n\ +example:\n\ +\n\ +@example\n\ +@group\n\ +[A, B, C] = arrayfun (@@find, [10; 0], \"UniformOutput\", false)\n\ +@result{}\n\ +A =\n\ +@{\n\ + [1,1] = 1\n\ + [2,1] = [](0x0)\n\ +@}\n\ +B =\n\ +@{\n\ + [1,1] = 1\n\ + [2,1] = [](0x0)\n\ +@}\n\ +C =\n\ +@{\n\ + [1,1] = 10\n\ + [2,1] = [](0x0)\n\ +@}\n\ +@end group\n\ +@end example\n\ +\n\ +If the parameter @var{errfunc} after a further string input argument\n\ +\"ErrorHandler\" is another string, a function handle, an inline\n\ +function, or an anonymous function, then @var{errfunc} defines a\n\ +function to call in the case that @var{func} generates an error.\n\ +The definition of the function must be of the form\n\ +\n\ +@example\n\ +function [@dots{}] = errfunc (@var{s}, @dots{})\n\ +@end example\n\ +\n\ +@noindent\n\ +where there is an additional input argument to @var{errfunc}\n\ +relative to @var{func}, given by @var{s}. This is a structure with\n\ +the elements \"identifier\", \"message\", and \"index\" giving,\n\ +respectively, the error identifier, the error message, and the index of\n\ +the array elements that caused the error. The size of the output\n\ +argument of @var{errfunc} must have the same size as the output\n\ +argument of @var{func}, otherwise a real error is thrown. For\n\ +example:\n\ +\n\ +@example\n\ +@group\n\ +function y = ferr (s, x), y = \"MyString\"; endfunction\n\ +arrayfun (@@str2num, [1234],\n\ + \"UniformOutput\", false, \"ErrorHandler\", @@ferr)\n\ +@result{}\n\ + @{\n\ + [1,1] = MyString\n\ + @}\n\ +@end group\n\ +@end example\n\ +\n\ +@seealso{spfun, cellfun, structfun}\n\ +@end deftypefn") +{ + octave_value_list retval; + int nargin = args.length (); + int nargout1 = (nargout < 1 ? 1 : nargout); + + if (nargin < 2) + { + error ("arrayfun: function requires at least 2 arguments"); + print_usage (); + return retval; + } + + octave_value func = args(0); + bool symbol_table_lookup = false; + + if (func.is_string ()) + { + // See if we can convert the string into a function. + + std::string name = args(0).string_value (); + + if (! valid_identifier (name)) + { + std::string fcn_name = unique_symbol_name ("__arrayfun_fcn_"); + std::string fname = "function y = " + fcn_name + "(x) y = "; + + octave_function *ptr_func + = extract_function (args(0), "arrayfun", fcn_name, + fname, "; endfunction"); + + if (ptr_func && ! error_state) + func = octave_value (ptr_func, true); + } + else + { + func = symbol_table::find_function (name); + + if (func.is_undefined ()) + error ("arrayfun: invalid function NAME: %s", name.c_str ()); + + symbol_table_lookup = true; + } + + if (error_state) + return retval; + } + + if (func.is_function_handle () || func.is_inline_function () + || func.is_function ()) + { + // The following is an optimisation because the symbol table can + // give a more specific function class, so this can result in + // fewer polymorphic function calls as the function gets called + // for each value of the array. + + if (! symbol_table_lookup ) + { + if (func.is_function_handle ()) + { + octave_fcn_handle* f = func.fcn_handle_value (); + + // Overloaded function handles need to check the type of + // the arguments for each element of the array, so they + // cannot be optimised this way. + + if (f -> is_overloaded ()) + goto nevermind; + } + octave_value f = symbol_table::find_function (func.function_value () + -> name ()); + if (f.is_defined ()) + func = f; + } + + nevermind: + + bool uniform_output = true; + octave_value error_handler; + + get_mapper_fun_options (args, nargin, uniform_output, error_handler); + + if (error_state) + return octave_value_list (); + + octave_value_list inputlist (nargin, octave_value ()); + + OCTAVE_LOCAL_BUFFER (octave_value, inputs, nargin); + OCTAVE_LOCAL_BUFFER (bool, mask, nargin); + + octave_idx_type k = 1; + + dim_vector fdims (1, 1); + + // Collect arguments. Pre-fill scalar elements of inputlist + // array. + + for (int j = 0; j < nargin; j++) + { + inputs[j] = args(j+1); + mask[j] = inputs[j].numel () != 1; + + if (! mask[j]) + inputlist(j) = inputs[j]; + } + + for (int j = 0; j < nargin; j++) + { + if (mask[j]) + { + fdims = inputs[j].dims (); + k = inputs[j].numel (); + + for (int i = j+1; i < nargin; i++) + { + if (mask[i] && inputs[i].dims () != fdims) + { + error ("arrayfun: dimensions mismatch"); + return retval; + } + } + break; + } + } + + + unwind_protect frame; + frame.protect_var (buffer_error_messages); + + if (error_handler.is_defined ()) + buffer_error_messages++; + + // Apply functions. + + if (uniform_output) + { + std::list<octave_value_list> idx_list (1); + idx_list.front ().resize (1); + std::string idx_type = "("; + + OCTAVE_LOCAL_BUFFER (octave_value, retv, nargout1); + + for (octave_idx_type count = 0; count < k; count++) + { + idx_list.front ()(0) = count + 1.0; + + for (int j = 0; j < nargin; j++) + { + if (mask[j]) + inputlist.xelem (j) = inputs[j].do_index_op (idx_list); + + if (error_state) + return retval; + } + + const octave_value_list tmp + = get_output_list (count, nargout, inputlist, func, + error_handler); + + if (error_state) + return retval; + + if (nargout > 0 && tmp.length () < nargout) + { + error ("arrayfun: function returned fewer than nargout values"); + return retval; + } + + if (nargout > 0 + || (nargout == 0 + && tmp.length () > 0 && tmp(0).is_defined ())) + { + int num_to_copy = tmp.length (); + + if (num_to_copy > nargout1) + num_to_copy = nargout1; + + if (count == 0) + { + for (int j = 0; j < num_to_copy; j++) + { + if (tmp(j).is_defined ()) + { + octave_value val = tmp(j); + + if (val.numel () == 1) + retv[j] = val.resize (fdims); + else + { + error ("arrayfun: all values must be scalars when UniformOutput = true"); + break; + } + } + } + } + else + { + for (int j = 0; j < num_to_copy; j++) + { + if (tmp(j).is_defined ()) + { + octave_value val = tmp(j); + + if (! retv[j].fast_elem_insert (count, val)) + { + if (val.numel () == 1) + { + idx_list.front ()(0) = count + 1.0; + retv[j].assign (octave_value::op_asn_eq, + idx_type, idx_list, val); + + if (error_state) + break; + } + else + { + error ("arrayfun: all values must be scalars when UniformOutput = true"); + break; + } + } + } + } + } + } + + if (error_state) + break; + } + + retval.resize (nargout1); + + for (int j = 0; j < nargout1; j++) + { + if (nargout > 0 && retv[j].is_undefined ()) + retval(j) = NDArray (fdims); + else + retval(j) = retv[j]; + } + } + else + { + std::list<octave_value_list> idx_list (1); + idx_list.front ().resize (1); + std::string idx_type = "("; + + OCTAVE_LOCAL_BUFFER (Cell, results, nargout1); + + for (int j = 0; j < nargout1; j++) + results[j].resize (fdims, Matrix ()); + + bool have_some_output = false; + + for (octave_idx_type count = 0; count < k; count++) + { + idx_list.front ()(0) = count + 1.0; + + for (int j = 0; j < nargin; j++) + { + if (mask[j]) + inputlist.xelem (j) = inputs[j].do_index_op (idx_list); + + if (error_state) + return retval; + } + + const octave_value_list tmp + = get_output_list (count, nargout, inputlist, func, + error_handler); + + if (error_state) + return retval; + + if (nargout > 0 && tmp.length () < nargout) + { + error ("arrayfun: function returned fewer than nargout values"); + return retval; + } + + if (nargout > 0 + || (nargout == 0 + && tmp.length () > 0 && tmp(0).is_defined ())) + { + int num_to_copy = tmp.length (); + + if (num_to_copy > nargout1) + num_to_copy = nargout1; + + if (num_to_copy > 0) + have_some_output = true; + + for (int j = 0; j < num_to_copy; j++) + results[j](count) = tmp(j); + } + } + + if (have_some_output || fdims.any_zero ()) + { + retval.resize (nargout1); + + for (int j = 0; j < nargout1; j++) + retval(j) = results[j]; + } + } + } + else + error ("arrayfun: argument NAME must be a string or function handle"); + + return retval; +} + +/* +%!function r = __f11 (x) +%! global __arrayfun_test_num_outputs__; +%! __arrayfun_test_num_outputs__ = nargout; +%! r = x; +%!endfunction + +%!function __f01 (x) +%! global __arrayfun_test_num_outputs__; +%! __arrayfun_test_num_outputs__ = nargout; +%!endfunction + +%!test +%! global __arrayfun_test_num_outputs__; +%! arrayfun (@__f11, {1}); +%! assert (__arrayfun_test_num_outputs__, 0); +%! x = arrayfun (@__f11, {1}); +%! assert (__arrayfun_test_num_outputs__, 1); + +%!test +%! global __arrayfun_test_num_outputs__; +%! arrayfun (@__f01, {1}); +%! assert (__arrayfun_test_num_outputs__, 0); + +%!error x = arrayfun (@__f01, [1, 2]); + +%!test +%! assert (arrayfun (@__f11, [1, 2]), [1, 2]); +%! assert (arrayfun (@__f11, [1, 2], "uniformoutput", false), {1, 2}); +%! assert (arrayfun (@__f11, {1, 2}), {1, 2}); +%! assert (arrayfun (@__f11, {1, 2}, "uniformoutput", false), {{1}, {2}}); + +%!assert (arrayfun (@ones, 1, [2,3], "uniformoutput", false), {[1,1], [1,1,1]}) + +%% Test function to check the "Errorhandler" option +%!function z = __arrayfunerror (S, varargin) +%! z = S; +%!endfunction +%% First input argument can be a string, an inline function, a +%% function_handle or an anonymous function +%!test +%! arrayfun (@isequal, [false, true], [true, true]); %% No output argument +%!error +%! arrayfun (@isequal); %% One or less input arguments +%!test +%! A = arrayfun ("isequal", [false, true], [true, true]); +%! assert (A, [false, true]); +%!test +%! A = arrayfun (inline ("(x == y)", "x", "y"), [false, true], [true, true]); +%! assert (A, [false, true]); +%!test +%! A = arrayfun (@isequal, [false, true], [true, true]); +%! assert (A, [false, true]); +%!test +%! A = arrayfun (@(x,y) isequal (x,y), [false, true], [true, true]); +%! assert (A, [false, true]); + +%% Number of input and output arguments may be greater than one +%#!test +%! A = arrayfun (@(x) islogical (x), false); +%! assert (A, true); +%!test +%! A = arrayfun (@(x,y,z) x + y + z, [1, 1, 1], [2, 2, 2], [3, 4, 5]); +%! assert (A, [6, 7, 8], 1e-16); +%!test %% Two input arguments of different types +%! A = arrayfun (@(x,y) islogical (x) && ischar (y), false, "a"); +%! assert (A, true); +%!test %% Pass another variable to the anonymous function +%! y = true; +%! A = arrayfun (@(x) islogical (x && y), false); +%! assert (A, true); +%!test %% Three ouptut arguments of different type +%! [A, B, C] = arrayfun (@find, [10, 11; 0, 12], "UniformOutput", false); +%! assert (isequal (A, {true, true; [], true})); +%! assert (isequal (B, {true, true; [], true})); +%! assert (isequal (C, {10, 11; [], 12})); + +%% Input arguments can be of type logical +%!test +%! A = arrayfun (@(x,y) x == y, [false, true], [true, true]); +%! assert (A, [false, true]); +%!test +%! A = arrayfun (@(x,y) x == y, [false; true], [true; true], "UniformOutput", true); +%! assert (A, [false; true]); +%!test +%! A = arrayfun (@(x) x, [false, true, false, true], "UniformOutput", false); +%! assert (A, {false, true, false, true}); +%!test %% Three ouptut arguments of same type +%! [A, B, C] = arrayfun (@find, [true, false; false, true], "UniformOutput", false); +%! assert (isequal (A, {true, []; [], true})); +%! assert (isequal (B, {true, []; [], true})); +%! assert (isequal (C, {true, []; [], true})); +%!test +%! A = arrayfun (@(x,y) array2str (x,y), true, true, \ +%! "ErrorHandler", @__arrayfunerror); +%! assert (isfield (A, "identifier"), true); +%! assert (isfield (A, "message"), true); +%! assert (isfield (A, "index"), true); +%! assert (isempty (A.message), false); +%! assert (A.index, 1); +%!test %% Overwriting setting of "UniformOutput" true +%! A = arrayfun (@(x,y) array2str (x,y), true, true, "UniformOutput", true, \ +%! "ErrorHandler", @__arrayfunerror); +%! assert (isfield (A, "identifier"), true); +%! assert (isfield (A, "message"), true); +%! assert (isfield (A, "index"), true); +%! assert (isempty (A.message), false); +%! assert (A.index, 1); + +%% Input arguments can be of type numeric +%!test +%! A = arrayfun (@(x,y) x>y, [1.1, 4.2], [3.1, 2+3*i]); +%! assert (A, [false, true]); +%!test +%! A = arrayfun (@(x,y) x>y, [1.1, 4.2; 2, 4], [3.1, 2; 2, 4+2*i], "UniformOutput", true); +%! assert (A, [false, true; false, false]); +%!test +%! A = arrayfun (@(x,y) x:y, [1.1, 4], [3.1, 6], "UniformOutput", false); +%! assert (isequal (A{1}, [1.1, 2.1, 3.1])); +%! assert (isequal (A{2}, [4, 5, 6])); +%!test %% Three ouptut arguments of different type +%! [A, B, C] = arrayfun (@find, [10, 11; 0, 12], "UniformOutput", false); +%! assert (isequal (A, {true, true; [], true})); +%! assert (isequal (B, {true, true; [], true})); +%! assert (isequal (C, {10, 11; [], 12})); +%!test +%! A = arrayfun (@(x,y) array2str (x,y), {1.1, 4}, {3.1, 6}, \ +%! "ErrorHandler", @__arrayfunerror); +%! B = isfield (A(1), "message") && isfield (A(1), "index"); +%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]); +%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]); +%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]); +%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]); +%! assert ([A(1).index, A(2).index], [1, 2]); +%!test %% Overwriting setting of "UniformOutput" true +%! A = arrayfun (@(x,y) array2str (x,y), {1.1, 4}, {3.1, 6}, \ +%! "UniformOutput", true, "ErrorHandler", @__arrayfunerror); +%! B = isfield (A(1), "message") && isfield (A(1), "index"); +%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]); +%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]); +%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]); +%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]); +%! assert ([A(1).index, A(2).index], [1, 2]); + +%% Input arguments can be of type character or strings +%!test +%! A = arrayfun (@(x,y) x>y, ["ad", "c", "ghi"], ["cc", "d", "fgh"]); +%! assert (A, [false, true, false, true, true, true]); +%!test +%! A = arrayfun (@(x,y) x>y, ["a"; "f"], ["c"; "d"], "UniformOutput", true); +%! assert (A, [false; true]); +%!test +%! A = arrayfun (@(x,y) x:y, ["a", "d"], ["c", "f"], "UniformOutput", false); +%! assert (A, {"abc", "def"}); +%!test +%! A = arrayfun (@(x,y) cell2str (x,y), ["a", "d"], ["c", "f"], \ +%! "ErrorHandler", @__arrayfunerror); +%! B = isfield (A(1), "identifier") && isfield (A(1), "message") && isfield (A(1), "index"); +%! assert (B, true); + +%% Input arguments can be of type structure +%!test +%! a = struct ("a", 1.1, "b", 4.2); b = struct ("a", 3.1, "b", 2); +%! A = arrayfun (@(x,y) (x.a < y.a) && (x.b > y.b), a, b); +%! assert (A, true); +%!test +%! a = struct ("a", 1.1, "b", 4.2); b = struct ("a", 3.1, "b", 2); +%! A = arrayfun (@(x,y) (x.a < y.a) && (x.b > y.b), a, b, "UniformOutput", true); +%! assert (A, true); +%!test +%! a = struct ("a", 1.1, "b", 4.2); b = struct ("a", 3.1, "b", 2); +%! A = arrayfun (@(x,y) x.a:y.a, a, b, "UniformOutput", false); +%! assert (isequal (A, {[1.1, 2.1, 3.1]})); +%!test +%! A = arrayfun (@(x) mat2str(x), "a", "ErrorHandler", @__arrayfunerror); +%! assert (isfield (A, "identifier"), true); +%! assert (isfield (A, "message"), true); +%! assert (isfield (A, "index"), true); +%! assert (isempty (A.message), false); +%! assert (A.index, 1); +%!test %% Overwriting setting of "UniformOutput" true +%! A = arrayfun (@(x) mat2str(x), "a", "UniformOutput", true, \ +%! "ErrorHandler", @__arrayfunerror); +%! assert (isfield (A, "identifier"), true); +%! assert (isfield (A, "message"), true); +%! assert (isfield (A, "index"), true); +%! assert (isempty (A.message), false); +%! assert (A.index, 1); + +%% Input arguments can be of type cell array +%!test +%! A = arrayfun (@(x,y) x{1} < y{1}, {1.1, 4.2}, {3.1, 2}); +%! assert (A, [true, false]); +%!test +%! A = arrayfun (@(x,y) x{1} < y{1}, {1.1; 4.2}, {3.1; 2}, "UniformOutput", true); +%! assert (A, [true; false]); +%!test +%! A = arrayfun (@(x,y) x{1} < y{1}, {1.1, 4.2}, {3.1, 2}, "UniformOutput", false); +%! assert (A, {true, false}); +%!test +%! A = arrayfun (@(x,y) num2str(x,y), {1.1, 4.2}, {3.1, 2}, "ErrorHandler", @__arrayfunerror); +%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]); +%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]); +%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]); +%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]); +%! assert ([A(1).index, A(2).index], [1, 2]); +%!test +%! A = arrayfun (@(x,y) num2str (x,y), {1.1, 4.2}, {3.1, 2}, \ +%! "UniformOutput", true, "ErrorHandler", @__arrayfunerror); +%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]); +%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]); +%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]); +%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]); +%! assert ([A(1).index, A(2).index], [1, 2]); +*/ + +static void +do_num2cell_helper (const dim_vector& dv, + const Array<int>& dimv, + dim_vector& celldv, dim_vector& arraydv, + Array<int>& perm) +{ + int dvl = dimv.length (); + int maxd = dv.length (); + celldv = dv; + for (int i = 0; i < dvl; i++) + maxd = std::max (maxd, dimv(i)); + if (maxd > dv.length ()) + celldv.resize (maxd, 1); + arraydv = celldv; + + OCTAVE_LOCAL_BUFFER_INIT (bool, sing, maxd, false); + + perm.clear (maxd, 1); + for (int i = 0; i < dvl; i++) + { + int k = dimv(i) - 1; + if (k < 0) + { + error ("num2cell: dimension indices must be positive"); + return; + } + else if (i > 0 && k < dimv(i-1) - 1) + { + error ("num2cell: dimension indices must be strictly increasing"); + return; + } + + sing[k] = true; + perm(i) = k; + } + + for (int k = 0, i = dvl; k < maxd; k++) + if (! sing[k]) + perm(i++) = k; + + for (int i = 0; i < maxd; i++) + if (sing[i]) + celldv(i) = 1; + else + arraydv(i) = 1; +} + +template<class NDA> +static inline typename NDA::element_type +do_num2cell_elem (const NDA& array, octave_idx_type i) +{ return array(i); } + +static inline Cell +do_num2cell_elem (const Cell& array, octave_idx_type i) +{ return Cell (array(i)); } + + +template<class NDA> +static Cell +do_num2cell (const NDA& array, const Array<int>& dimv) +{ + if (dimv.is_empty ()) + { + Cell retval (array.dims ()); + octave_idx_type nel = array.numel (); + for (octave_idx_type i = 0; i < nel; i++) + retval.xelem (i) = do_num2cell_elem (array, i); + + return retval; + } + else + { + dim_vector celldv, arraydv; + Array<int> perm; + do_num2cell_helper (array.dims (), dimv, celldv, arraydv, perm); + if (error_state) + return Cell (); + + NDA parray = array.permute (perm); + + octave_idx_type nela = arraydv.numel (), nelc = celldv.numel (); + parray = parray.reshape (dim_vector (nela, nelc)); + + Cell retval (celldv); + for (octave_idx_type i = 0; i < nelc; i++) + { + retval.xelem (i) = NDA (parray.column (i).reshape (arraydv)); + } + + return retval; + } +} + +// FIXME -- this is a mess, but if a size method for the object exists, +// we have to call it to get the size of the object instead of using the +// internal dims method. + +static dim_vector +get_object_dims (octave_value& obj) +{ + dim_vector retval; + + Matrix m = obj.size (); + + int n = m.numel (); + + retval.resize (n); + + for (int i = 0; i < n; i++) + retval(i) = m(i); + + return retval; +} + +static Cell +do_object2cell (const octave_value& obj, const Array<int>& dimv) +{ + Cell retval; + + // FIXME -- this copy is only needed because the octave_value::size + // method is not const. + octave_value array = obj; + + if (dimv.is_empty ()) + { + dim_vector dv = get_object_dims (array); + + if (! error_state) + { + retval.resize (dv); + + octave_value_list idx (1); + + for (octave_idx_type i = 0; i < dv.numel (); i++) + { + octave_quit (); + + idx(0) = double (i+1); + + retval.xelem (i) = array.single_subsref ("(", idx); + + if (error_state) + break; + } + } + } + else + { + error ("num2cell (A, dim) not implemented for class objects"); + } + + return retval; +} + +DEFUN (num2cell, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {@var{C} =} num2cell (@var{A})\n\ +@deftypefnx {Built-in Function} {@var{C} =} num2cell (@var{A}, @var{dim})\n\ +Convert the numeric matrix @var{A} to a cell array. If @var{dim} is\n\ +defined, the value @var{C} is of dimension 1 in this dimension and the\n\ +elements of @var{A} are placed into @var{C} in slices. For example:\n\ +\n\ +@example\n\ +@group\n\ +num2cell ([1,2;3,4])\n\ + @result{}\n\ + @{\n\ + [1,1] = 1\n\ + [2,1] = 3\n\ + [1,2] = 2\n\ + [2,2] = 4\n\ + @}\n\ +num2cell ([1,2;3,4],1)\n\ + @result{}\n\ + @{\n\ + [1,1] =\n\ + 1\n\ + 3\n\ + [1,2] =\n\ + 2\n\ + 4\n\ + @}\n\ +@end group\n\ +@end example\n\ +\n\ +@seealso{mat2cell}\n\ +@end deftypefn") +{ + int nargin = args.length (); + octave_value retval; + + if (nargin < 1 || nargin > 2) + print_usage (); + else + { + octave_value array = args(0); + Array<int> dimv; + if (nargin > 1) + dimv = args (1).int_vector_value (true); + + if (error_state) + ; + else if (array.is_bool_type ()) + retval = do_num2cell (array.bool_array_value (), dimv); + else if (array.is_char_matrix ()) + retval = do_num2cell (array.char_array_value (), dimv); + else if (array.is_numeric_type ()) + { + if (array.is_integer_type ()) + { + if (array.is_int8_type ()) + retval = do_num2cell (array.int8_array_value (), dimv); + else if (array.is_int16_type ()) + retval = do_num2cell (array.int16_array_value (), dimv); + else if (array.is_int32_type ()) + retval = do_num2cell (array.int32_array_value (), dimv); + else if (array.is_int64_type ()) + retval = do_num2cell (array.int64_array_value (), dimv); + else if (array.is_uint8_type ()) + retval = do_num2cell (array.uint8_array_value (), dimv); + else if (array.is_uint16_type ()) + retval = do_num2cell (array.uint16_array_value (), dimv); + else if (array.is_uint32_type ()) + retval = do_num2cell (array.uint32_array_value (), dimv); + else if (array.is_uint64_type ()) + retval = do_num2cell (array.uint64_array_value (), dimv); + } + else if (array.is_complex_type ()) + { + if (array.is_single_type ()) + retval = do_num2cell (array.float_complex_array_value (), dimv); + else + retval = do_num2cell (array.complex_array_value (), dimv); + } + else + { + if (array.is_single_type ()) + retval = do_num2cell (array.float_array_value (), dimv); + else + retval = do_num2cell (array.array_value (), dimv); + } + } + else if (array.is_object ()) + retval = do_object2cell (array, dimv); + else if (array.is_map ()) + retval = do_num2cell (array.map_value (), dimv); + else if (array.is_cell ()) + retval = do_num2cell (array.cell_value (), dimv); + else if (array.is_object ()) + retval = do_num2cell (array.cell_value (), dimv); + else + gripe_wrong_type_arg ("num2cell", array); + } + + return retval; +} + +/* +%!assert (num2cell ([1,2;3,4]), {1,2;3,4}) +%!assert (num2cell ([1,2;3,4], 1), {[1;3],[2;4]}) +%!assert (num2cell ([1,2;3,4], 2), {[1,2];[3,4]}) +*/ + +static bool +mat2cell_mismatch (const dim_vector& dv, + const Array<octave_idx_type> *d, int nd) +{ + for (int i = 0; i < nd; i++) + { + octave_idx_type s = 0; + for (octave_idx_type j = 0; j < d[i].length (); j++) + s += d[i](j); + + octave_idx_type r = i < dv.length () ? dv(i) : 1; + + if (s != r) + { + error ("mat2cell: mismatch on %d-th dimension (%d != %d)", + i+1, r, s); + return true; + } + } + + return false; +} + +template<class container> +static void +prepare_idx (container *idx, int idim, int nd, + const Array<octave_idx_type>* d) +{ + octave_idx_type nidx = idim < nd ? d[idim].numel () : 1; + if (nidx == 1) + idx[0] = idx_vector::colon; + else + { + octave_idx_type l = 0; + for (octave_idx_type i = 0; i < nidx; i++) + { + octave_idx_type u = l + d[idim](i); + idx[i] = idx_vector (l, u); + l = u; + } + } +} + +// 2D specialization, works for Array, Sparse and octave_map. +// Uses 1D or 2D indexing. + +template <class Array2D> +static Cell +do_mat2cell_2d (const Array2D& a, const Array<octave_idx_type> *d, int nd) +{ + NoAlias<Cell> retval; + assert (nd == 1 || nd == 2); + assert (a.ndims () == 2); + + if (mat2cell_mismatch (a.dims (), d, nd)) + return retval; + + octave_idx_type nridx = d[0].length (); + octave_idx_type ncidx = nd == 1 ? 1 : d[1].length (); + retval.clear (nridx, ncidx); + + int ivec = -1; + if (a.rows () > 1 && a.cols () == 1 && ncidx == 1) + ivec = 0; + else if (a.rows () == 1 && nridx == 1 && nd == 2) + ivec = 1; + + if (ivec >= 0) + { + // Vector split. Use 1D indexing. + octave_idx_type l = 0, nidx = (ivec == 0 ? nridx : ncidx); + for (octave_idx_type i = 0; i < nidx; i++) + { + octave_idx_type u = l + d[ivec](i); + retval(i) = a.index (idx_vector (l, u)); + l = u; + } + } + else + { + // General 2D case. Use 2D indexing. + OCTAVE_LOCAL_BUFFER (idx_vector, ridx, nridx); + prepare_idx (ridx, 0, nd, d); + + OCTAVE_LOCAL_BUFFER (idx_vector, cidx, ncidx); + prepare_idx (cidx, 1, nd, d); + + for (octave_idx_type j = 0; j < ncidx; j++) + for (octave_idx_type i = 0; i < nridx; i++) + { + octave_quit (); + + retval(i,j) = a.index (ridx[i], cidx[j]); + } + } + + return retval; +} + +// Nd case. Works for Arrays and octave_map. +// Uses Nd indexing. + +template <class ArrayND> +Cell +do_mat2cell_nd (const ArrayND& a, const Array<octave_idx_type> *d, int nd) +{ + NoAlias<Cell> retval; + assert (nd >= 1); + + if (mat2cell_mismatch (a.dims (), d, nd)) + return retval; + + dim_vector rdv = dim_vector::alloc (nd); + OCTAVE_LOCAL_BUFFER (octave_idx_type, nidx, nd); + octave_idx_type idxtot = 0; + for (int i = 0; i < nd; i++) + { + rdv(i) = nidx[i] = d[i].length (); + idxtot += nidx[i]; + } + + retval.clear (rdv); + + OCTAVE_LOCAL_BUFFER (idx_vector, xidx, idxtot); + OCTAVE_LOCAL_BUFFER (idx_vector *, idx, nd); + + idxtot = 0; + for (int i = 0; i < nd; i++) + { + idx[i] = xidx + idxtot; + prepare_idx (idx[i], i, nd, d); + idxtot += nidx[i]; + } + + OCTAVE_LOCAL_BUFFER_INIT (octave_idx_type, ridx, nd, 0); + NoAlias< Array<idx_vector> > ra_idx + (dim_vector (1, std::max (nd, a.ndims ())), idx_vector::colon); + + for (octave_idx_type j = 0; j < retval.numel (); j++) + { + octave_quit (); + + for (int i = 0; i < nd; i++) + ra_idx(i) = idx[i][ridx[i]]; + + retval(j) = a.index (ra_idx); + + rdv.increment_index (ridx); + } + + return retval; +} + +// Dispatcher. +template <class ArrayND> +Cell +do_mat2cell (const ArrayND& a, const Array<octave_idx_type> *d, int nd) +{ + if (a.ndims () == 2 && nd <= 2) + return do_mat2cell_2d (a, d, nd); + else + return do_mat2cell_nd (a, d, nd); +} + +// General case. Works for any class supporting do_index_op. +// Uses Nd indexing. + +Cell +do_mat2cell (octave_value& a, const Array<octave_idx_type> *d, int nd) +{ + NoAlias<Cell> retval; + assert (nd >= 1); + + if (mat2cell_mismatch (a.dims (), d, nd)) + return retval; + + dim_vector rdv = dim_vector::alloc (nd); + OCTAVE_LOCAL_BUFFER (octave_idx_type, nidx, nd); + octave_idx_type idxtot = 0; + for (int i = 0; i < nd; i++) + { + rdv(i) = nidx[i] = d[i].length (); + idxtot += nidx[i]; + } + + retval.clear (rdv); + + OCTAVE_LOCAL_BUFFER (octave_value, xidx, idxtot); + OCTAVE_LOCAL_BUFFER (octave_value *, idx, nd); + + idxtot = 0; + for (int i = 0; i < nd; i++) + { + idx[i] = xidx + idxtot; + prepare_idx (idx[i], i, nd, d); + idxtot += nidx[i]; + } + + OCTAVE_LOCAL_BUFFER_INIT (octave_idx_type, ridx, nd, 0); + octave_value_list ra_idx (std::max (nd, a.ndims ()), + octave_value::magic_colon_t); + + for (octave_idx_type j = 0; j < retval.numel (); j++) + { + octave_quit (); + + for (int i = 0; i < nd; i++) + ra_idx(i) = idx[i][ridx[i]]; + + retval(j) = a.do_index_op (ra_idx); + + if (error_state) + break; + + rdv.increment_index (ridx); + } + + return retval; +} + +DEFUN (mat2cell, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {@var{C} =} mat2cell (@var{A}, @var{m}, @var{n})\n\ +@deftypefnx {Built-in Function} {@var{C} =} mat2cell (@var{A}, @var{d1}, @var{d2}, @dots{})\n\ +@deftypefnx {Built-in Function} {@var{C} =} mat2cell (@var{A}, @var{r})\n\ +Convert the matrix @var{A} to a cell array. If @var{A} is 2-D, then\n\ +it is required that @code{sum (@var{m}) == size (@var{A}, 1)} and\n\ +@code{sum (@var{n}) == size (@var{A}, 2)}. Similarly, if @var{A} is\n\ +multi-dimensional and the number of dimensional arguments is equal\n\ +to the dimensions of @var{A}, then it is required that @code{sum (@var{di})\n\ +== size (@var{A}, i)}.\n\ +\n\ +Given a single dimensional argument @var{r}, the other dimensional\n\ +arguments are assumed to equal @code{size (@var{A},@var{i})}.\n\ +\n\ +An example of the use of mat2cell is\n\ +\n\ +@example\n\ +mat2cell (reshape (1:16,4,4), [3,1], [3,1])\n\ +@result{}\n\ +@{\n\ + [1,1] =\n\ +\n\ + 1 5 9\n\ + 2 6 10\n\ + 3 7 11\n\ +\n\ + [2,1] =\n\ +\n\ + 4 8 12\n\ +\n\ + [1,2] =\n\ +\n\ + 13\n\ + 14\n\ + 15\n\ +\n\ + [2,2] = 16\n\ +@}\n\ +@end example\n\ +@seealso{num2cell, cell2mat}\n\ +@end deftypefn") +{ + int nargin = args.length (); + octave_value retval; + + if (nargin < 2) + print_usage (); + else + { + // Prepare indices. + OCTAVE_LOCAL_BUFFER (Array<octave_idx_type>, d, nargin-1); + + for (int i = 1; i < nargin; i++) + { + d[i-1] = args(i).octave_idx_type_vector_value (true); + if (error_state) + return retval; + } + + octave_value a = args(0); + bool sparse = a.is_sparse_type (); + if (sparse && nargin > 3) + { + error ("mat2cell: sparse arguments only support 2D indexing"); + return retval; + } + + switch (a.builtin_type ()) + { + case btyp_double: + { + if (sparse) + retval = do_mat2cell_2d (a.sparse_matrix_value (), d, nargin-1); + else + retval = do_mat2cell (a.array_value (), d, nargin - 1); + break; + } + case btyp_complex: + { + if (sparse) + retval = do_mat2cell_2d (a.sparse_complex_matrix_value (), d, nargin-1); + else + retval = do_mat2cell (a.complex_array_value (), d, nargin - 1); + break; + } +#define BTYP_BRANCH(X,Y) \ + case btyp_ ## X: \ + retval = do_mat2cell (a.Y ## _value (), d, nargin - 1); \ + break + + BTYP_BRANCH (float, float_array); + BTYP_BRANCH (float_complex, float_complex_array); + BTYP_BRANCH (bool, bool_array); + BTYP_BRANCH (char, char_array); + + BTYP_BRANCH (int8, int8_array); + BTYP_BRANCH (int16, int16_array); + BTYP_BRANCH (int32, int32_array); + BTYP_BRANCH (int64, int64_array); + BTYP_BRANCH (uint8, uint8_array); + BTYP_BRANCH (uint16, uint16_array); + BTYP_BRANCH (uint32, uint32_array); + BTYP_BRANCH (uint64, uint64_array); + + BTYP_BRANCH (cell, cell); + BTYP_BRANCH (struct, map); +#undef BTYP_BRANCH + + case btyp_func_handle: + gripe_wrong_type_arg ("mat2cell", a); + break; + default: + retval = do_mat2cell (a, d, nargin-1); + } + } + + return retval; +} + +/* +%!test +%! x = reshape (1:20, 5, 4); +%! c = mat2cell (x, [3,2], [3,1]); +%! assert (c, {[1,6,11;2,7,12;3,8,13],[16;17;18];[4,9,14;5,10,15],[19;20]}); + +%!test +%! x = "abcdefghij"; +%! c = mat2cell (x, 1, [0,4,2,0,4,0]); +%! empty1by0str = resize ("", 1, 0); +%! assert (c, {empty1by0str,"abcd","ef",empty1by0str,"ghij",empty1by0str}); +*/ + +// FIXME: it would be nice to allow ranges being handled without a conversion. +template <class NDA> +static Cell +do_cellslices_nda (const NDA& array, + const Array<octave_idx_type>& lb, + const Array<octave_idx_type>& ub, + int dim = -1) +{ + octave_idx_type n = lb.length (); + Cell retval (1, n); + if (array.is_vector () && (dim == -1 + || (dim == 0 && array.columns () == 1) + || (dim == 1 && array.rows () == 1))) + { + for (octave_idx_type i = 0; i < n && ! error_state; i++) + retval(i) = array.index (idx_vector (lb(i) - 1, ub(i))); + } + else + { + const dim_vector dv = array.dims (); + int ndims = dv.length (); + if (dim < 0) + dim = dv.first_non_singleton (); + ndims = std::max (ndims, dim + 1); + + Array<idx_vector> idx (dim_vector (ndims, 1), idx_vector::colon); + + for (octave_idx_type i = 0; i < n && ! error_state; i++) + { + idx(dim) = idx_vector (lb(i) - 1, ub(i)); + retval(i) = array.index (idx); + } + } + + return retval; +} + +DEFUN (cellslices, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {@var{sl} =} cellslices (@var{x}, @var{lb}, @var{ub}, @var{dim})\n\ +Given an array @var{x}, this function produces a cell array of slices from\n\ +the array determined by the index vectors @var{lb}, @var{ub}, for lower and\n\ +upper bounds, respectively. In other words, it is equivalent to the\n\ +following code:\n\ +\n\ +@example\n\ +@group\n\ +n = length (lb);\n\ +sl = cell (1, n);\n\ +for i = 1:length (lb)\n\ + sl@{i@} = x(:,@dots{},lb(i):ub(i),@dots{},:);\n\ +endfor\n\ +@end group\n\ +@end example\n\ +\n\ +The position of the index is determined by @var{dim}. If not specified,\n\ +slicing is done along the first non-singleton dimension.\n\ +@seealso{cell2mat, cellindexmat, cellfun}\n\ +@end deftypefn") +{ + octave_value retval; + int nargin = args.length (); + if (nargin == 3 || nargin == 4) + { + octave_value x = args(0); + Array<octave_idx_type> lb = args(1).octave_idx_type_vector_value (); + Array<octave_idx_type> ub = args(2).octave_idx_type_vector_value (); + int dim = -1; + if (nargin == 4) + { + dim = args(3).int_value () - 1; + if (dim < 0) + error ("cellslices: DIM must be a valid dimension"); + } + + if (! error_state) + { + if (lb.length () != ub.length ()) + error ("cellslices: the lengths of LB and UB must match"); + else + { + Cell retcell; + if (! x.is_sparse_type () && x.is_matrix_type ()) + { + // specialize for some dense arrays. + if (x.is_bool_type ()) + retcell = do_cellslices_nda (x.bool_array_value (), lb, ub, dim); + else if (x.is_char_matrix ()) + retcell = do_cellslices_nda (x.char_array_value (), lb, ub, dim); + else if (x.is_integer_type ()) + { + if (x.is_int8_type ()) + retcell = do_cellslices_nda (x.int8_array_value (), lb, ub, dim); + else if (x.is_int16_type ()) + retcell = do_cellslices_nda (x.int16_array_value (), lb, ub, dim); + else if (x.is_int32_type ()) + retcell = do_cellslices_nda (x.int32_array_value (), lb, ub, dim); + else if (x.is_int64_type ()) + retcell = do_cellslices_nda (x.int64_array_value (), lb, ub, dim); + else if (x.is_uint8_type ()) + retcell = do_cellslices_nda (x.uint8_array_value (), lb, ub, dim); + else if (x.is_uint16_type ()) + retcell = do_cellslices_nda (x.uint16_array_value (), lb, ub, dim); + else if (x.is_uint32_type ()) + retcell = do_cellslices_nda (x.uint32_array_value (), lb, ub, dim); + else if (x.is_uint64_type ()) + retcell = do_cellslices_nda (x.uint64_array_value (), lb, ub, dim); + } + else if (x.is_complex_type ()) + { + if (x.is_single_type ()) + retcell = do_cellslices_nda (x.float_complex_array_value (), lb, ub, dim); + else + retcell = do_cellslices_nda (x.complex_array_value (), lb, ub, dim); + } + else + { + if (x.is_single_type ()) + retcell = do_cellslices_nda (x.float_array_value (), lb, ub, dim); + else + retcell = do_cellslices_nda (x.array_value (), lb, ub, dim); + } + } + else + { + // generic code. + octave_idx_type n = lb.length (); + retcell = Cell (1, n); + const dim_vector dv = x.dims (); + int ndims = dv.length (); + if (dim < 0) + dim = dv.first_non_singleton (); + ndims = std::max (ndims, dim + 1); + octave_value_list idx (ndims, octave_value::magic_colon_t); + for (octave_idx_type i = 0; i < n && ! error_state; i++) + { + idx(dim) = Range (lb(i), ub(i)); + retcell(i) = x.do_index_op (idx); + } + } + if (! error_state) + retval = retcell; + } + } + } + else + print_usage (); + + return retval; +} + +/* +%!test +%! m = [1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12]; +%! c = cellslices (m, [1, 2], [2, 3], 2); +%! assert (c, {[1, 2; 5, 6; 9, 10], [2, 3; 6, 7; 10, 11]}); +*/ + +DEFUN (cellindexmat, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {@var{y} =} cellindexmat (@var{x}, @var{varargin})\n\ +Given a cell array of matrices @var{x}, this function computes\n\ +\n\ +@example\n\ +@group\n\ +Y = cell (size (X));\n\ +for i = 1:numel (X)\n\ + Y@{i@} = X@{i@}(varargin@{:@});\n\ +endfor\n\ +@end group\n\ +@end example\n\ +@seealso{cellslices, cellfun}\n\ +@end deftypefn") +{ + octave_value retval; + if (args.length () >= 1) + { + if (args(0).is_cell ()) + { + const Cell x = args(0).cell_value (); + NoAlias<Cell> y(x.dims ()); + octave_idx_type nel = x.numel (); + octave_value_list idx = args.slice (1, args.length () - 1); + + for (octave_idx_type i = 0; i < nel; i++) + { + octave_quit (); + octave_value tmp = x(i); + y(i) = tmp.do_index_op (idx); + if (error_state) + break; + } + + retval = y; + } + else + error ("cellindexmat: X must be a cell"); + } + else + print_usage (); + + return retval; +}