changelog shortlog tags changeset files revisions annotate raw

src/DLD-FUNCTIONS/cellfun.cc

changeset 10289: 4b124317dc38
parent:d0ce5e973937
author: John W. Eaton <jwe@octave.org>
date: Tue Feb 09 20:58:55 2010 -0500 (70 minutes ago)
permissions: -rw-r--r--
description: base_properties::set_children: account for hidden children
1/*
2
3Copyright (C) 2005, 2006, 2007, 2008, 2009 Mohamed Kamoun
4Copyright (C) 2009 Jaroslav Hajek
5
6This file is part of Octave.
7
8Octave is free software; you can redistribute it and/or modify it
9under the terms of the GNU General Public License as published by the
10Free Software Foundation; either version 3 of the License, or (at your
11option) any later version.
12
13Octave is distributed in the hope that it will be useful, but WITHOUT
14ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License
19along with Octave; see the file COPYING. If not, see
20<http://www.gnu.org/licenses/>.
21
22*/
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <string>
29#include <vector>
30#include <list>
31#include <memory>
32
33#include "lo-mappers.h"
34#include "oct-locbuf.h"
35
36#include "Cell.h"
37#include "oct-map.h"
38#include "defun-dld.h"
39#include "parse.h"
40#include "variables.h"
41#include "ov-colon.h"
42#include "unwind-prot.h"
43#include "gripes.h"
44#include "utils.h"
45
46#include "ov-scalar.h"
47#include "ov-float.h"
48#include "ov-complex.h"
49#include "ov-flt-complex.h"
50#include "ov-bool.h"
51#include "ov-int8.h"
52#include "ov-int16.h"
53#include "ov-int32.h"
54#include "ov-int64.h"
55#include "ov-uint8.h"
56#include "ov-uint16.h"
57#include "ov-uint32.h"
58#include "ov-uint64.h"
59
60// Rationale:
61// The octave_base_value::subsasgn method carries too much overhead for
62// per-element assignment strategy.
63// This class will optimize the most optimistic and most likely case
64// when the output really is scalar by defining a hierarchy of virtual
65// collectors specialized for some scalar types.
66
67class scalar_col_helper
68{
69public:
70 virtual bool collect (octave_idx_type i, const octave_value& val) = 0;
71 virtual octave_value result (void) = 0;
72 virtual ~scalar_col_helper (void) { }
73};
74
75// The default collector represents what was previously done in the main loop.
76// This reuses the existing assignment machinery via octave_value::subsasgn,
77// which can perform all sorts of conversions, but is relatively slow.
78
79class scalar_col_helper_def : public scalar_col_helper
80{
81 std::list<octave_value_list> idx_list;
82 octave_value resval;
83public:
84 scalar_col_helper_def (const octave_value& val, const dim_vector& dims)
85 : idx_list (1), resval (val)
86 {
87 idx_list.front ().resize (1);
88 if (resval.dims () != dims)
89 resval.resize (dims);
90 }
91 ~scalar_col_helper_def (void) { }
92
93 bool collect (octave_idx_type i, const octave_value& val)
94 {
95 if (val.numel () == 1)
96 {
97 idx_list.front ()(0) = static_cast<double> (i + 1);
98 resval = resval.subsasgn ("(", idx_list, val);
99 }
100 else
101 error ("cellfun: expecting all values to be scalars for UniformOutput = true");
102
103 return true;
104 }
105 octave_value result (void)
106 {
107 return resval;
108 }
109};
110
111template <class T>
112static bool can_extract (const octave_value& val)
113{ return false; }
114
115#define DEF_CAN_EXTRACT(T, CLASS) \
116template <> \
117bool can_extract<T> (const octave_value& val) \
118{ return val.type_id () == octave_ ## CLASS::static_type_id (); }
119
120DEF_CAN_EXTRACT (double, scalar);
121DEF_CAN_EXTRACT (float, float_scalar);
122DEF_CAN_EXTRACT (bool, bool);
123DEF_CAN_EXTRACT (octave_int8, int8_scalar);
124DEF_CAN_EXTRACT (octave_int16, int16_scalar);
125DEF_CAN_EXTRACT (octave_int32, int32_scalar);
126DEF_CAN_EXTRACT (octave_int64, int64_scalar);
127DEF_CAN_EXTRACT (octave_uint8, uint8_scalar);
128DEF_CAN_EXTRACT (octave_uint16, uint16_scalar);
129DEF_CAN_EXTRACT (octave_uint32, uint32_scalar);
130DEF_CAN_EXTRACT (octave_uint64, uint64_scalar);
131
132template <>
133bool can_extract<Complex> (const octave_value& val)
134{
135 int t = val.type_id ();
136 return (t == octave_complex::static_type_id ()
137 || t == octave_scalar::static_type_id ());
138}
139
140template <>
141bool can_extract<FloatComplex> (const octave_value& val)
142{
143 int t = val.type_id ();
144 return (t == octave_float_complex::static_type_id ()
145 || t == octave_float_scalar::static_type_id ());
146}
147
148// This specializes for collecting elements of a single type, by accessing
149// an array directly. If the scalar is not valid, it returns false.
150
151template <class NDA>
152class scalar_col_helper_nda : public scalar_col_helper
153{
154 NDA arrayval;
155 typedef typename NDA::element_type T;
156public:
157 scalar_col_helper_nda (const octave_value& val, const dim_vector& dims)
158 : arrayval (dims)
159 {
160 arrayval(0) = octave_value_extract<T> (val);
161 }
162 ~scalar_col_helper_nda (void) { }
163
164 bool collect (octave_idx_type i, const octave_value& val)
165 {
166 bool retval = can_extract<T> (val);
167 if (retval)
168 arrayval(i) = octave_value_extract<T> (val);
169 return retval;
170 }
171 octave_value result (void)
172 {
173 return arrayval;
174 }
175};
176
177template class scalar_col_helper_nda<NDArray>;
178template class scalar_col_helper_nda<FloatNDArray>;
179template class scalar_col_helper_nda<ComplexNDArray>;
180template class scalar_col_helper_nda<FloatComplexNDArray>;
181template class scalar_col_helper_nda<boolNDArray>;
182template class scalar_col_helper_nda<int8NDArray>;
183template class scalar_col_helper_nda<int16NDArray>;
184template class scalar_col_helper_nda<int32NDArray>;
185template class scalar_col_helper_nda<int64NDArray>;
186template class scalar_col_helper_nda<uint8NDArray>;
187template class scalar_col_helper_nda<uint16NDArray>;
188template class scalar_col_helper_nda<uint32NDArray>;
189template class scalar_col_helper_nda<uint64NDArray>;
190
191// the virtual constructor.
192scalar_col_helper *
193make_col_helper (const octave_value& val, const dim_vector& dims)
194{
195 scalar_col_helper *retval;
196
197 // No need to check numel() here.
198 switch (val.builtin_type ())
199 {
200#define ARRAYCASE(BTYP, ARRAY) \
201 case BTYP: \
202 retval = new scalar_col_helper_nda<ARRAY> (val, dims); \
203 break
204
205 ARRAYCASE (btyp_double, NDArray);
206 ARRAYCASE (btyp_float, FloatNDArray);
207 ARRAYCASE (btyp_complex, ComplexNDArray);
208 ARRAYCASE (btyp_float_complex, FloatComplexNDArray);
209 ARRAYCASE (btyp_bool, boolNDArray);
210 ARRAYCASE (btyp_int8, int8NDArray);
211 ARRAYCASE (btyp_int16, int16NDArray);
212 ARRAYCASE (btyp_int32, int32NDArray);
213 ARRAYCASE (btyp_int64, int64NDArray);
214 ARRAYCASE (btyp_uint8, uint8NDArray);
215 ARRAYCASE (btyp_uint16, uint16NDArray);
216 ARRAYCASE (btyp_uint32, uint32NDArray);
217 ARRAYCASE (btyp_uint64, uint64NDArray);
218 default:
219 retval = new scalar_col_helper_def (val, dims);
220 break;
221 }
222
223 return retval;
224}
225
226static octave_value_list
227get_output_list (octave_idx_type count, octave_idx_type nargout,
228 const octave_value_list& inputlist,
229 octave_value& func,
230 octave_value& error_handler)
231{
232 octave_value_list tmp = func.do_multi_index_op (nargout, inputlist);
233
234 if (error_state)
235 {
236 if (error_handler.is_defined ())
237 {
238 Octave_map msg;
239 msg.assign ("identifier", last_error_id ());
240 msg.assign ("message", last_error_message ());
241 msg.assign ("index", octave_value(double (count + static_cast<octave_idx_type>(1))));
242 octave_value_list errlist = inputlist;
243 errlist.prepend (msg);
244 buffer_error_messages--;
245 error_state = 0;
246 tmp = error_handler.do_multi_index_op (nargout, errlist);
247 buffer_error_messages++;
248
249 if (error_state)
250 tmp.clear ();
251 }
252 else
253 tmp.clear ();
254 }
255
256 return tmp;
257}
258
259DEFUN_DLD (cellfun, args, nargout,
260 "-*- texinfo -*-\n\
261@deftypefn {Loadable Function} {} cellfun (@var{name}, @var{c})\n\
262@deftypefnx {Loadable Function} {} cellfun (\"size\", @var{c}, @var{k})\n\
263@deftypefnx {Loadable Function} {} cellfun (\"isclass\", @var{c}, @var{class})\n\
264@deftypefnx {Loadable Function} {} cellfun (@var{func}, @var{c})\n\
265@deftypefnx {Loadable Function} {} cellfun (@var{func}, @var{c}, @var{d})\n\
266@deftypefnx {Loadable Function} {[@var{a}, @dots{}] =} cellfun (@dots{})\n\
267@deftypefnx {Loadable Function} {} cellfun (@dots{}, 'ErrorHandler', @var{errfunc})\n\
268@deftypefnx {Loadable Function} {} cellfun (@dots{}, 'UniformOutput', @var{val})\n\
269\n\
270Evaluate the function named @var{name} on the elements of the cell array\n\
271@var{c}. Elements in @var{c} are passed on to the named function\n\
272individually. The function @var{name} can be one of the functions\n\
273\n\
274@table @code\n\
275@item isempty\n\
276Return 1 for empty elements.\n\
277@item islogical\n\
278Return 1 for logical elements.\n\
279@item isreal\n\
280Return 1 for real elements.\n\
281@item length\n\
282Return a vector of the lengths of cell elements.\n\
283@item ndims\n\
284Return the number of dimensions of each element.\n\
285@item prodofsize\n\
286Return the product of dimensions of each element.\n\
287@item size\n\
288Return the size along the @var{k}-th dimension.\n\
289@item isclass\n\
290Return 1 for elements of @var{class}.\n\
291@end table\n\
292\n\
293Additionally, @code{cellfun} accepts an arbitrary function @var{func}\n\
294in the form of an inline function, function handle, or the name of a\n\
295function (in a character string). In the case of a character string\n\
296argument, the function must accept a single argument named @var{x}, and\n\
297it must return a string value. The function can take one or more arguments,\n\
298with the inputs args given by @var{c}, @var{d}, etc. Equally the function\n\
299can return one or more output arguments. For example\n\
300\n\
301@example\n\
302@group\n\
303cellfun (@@atan2, @{1, 0@}, @{0, 1@})\n\
304 @result{}ans = [1.57080 0.00000]\n\
305@end group\n\
306@end example\n\
307\n\
308The number of output arguments of @code{cellfun} matches the number of output\n\
309arguments of the function. The outputs of the function will be collected into the\n\
310output arguments of @code{cellfun} like this:\n\
311\n\
312@example\n\
313@group\n\
314function [a, b] = twoouts (x)\n\
315 a = x;\n\
316 b = x*x;\n\
317endfunction\n\
318[aa, bb] = cellfun(@@twoouts, @{1, 2, 3@})\n\
319 @result{}\n\
320 aa = \n\
321 1 2 3\n\
322 bb =\n\
323 1 4 9\n\
324@end group\n\
325@end example\n\
326Note that per default the output argument(s) are arrays of the same size as the\n\
327input arguments.\n\
328Input arguments that are singleton (1x1) cells will be automatically expanded\n\
329to the size of the other arguments.\n\
330\n\
331If the parameter 'UniformOutput' is set to true (the default), then the function\n\
332must return scalars which will be concatenated into the\n\
333return array(s). If 'UniformOutput' is false, the outputs are concatenated into\n\
334a cell array (or cell arrays). For example\n\
335\n\
336@example\n\
337@group\n\
338cellfun (\"tolower(x)\", @{\"Foo\", \"Bar\", \"FooBar\"@},\n\
339 \"UniformOutput\",false)\n\
340@result{} ans = @{\"foo\", \"bar\", \"foobar\"@}\n\
341@end group\n\
342@end example\n\
343\n\
344Given the parameter 'ErrorHandler', then @var{errfunc} defines a function to\n\
345call in case @var{func} generates an error. The form of the function is\n\
346\n\
347@example\n\
348function [@dots{}] = errfunc (@var{s}, @dots{})\n\
349@end example\n\
350\n\
351where there is an additional input argument to @var{errfunc} relative to\n\
352@var{func}, given by @var{s}. This is a structure with the elements\n\
353'identifier', 'message' and 'index', giving respectively the error\n\
354identifier, the error message, and the index into the input arguments\n\
355of the element that caused the error. For example\n\
356\n\
357@example\n\
358@group\n\
359function y = foo (s, x), y = NaN; endfunction\n\
360cellfun (@@factorial, @{-1,2@},'ErrorHandler',@@foo)\n\
361@result{} ans = [NaN 2]\n\
362@end group\n\
363@end example\n\
364\n\
365@seealso{isempty, islogical, isreal, length, ndims, numel, size}\n\
366@end deftypefn")
367{
368 octave_value_list retval;
369 int nargin = args.length ();
370 int nargout1 = (nargout < 1 ? 1 : nargout);
371
372 if (nargin < 2)
373 {
374 error ("cellfun: you must supply at least 2 arguments");
375 print_usage ();
376 return retval;
377 }
378
379 octave_value func = args(0);
380
381 if (! args(1).is_cell ())
382 {
383 error ("cellfun: second argument must be a cell array");
384
385 return retval;
386 }
387
388 if (func.is_string ())
389 {
390 const Cell f_args = args(1).cell_value ();
391
392 octave_idx_type k = f_args.numel ();
393
394 std::string name = func.string_value ();
395
396 if (name == "isempty")
397 {
398 boolNDArray result (f_args.dims ());
399 for (octave_idx_type count = 0; count < k ; count++)
400 result(count) = f_args.elem(count).is_empty ();
401 retval(0) = result;
402 }
403 else if (name == "islogical")
404 {
405 boolNDArray result (f_args.dims ());
406 for (octave_idx_type count= 0; count < k ; count++)
407 result(count) = f_args.elem(count).is_bool_type ();
408 retval(0) = result;
409 }
410 else if (name == "isreal")
411 {
412 boolNDArray result (f_args.dims ());
413 for (octave_idx_type count= 0; count < k ; count++)
414 result(count) = f_args.elem(count).is_real_type ();
415 retval(0) = result;
416 }
417 else if (name == "length")
418 {
419 NDArray result (f_args.dims ());
420 for (octave_idx_type count= 0; count < k ; count++)
421 result(count) = static_cast<double> (f_args.elem(count).length ());
422 retval(0) = result;
423 }
424 else if (name == "ndims")
425 {
426 NDArray result (f_args.dims ());
427 for (octave_idx_type count = 0; count < k ; count++)
428 result(count) = static_cast<double> (f_args.elem(count).ndims ());
429 retval(0) = result;
430 }
431 else if (name == "prodofsize" || name == "numel")
432 {
433 NDArray result (f_args.dims ());
434 for (octave_idx_type count = 0; count < k ; count++)
435 result(count) = static_cast<double> (f_args.elem(count).numel ());
436 retval(0) = result;
437 }
438 else if (name == "size")
439 {
440 if (nargin == 3)
441 {
442 int d = args(2).nint_value () - 1;
443
444 if (d < 0)
445 error ("cellfun: third argument must be a positive integer");
446
447 if (! error_state)
448 {
449 NDArray result (f_args.dims ());
450 for (octave_idx_type count = 0; count < k ; count++)
451 {
452 dim_vector dv = f_args.elem(count).dims ();
453 if (d < dv.length ())
454 result(count) = static_cast<double> (dv(d));
455 else
456 result(count) = 1.0;
457 }
458 retval(0) = result;
459 }
460 }
461 else
462 error ("not enough arguments for `size'");
463 }
464 else if (name == "isclass")
465 {
466 if (nargin == 3)
467 {
468 std::string class_name = args(2).string_value();
469 boolNDArray result (f_args.dims ());
470 for (octave_idx_type count = 0; count < k ; count++)
471 result(count) = (f_args.elem(count).class_name() == class_name);
472
473 retval(0) = result;
474 }
475 else
476 error ("not enough arguments for `isclass'");
477 }
478 else if (name == "subsref" && nargin == 5 && nargout == 1
479 && args(2).numel () == 1 && args(2).is_cell ()
480 && args(3).is_string ()
481 && args(3).xtolower ().string_value () == "uniformoutput"
482 && ! args(4).bool_value () && ! error_state)
483 {
484 // This optimizes the case of applying the same index expression to
485 // multiple values. We decode the subscript just once. uniformoutput must
486 // be set to false.
487
488 const Cell tmpc = args(2).cell_value ();
489 octave_value subs = tmpc(0);
490
491 std::string type;
492 std::list<octave_value_list> idx;
493 decode_subscripts ("subsref", subs, type, idx);
494
495 if (! error_state)
496 {
497 Cell result (f_args.dims ());
498 for (octave_idx_type count = 0; count < k && ! error_state; count++)
499 {
500 octave_value tmp = f_args.elem (count);
501 result(count) = tmp.subsref (type, idx);
502 }
503
504 retval(0) = result;
505 }
506 }
507 else
508 {
509 if (! valid_identifier (name))
510 {
511
512 std::string fcn_name = unique_symbol_name ("__cellfun_fcn_");
513 std::string fname = "function y = ";
514 fname.append (fcn_name);
515 fname.append ("(x) y = ");
516 octave_function *ptr_func = extract_function (args(0), "cellfun",
517 fcn_name, fname, "; endfunction");
518 if (ptr_func && ! error_state)
519 func = octave_value (ptr_func, true);
520 }
521
522 func = symbol_table::find_function (name);
523 if (func.is_undefined ())
524 error ("cellfun: invalid function name: %s", name.c_str ());
525 }
526 }
527
528 if (error_state || ! retval.empty ())
529 return retval;
530
531 if (func.is_function_handle () || func.is_inline_function ()
532 || func.is_function ())
533 {
534 unwind_protect frame;
535 frame.protect_var (buffer_error_messages);
536
537 bool uniform_output = true;
538 octave_value error_handler;
539
540 while (nargin > 3 && args(nargin-2).is_string())
541 {
542 std::string arg = args(nargin-2).string_value();
543
544 std::transform (arg.begin (), arg.end (),
545 arg.begin (), tolower);
546
547 if (arg == "uniformoutput")
548 uniform_output = args(nargin-1).bool_value();
549 else if (arg == "errorhandler")
550 {
551 if (args(nargin-1).is_function_handle () ||
552 args(nargin-1).is_inline_function ())
553 {
554 error_handler = args(nargin-1);
555 }
556 else if (args(nargin-1).is_string ())
557 {
558 std::string err_name = args(nargin-1).string_value ();
559 error_handler = symbol_table::find_function (err_name);
560 if (error_handler.is_undefined ())
561 {
562 error ("cellfun: invalid function name: %s", err_name.c_str ());
563 break;
564 }
565 }
566 else
567 {
568 error ("invalid errorhandler value");
569 break;
570 }
571 }
572 else
573 {
574 error ("cellfun: unrecognized parameter %s",
575 arg.c_str());
576 break;
577 }
578
579 nargin -= 2;
580 }
581
582 nargin -= 1;
583
584 octave_value_list inputlist (nargin, octave_value ());
585
586 OCTAVE_LOCAL_BUFFER (Cell, inputs, nargin);
587 OCTAVE_LOCAL_BUFFER (bool, mask, nargin);
588
589 // This is to prevent copy-on-write.
590 const Cell *cinputs = inputs;
591
592 octave_idx_type k = 1;
593
594 dim_vector fdims (1, 1);
595
596 if (error_state)
597 return octave_value_list ();
598
599 for (int j = 0; j < nargin; j++)
600 {
601 if (! args(j+1).is_cell ())
602 {
603 error ("cellfun: arguments must be cells");
604 return octave_value_list ();
605 }
606
607 inputs[j] = args(j+1).cell_value ();
608 mask[j] = inputs[j].numel () != 1;
609 if (! mask[j])
610 inputlist(j) = cinputs[j](0);
611 }
612
613 for (int j = 0; j < nargin; j++)
614 {
615 if (mask[j])
616 {
617 fdims = inputs[j].dims ();
618 k = inputs[j].numel ();
619 for (int i = j+1; i < nargin; i++)
620 {
621 if (mask[i] && inputs[i].dims () != fdims)
622 {
623 error ("cellfun: Dimensions mismatch.");
624 return octave_value_list ();
625 }
626 }
627 break;
628 }
629 }
630
631 if (error_handler.is_defined ())
632 buffer_error_messages++;
633
634 if (uniform_output)
635 {
636 OCTAVE_LOCAL_BUFFER (std::auto_ptr<scalar_col_helper>, retptr, nargout1);
637
638 for (octave_idx_type count = 0; count < k ; count++)
639 {
640 for (int j = 0; j < nargin; j++)
641 {
642 if (mask[j])
643 inputlist.xelem (j) = cinputs[j](count);
644 }
645
646 const octave_value_list tmp = get_output_list (count, nargout, inputlist,
647 func, error_handler);
648
649 if (error_state)
650 return retval;
651
652 if (tmp.length () < nargout1)
653 {
654 if (tmp.length () < nargout)
655 {
656 error ("cellfun: too many output arguments");
657 return octave_value_list ();
658 }
659 else
660 nargout1 = 0;
661 }
662
663 if (count == 0)
664 {
665 for (int j = 0; j < nargout1; j++)
666 {
667 octave_value val = tmp(j);
668
669 if (val.numel () == 1)
670 retptr[j].reset (make_col_helper (val, fdims));
671 else
672 {
673 error ("cellfun: expecting all values to be scalars for UniformOutput = true");
674 break;
675 }
676 }
677 }
678 else
679 {
680 for (int j = 0; j < nargout1; j++)
681 {
682 octave_value val = tmp(j);
683
684 if (! retptr[j]->collect (count, val))
685 {
686 // FIXME: A more elaborate structure would allow again a virtual
687 // constructor here.
688 retptr[j].reset (new scalar_col_helper_def (retptr[j]->result (),
689 fdims));
690 retptr[j]->collect (count, val);
691 }
692 }
693 }
694
695 if (error_state)
696 break;
697 }
698
699 retval.resize (nargout1);
700 for (int j = 0; j < nargout1; j++)
701 {
702 if (retptr[j].get ())
703 retval(j) = retptr[j]->result ();
704 else
705 retval(j) = Matrix ();
706 }
707 }
708 else
709 {
710 OCTAVE_LOCAL_BUFFER (Cell, results, nargout1);
711 for (int j = 0; j < nargout1; j++)
712 results[j].resize (fdims);
713
714 for (octave_idx_type count = 0; count < k ; count++)
715 {
716 for (int j = 0; j < nargin; j++)
717 {
718 if (mask[j])
719 inputlist.xelem (j) = cinputs[j](count);
720 }
721
722 const octave_value_list tmp = get_output_list (count, nargout, inputlist,
723 func, error_handler);
724
725 if (error_state)
726 return retval;
727
728 if (tmp.length () < nargout1)
729 {
730 if (tmp.length () < nargout)
731 {
732 error ("cellfun: too many output arguments");
733 return octave_value_list ();
734 }
735 else
736 nargout1 = 0;
737 }
738
739
740 for (int j = 0; j < nargout1; j++)
741 results[j](count) = tmp(j);
742 }
743
744 retval.resize(nargout1);
745 for (int j = 0; j < nargout1; j++)
746 retval(j) = results[j];
747 }
748 }
749 else
750 error ("cellfun: first argument must be a string or function handle");
751
752 return retval;
753}
754
755/*
756
757%% Test function to check the "Errorhandler" option
758%!function [z] = cellfunerror (S, varargin)
759%! z = S;
760%! endfunction
761
762%% First input argument can be a string, an inline function,
763%% a function_handle or an anonymous function
764%!test
765%! A = cellfun ("islogical", {true, 0.1, false, i*2});
766%! assert (A, [true, false, true, false]);
767%!test
768%! A = cellfun (inline ("islogical (x)", "x"), {true, 0.1, false, i*2});
769%! assert (A, [true, false, true, false]);
770%!test
771%! A = cellfun (@islogical, {true, 0.1, false, i*2});
772%! assert (A, [true, false, true, false]);
773%!test
774%! A = cellfun (@(x) islogical(x), {true, 0.1, false, i*2});
775%! assert (A, [true, false, true, false]);
776
777%% First input argument can be the special string "isreal",
778%% "isempty", "islogical", "length", "ndims" or "prodofsize"
779%!test
780%! A = cellfun ("isreal", {true, 0.1, false, i*2, [], "abc"});
781%! assert (A, [true, true, true, false, true, false]);
782%!test
783%! A = cellfun ("isempty", {true, 0.1, false, i*2, [], "abc"});
784%! assert (A, [false, false, false, false, true, false]);
785%!test
786%! A = cellfun ("islogical", {true, 0.1, false, i*2, [], "abc"});
787%! assert (A, [true, false, true, false, false, false]);
788%!test
789%! A = cellfun ("length", {true, 0.1, false, i*2, [], "abc"});
790%! assert (A, [1, 1, 1, 1, 0, 3]);
791%!test
792%! A = cellfun ("ndims", {[1, 2; 3, 4]; (cell (1,2,3,4))});
793%! assert (A, [2; 4]);
794%!test
795%! A = cellfun ("prodofsize", {[1, 2; 3, 4], (cell (1,2,3,4))});
796%! assert (A, [4, 24]);
797
798%% Number of input and output arguments may not be limited to one
799%!test
800%! A = cellfun (@(x,y,z) x + y + z, {1, 1, 1}, {2, 2, 2}, {3, 4, 5});
801%! assert (A, [6, 7, 8]);
802%!test
803%! A = cellfun (@(x,y,z) x + y + z, {1, 1, 1}, {2, 2, 2}, {3, 4, 5}, \
804%! "UniformOutput", false);
805%! assert (A, {6, 7, 8});
806%!test %% Two input arguments of different types
807%! A = cellfun (@(x,y) islogical (x) && ischar (y), {false, true}, {"a", 3});
808%! assert (A, [true, false]);
809%!test %% Pass another variable to the anonymous function
810%! y = true; A = cellfun (@(x) islogical (x) && y, {false, 0.3});
811%! assert (A, [true, false]);
812%!test %% Three ouptut arguments of different type
813%! [A, B, C] = cellfun (@find, {10, 11; 0, 12}, "UniformOutput", false);
814%! assert (isequal (A, {true, true; [], true}));
815%! assert (isequal (B, {true, true; [], true}));
816%! assert (isequal (C, {10, 11; [], 12}));
817
818%% Input arguments can be of type cell array of logical
819%!test
820%! A = cellfun (@(x,y) x == y, {false, true}, {true, true});
821%! assert (A, [false, true]);
822%!test
823%! A = cellfun (@(x,y) x == y, {false; true}, {true; true}, \
824%! "UniformOutput", true);
825%! assert (A, [false; true]);
826%!test
827%! A = cellfun (@(x) x, {false, true; false, true}, "UniformOutput", false);
828%! assert (A, {false, true; false, true});
829%!test %% Three ouptut arguments of same type
830%! [A, B, C] = cellfun (@find, {true, false; false, true}, \
831%! "UniformOutput", false);
832%! assert (isequal (A, {true, []; [], true}));
833%! assert (isequal (B, {true, []; [], true}));
834%! assert (isequal (C, {true, []; [], true}));
835%!test
836%! A = cellfun (@(x,y) cell2str (x,y), {true}, {true}, \
837%! "ErrorHandler", @cellfunerror);
838%! assert (isfield (A, "identifier"), true);
839%! assert (isfield (A, "message"), true);
840%! assert (isfield (A, "index"), true);
841%! assert (isempty (A.message), false);
842%! assert (A.index, 1);
843%!test %% Overwriting setting of "UniformOutput" true
844%! A = cellfun (@(x,y) cell2str (x,y), {true}, {true}, \
845%! "UniformOutput", true, "ErrorHandler", @cellfunerror);
846%! assert (isfield (A, "identifier"), true);
847%! assert (isfield (A, "message"), true);
848%! assert (isfield (A, "index"), true);
849%! assert (isempty (A.message), false);
850%! assert (A.index, 1);
851
852%% Input arguments can be of type cell array of numeric
853%!test
854%! A = cellfun (@(x,y) x>y, {1.1, 4.2}, {3.1, 2+3*i});
855%! assert (A, [false, true]);
856%!test
857%! A = cellfun (@(x,y) x>y, {1.1, 4.2; 2, 4}, {3.1, 2; 2, 4+2*i}, \
858%! "UniformOutput", true);
859%! assert (A, [false, true; false, false]);
860%!test
861%! A = cellfun (@(x,y) x:y, {1.1, 4}, {3.1, 6}, "UniformOutput", false);
862%! assert (isequal (A{1}, [1.1, 2.1, 3.1]));
863%! assert (isequal (A{2}, [4, 5, 6]));
864%!test %% Three ouptut arguments of different type
865%! [A, B, C] = cellfun (@find, {10, 11; 0, 12}, "UniformOutput", false);
866%! assert (isequal (A, {true, true; [], true}));
867%! assert (isequal (B, {true, true; [], true}));
868%! assert (isequal (C, {10, 11; [], 12}));
869%!test
870%! A = cellfun (@(x,y) cell2str(x,y), {1.1, 4}, {3.1, 6}, \
871%! "ErrorHandler", @cellfunerror);
872%! B = isfield (A(1), "message") && isfield (A(1), "index");
873%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]);
874%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]);
875%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]);
876%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]);
877%! assert ([A(1).index, A(2).index], [1, 2]);
878%!test %% Overwriting setting of "UniformOutput" true
879%! A = cellfun (@(x,y) cell2str(x,y), {1.1, 4}, {3.1, 6}, \
880%! "UniformOutput", true, "ErrorHandler", @cellfunerror);
881%! B = isfield (A(1), "message") && isfield (A(1), "index");
882%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]);
883%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]);
884%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]);
885%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]);
886%! assert ([A(1).index, A(2).index], [1, 2]);
887
888%% Input arguments can be of type cell arrays of character or strings
889%!error %% "UniformOutput" false should be used
890%! A = cellfun (@(x,y) x>y, {"ad", "c", "ghi"}, {"cc", "d", "fgh"});
891%!test
892%! A = cellfun (@(x,y) x>y, {"a"; "f"}, {"c"; "d"}, "UniformOutput", true);
893%! assert (A, [false; true]);
894%!test
895%! A = cellfun (@(x,y) x:y, {"a", "d"}, {"c", "f"}, "UniformOutput", false);
896%! assert (A, {"abc", "def"});
897%!test
898%! A = cellfun (@(x,y) cell2str(x,y), {"a", "d"}, {"c", "f"}, \
899%! "ErrorHandler", @cellfunerror);
900%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]);
901%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]);
902%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]);
903%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]);
904%! assert ([A(1).index, A(2).index], [1, 2]);
905%!test %% Overwriting setting of "UniformOutput" true
906%! A = cellfun (@(x,y) cell2str(x,y), {"a", "d"}, {"c", "f"}, \
907%! "UniformOutput", true, "ErrorHandler", @cellfunerror);
908%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]);
909%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]);
910%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]);
911%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]);
912%! assert ([A(1).index, A(2).index], [1, 2]);
913
914%% Structures cannot be handled by cellfun
915%!error
916%! vst1.a = 1.1; vst1.b = 4.2; vst2.a = 3.1; vst2.b = 2;
917%! A = cellfun (@(x,y) (x.a < y.a) && (x.b > y.b), vst1, vst2);
918
919%% Input arguments can be of type cell array of cell arrays
920%!test
921%! A = cellfun (@(x,y) x{1} < y{1}, {{1.1}, {4.2}}, {{3.1}, {2}});
922%! assert (A, [1, 0], 1e-16);
923%!test
924%! A = cellfun (@(x,y) x{1} < y{1}, {{1.1}; {4.2}}, {{3.1}; {2}}, \
925%! "UniformOutput", true);
926%! assert (A, [1; 0], 1e-16);
927%!test
928%! A = cellfun (@(x,y) x{1} < y{1}, {{1.1}, {4.2}}, {{3.1}, {2}}, \
929%! "UniformOutput", false);
930%! assert (A, {true, false});
931%!test
932%! A = cellfun (@(x,y) mat2str(x,y), {{1.1}, {4.2}}, {{3.1}, {2}}, \
933%! "ErrorHandler", @cellfunerror);
934%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]);
935%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]);
936%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]);
937%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]);
938%! assert ([A(1).index, A(2).index], [1, 2]);
939%!test %% Overwriting setting of "UniformOutput" true
940%! A = cellfun (@(x,y) mat2str(x,y), {{1.1}, {4.2}}, {{3.1}, {2}}, \
941%! "UniformOutput", true, "ErrorHandler", @cellfunerror);
942%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]);
943%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]);
944%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]);
945%! assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]);
946%! assert ([A(1).index, A(2).index], [1, 2]);
947
948%% Input arguments can be of type cell array of structure arrays
949%!test
950%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3);
951%! A = cellfun (@(x,y) (x.a == y.a) && (x.b < y.b), {a}, {b});
952%! assert (A, true);
953%!test
954%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3);
955%! A = cellfun (@(x,y) (x.a == y.a) && (x.b < y.b) , {a}, {b}, \
956%! "UniformOutput", true);
957%! assert (A, true);
958%!test
959%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3);
960%! A = cellfun (@(x,y) (x.a == y.a) && (x.b < y.b) , {a}, {b}, \
961%! "UniformOutput", false);
962%! assert (A, {true});
963%!test
964%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3);
965%! A = cellfun (@(x,y) cell2str (x.a, y.a), {a}, {b}, \
966%! "ErrorHandler", @cellfunerror);
967%! assert (isfield (A, "identifier"), true);
968%! assert (isfield (A, "message"), true);
969%! assert (isfield (A, "index"), true);
970%! assert (isempty (A.message), false);
971%! assert (A.index, 1);
972%!test %% Overwriting setting of "UniformOutput" true
973%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3);
974%! A = cellfun (@(x,y) cell2str (x.a, y.a), {a}, {b}, \
975%! "UniformOutput", true, "ErrorHandler", @cellfunerror);
976%! assert (isfield (A, "identifier"), true);
977%! assert (isfield (A, "message"), true);
978%! assert (isfield (A, "index"), true);
979%! assert (isempty (A.message), false);
980%! assert (A.index, 1);
981
982%% A lot of other tests
983%!error(cellfun(1))
984%!error(cellfun('isclass',1))
985%!error(cellfun('size',1))
986%!error(cellfun(@sin,{[]},'BadParam',false))
987%!error(cellfun(@sin,{[]},'UniformOuput'))
988%!error(cellfun(@sin,{[]},'ErrorHandler'))
989%!assert(cellfun(@sin,{0,1}),sin([0,1]))
990%!assert(cellfun(inline('sin(x)'),{0,1}),sin([0,1]))
991%!assert(cellfun('sin',{0,1}),sin([0,1]))
992%!assert(cellfun('isempty',{1,[]}),[false,true])
993%!assert(cellfun('islogical',{false,pi}),[true,false])
994%!assert(cellfun('isreal',{1i,1}),[false,true])
995%!assert(cellfun('length',{zeros(2,2),1}),[2,1])
996%!assert(cellfun('prodofsize',{zeros(2,2),1}),[4,1])
997%!assert(cellfun('ndims',{zeros([2,2,2]),1}),[3,2])
998%!assert(cellfun('isclass',{zeros([2,2,2]),'test'},'double'),[true,false])
999%!assert(cellfun('size',{zeros([1,2,3]),1},1),[1,1])
1000%!assert(cellfun('size',{zeros([1,2,3]),1},2),[2,1])
1001%!assert(cellfun('size',{zeros([1,2,3]),1},3),[3,1])
1002%!assert(cellfun(@atan2,{1,1},{1,2}),[atan2(1,1),atan2(1,2)])
1003%!assert(cellfun(@atan2,{1,1},{1,2},'UniformOutput',false),{atan2(1,1),atan2(1,2)})
1004%!assert(cellfun(@sin,{1,2;3,4}),sin([1,2;3,4]))
1005%!assert(cellfun(@atan2,{1,1;1,1},{1,2;1,2}),atan2([1,1;1,1],[1,2;1,2]))
1006%!error(cellfun(@factorial,{-1,3}))
1007%!assert(cellfun(@factorial,{-1,3},'ErrorHandler',@(x,y) NaN),[NaN,6])
1008%!test
1009%! [a,b,c]=cellfun(@fileparts,{fullfile("a","b","c.d"),fullfile("e","f","g.h")},'UniformOutput',false);
1010%! assert(a,{fullfile("a","b"),fullfile("e","f")})
1011%! assert(b,{'c','g'})
1012%! assert(c,{'.d','.h'})
1013
1014*/
1015
1016static void
1017do_num2cell_helper (const dim_vector& dv,
1018 const Array<int>& dimv,
1019 dim_vector& celldv, dim_vector& arraydv,
1020 Array<int>& perm)
1021{
1022 int dvl = dimv.length ();
1023 int maxd = dv.length ();
1024 celldv = dv;
1025 for (int i = 0; i < dvl; i++)
1026 maxd = std::max (maxd, dimv(i));
1027 if (maxd > dv.length ())
1028 celldv.resize (maxd, 1);
1029 arraydv = celldv;
1030
1031 OCTAVE_LOCAL_BUFFER_INIT (bool, sing, maxd, false);
1032
1033 perm.clear (maxd);
1034 for (int i = 0; i < dvl; i++)
1035 {
1036 int k = dimv(i) - 1;
1037 if (k < 0)
1038 {
1039 error ("num2cell: dimension indices must be positive");
1040 return;
1041 }
1042 else if (i > 0 && k < dimv(i-1) - 1)
1043 {
1044 error ("num2cell: dimension indices must be strictly increasing");
1045 return;
1046 }
1047
1048 sing[k] = true;
1049 perm(i) = k;
1050 }
1051
1052 for (int k = 0, i = dvl; k < maxd; k++)
1053 if (! sing[k])
1054 perm(i++) = k;
1055
1056 for (int i = 0; i < maxd; i++)
1057 if (sing[i])
1058 celldv(i) = 1;
1059 else
1060 arraydv(i) = 1;
1061}
1062
1063template<class NDA>
1064static Cell
1065do_num2cell (const NDA& array, const Array<int>& dimv)
1066{
1067 if (dimv.is_empty ())
1068 {
1069 Cell retval (array.dims ());
1070 octave_idx_type nel = array.numel ();
1071 for (octave_idx_type i = 0; i < nel; i++)
1072 retval.xelem (i) = array(i);
1073
1074 return retval;
1075 }
1076 else
1077 {
1078 dim_vector celldv, arraydv;
1079 Array<int> perm;
1080 do_num2cell_helper (array.dims (), dimv, celldv, arraydv, perm);
1081 if (error_state)
1082 return Cell ();
1083
1084 NDA parray = array.permute (perm);
1085
1086 octave_idx_type nela = arraydv.numel (), nelc = celldv.numel ();
1087 parray = parray.reshape (dim_vector (nela, nelc));
1088
1089 Cell retval (celldv);
1090 for (octave_idx_type i = 0; i < nelc; i++)
1091 {
1092 retval.xelem (i) = NDA (parray.column (i).reshape (arraydv));
1093 }
1094
1095 return retval;
1096 }
1097}
1098
1099
1100DEFUN_DLD (num2cell, args, ,
1101 "-*- texinfo -*-\n\
1102@deftypefn {Loadable Function} {@var{c} =} num2cell (@var{m})\n\
1103@deftypefnx {Loadable Function} {@var{c} =} num2cell (@var{m}, @var{dim})\n\
1104Convert the matrix @var{m} to a cell array. If @var{dim} is defined, the\n\
1105value @var{c} is of dimension 1 in this dimension and the elements of\n\
1106@var{m} are placed into @var{c} in slices. For example:\n\
1107\n\
1108@example\n\
1109@group\n\
1110num2cell([1,2;3,4])\n\
1111 @result{} ans =\n\
1112 @{\n\
1113 [1,1] = 1\n\
1114 [2,1] = 3\n\
1115 [1,2] = 2\n\
1116 [2,2] = 4\n\
1117 @}\n\
1118num2cell([1,2;3,4],1)\n\
1119 @result{} ans =\n\
1120 @{\n\
1121 [1,1] =\n\
1122 1\n\
1123 3\n\
1124 [1,2] =\n\
1125 2\n\
1126 4\n\
1127 @}\n\
1128@end group\n\
1129@end example\n\
1130\n\
1131@seealso{mat2cell}\n\
1132@end deftypefn")
1133{
1134 int nargin = args.length();
1135 octave_value retval;
1136
1137 if (nargin < 1 || nargin > 2)
1138 print_usage ();
1139 else
1140 {
1141 octave_value array = args(0);
1142 Array<int> dimv;
1143 if (nargin > 1)
1144 dimv = args (1).int_vector_value (true);
1145
1146 if (error_state)
1147 ;
1148 else if (array.is_bool_type ())
1149 retval = do_num2cell (array.bool_array_value (), dimv);
1150 else if (array.is_char_matrix ())
1151 retval = do_num2cell (array.char_array_value (), dimv);
1152 else if (array.is_numeric_type ())
1153 {
1154 if (array.is_integer_type ())
1155 {
1156 if (array.is_int8_type ())
1157 retval = do_num2cell (array.int8_array_value (), dimv);
1158 else if (array.is_int16_type ())
1159 retval = do_num2cell (array.int16_array_value (), dimv);
1160 else if (array.is_int32_type ())
1161 retval = do_num2cell (array.int32_array_value (), dimv);
1162 else if (array.is_int64_type ())
1163 retval = do_num2cell (array.int64_array_value (), dimv);
1164 else if (array.is_uint8_type ())
1165 retval = do_num2cell (array.uint8_array_value (), dimv);
1166 else if (array.is_uint16_type ())
1167 retval = do_num2cell (array.uint16_array_value (), dimv);
1168 else if (array.is_uint32_type ())
1169 retval = do_num2cell (array.uint32_array_value (), dimv);
1170 else if (array.is_uint64_type ())
1171 retval = do_num2cell (array.uint64_array_value (), dimv);
1172 }
1173 else if (array.is_complex_type ())
1174 {
1175 if (array.is_single_type ())
1176 retval = do_num2cell (array.float_complex_array_value (), dimv);
1177 else
1178 retval = do_num2cell (array.complex_array_value (), dimv);
1179 }
1180 else
1181 {
1182 if (array.is_single_type ())
1183 retval = do_num2cell (array.float_array_value (), dimv);
1184 else
1185 retval = do_num2cell (array.array_value (), dimv);
1186 }
1187 }
1188 else if (array.is_cell () || array.is_map ())
1189 {
1190 dim_vector celldv, arraydv;
1191 Array<int> perm;
1192 do_num2cell_helper (array.dims (), dimv, celldv, arraydv, perm);
1193
1194 if (! error_state)
1195 {
1196 // FIXME: this operation may be rather inefficient.
1197 octave_value parray = array.permute (perm);
1198
1199 octave_idx_type nela = arraydv.numel (), nelc = celldv.numel ();
1200 parray = parray.reshape (dim_vector (nela, nelc));
1201
1202 Cell retcell (celldv);
1203 octave_value_list idx (2);
1204 idx(0) = octave_value::magic_colon_t;
1205
1206 for (octave_idx_type i = 0; i < nelc; i++)
1207 {
1208 idx(1) = i + 1;
1209 octave_value tmp = parray.do_index_op (idx);
1210 retcell(i) = tmp.reshape (arraydv);
1211 }
1212
1213 retval = retcell;
1214 }
1215 }
1216 else
1217 gripe_wrong_type_arg ("num2cell", array);
1218 }
1219
1220 return retval;
1221}
1222
1223/*
1224
1225%!assert(num2cell([1,2;3,4]),{1,2;3,4})
1226%!assert(num2cell([1,2;3,4],1),{[1;3],[2;4]})
1227%!assert(num2cell([1,2;3,4],2),{[1,2];[3,4]})
1228
1229*/
1230
1231DEFUN_DLD (mat2cell, args, ,
1232 "-*- texinfo -*-\n\
1233@deftypefn {Loadable Function} {@var{b} =} mat2cell (@var{a}, @var{m}, @var{n})\n\
1234@deftypefnx {Loadable Function} {@var{b} =} mat2cell (@var{a}, @var{d1}, @var{d2}, @dots{})\n\
1235@deftypefnx {Loadable Function} {@var{b} =} mat2cell (@var{a}, @var{r})\n\
1236Convert the matrix @var{a} to a cell array. If @var{a} is 2-D, then\n\
1237it is required that @code{sum (@var{m}) == size (@var{a}, 1)} and\n\
1238@code{sum (@var{n}) == size (@var{a}, 2)}. Similarly, if @var{a} is\n\
1239a multi-dimensional and the number of dimensional arguments is equal\n\
1240to the dimensions of @var{a}, then it is required that @code{sum (@var{di})\n\
1241== size (@var{a}, i)}.\n\
1242\n\
1243Given a single dimensional argument @var{r}, the other dimensional\n\
1244arguments are assumed to equal @code{size (@var{a},@var{i})}.\n\
1245\n\
1246An example of the use of mat2cell is\n\
1247\n\
1248@example\n\
1249mat2cell (reshape(1:16,4,4),[3,1],[3,1])\n\
1250@result{} @{\n\
1251 [1,1] =\n\
1252\n\
1253 1 5 9\n\
1254 2 6 10\n\
1255 3 7 11\n\
1256\n\
1257 [2,1] =\n\
1258\n\
1259 4 8 12\n\
1260\n\
1261 [1,2] =\n\
1262\n\
1263 13\n\
1264 14\n\
1265 15\n\
1266\n\
1267 [2,2] = 16\n\
1268@}\n\
1269@end example\n\
1270@seealso{num2cell, cell2mat}\n\
1271@end deftypefn")
1272{
1273 int nargin = args.length();
1274 octave_value retval;
1275
1276 if (nargin < 2)
1277 print_usage ();
1278 else
1279 {
1280 dim_vector dv = args(0).dims();
1281 dim_vector new_dv;
1282 new_dv.resize(dv.length());
1283
1284 if (nargin > 2)
1285 {
1286 octave_idx_type nmax = -1;
1287
1288 if (nargin - 1 != dv.length())
1289 error ("mat2cell: Incorrect number of dimensions");
1290 else
1291 {
1292 for (octave_idx_type j = 0; j < dv.length(); j++)
1293 {
1294 ColumnVector d = ColumnVector (args(j+1).vector_value
1295 (false, true));
1296
1297 if (d.length() < 1)
1298 {
1299 error ("mat2cell: dimension can not be empty");
1300 break;
1301 }
1302 else
1303 {
1304 if (nmax < d.length())
1305 nmax = d.length();
1306
1307 for (octave_idx_type i = 1; i < d.length(); i++)
1308 {
1309 OCTAVE_QUIT;
1310
1311 if (d(i) >= 0)
1312 d(i) += d(i-1);
1313 else
1314 {
1315 error ("mat2cell: invalid dimensional argument");
1316 break;
1317 }
1318 }
1319
1320 if (d(0) < 0)
1321 error ("mat2cell: invalid dimensional argument");
1322
1323 if (d(d.length() - 1) != dv(j))
1324 error ("mat2cell: inconsistent dimensions");
1325
1326 if (error_state)
1327 break;
1328
1329 new_dv(j) = d.length();
1330 }
1331 }
1332 }
1333
1334 if (! error_state)
1335 {
1336 // Construct a matrix with the index values
1337 Matrix dimargs(nmax, new_dv.length());
1338 for (octave_idx_type j = 0; j < new_dv.length(); j++)
1339 {
1340 OCTAVE_QUIT;
1341
1342 ColumnVector d = ColumnVector (args(j+1).vector_value
1343 (false, true));
1344
1345 dimargs(0,j) = d(0);
1346 for (octave_idx_type i = 1; i < d.length(); i++)
1347 dimargs(i,j) = dimargs(i-1,j) + d(i);
1348 }
1349
1350
1351 octave_value_list lst (new_dv.length(), octave_value());
1352 Cell ret (new_dv);
1353 octave_idx_type nel = new_dv.numel();
1354 octave_idx_type ntot = 1;
1355
1356 for (int j = 0; j < new_dv.length()-1; j++)
1357 ntot *= new_dv(j);
1358
1359 for (octave_idx_type i = 0; i < nel; i++)
1360 {
1361 octave_idx_type n = ntot;
1362 octave_idx_type ii = i;
1363 for (octave_idx_type j = new_dv.length() - 1; j >= 0; j--)
1364 {
1365 OCTAVE_QUIT;
1366
1367 octave_idx_type idx = ii / n;
1368 lst (j) = Range((idx == 0 ? 1. : dimargs(idx-1,j)+1.),
1369 dimargs(idx,j));
1370 ii = ii % n;
1371 if (j != 0)
1372 n /= new_dv(j-1);
1373 }
1374 ret(i) = octave_value(args(0)).do_index_op(lst, 0);
1375 if (error_state)
1376 break;
1377 }
1378
1379 if (!error_state)
1380 retval = ret;
1381 }
1382 }
1383 else
1384 {
1385 ColumnVector d = ColumnVector (args(1).vector_value
1386 (false, true));
1387
1388 double sumd = 0.;
1389 for (octave_idx_type i = 0; i < d.length(); i++)
1390 {
1391 OCTAVE_QUIT;
1392
1393 if (d(i) >= 0)
1394 sumd += d(i);
1395 else
1396 {
1397 error ("mat2cell: invalid dimensional argument");
1398 break;
1399 }
1400 }
1401
1402 if (sumd != dv(0))
1403 error ("mat2cell: inconsistent dimensions");
1404
1405 new_dv(0) = d.length();
1406 for (octave_idx_type i = 1; i < dv.length(); i++)
1407 new_dv(i) = 1;
1408
1409 if (! error_state)
1410 {
1411 octave_value_list lst (new_dv.length(), octave_value());
1412 Cell ret (new_dv);
1413
1414 for (octave_idx_type i = 1; i < new_dv.length(); i++)
1415 lst (i) = Range (1., static_cast<double>(dv(i)));
1416
1417 double idx = 0.;
1418 for (octave_idx_type i = 0; i < new_dv(0); i++)
1419 {
1420 OCTAVE_QUIT;
1421
1422 lst(0) = Range(idx + 1., idx + d(i));
1423 ret(i) = octave_value(args(0)).do_index_op(lst, 0);
1424 idx += d(i);
1425 if (error_state)
1426 break;
1427 }
1428
1429 if (!error_state)
1430 retval = ret;
1431 }
1432 }
1433 }
1434
1435 return retval;
1436}
1437
1438/*
1439
1440%!test
1441%! x = reshape(1:20,5,4);
1442%! c = mat2cell(x,[3,2],[3,1]);
1443%! assert(c,{[1,6,11;2,7,12;3,8,13],[16;17;18];[4,9,14;5,10,15],[19;20]})
1444
1445%!test
1446%! x = 'abcdefghij';
1447%! c = mat2cell(x,1,[0,4,2,0,4,0]);
1448%! empty1by0str = resize('',1,0);
1449%! assert(c,{empty1by0str,'abcd','ef',empty1by0str,'ghij',empty1by0str})
1450
1451*/
1452
1453// FIXME: it would be nice to allow ranges being handled without a conversion.
1454template <class NDA>
1455static Cell
1456do_cellslices_nda (const NDA& array,
1457 const Array<octave_idx_type>& lb,
1458 const Array<octave_idx_type>& ub,
1459 int dim = -1)
1460{
1461 octave_idx_type n = lb.length ();
1462 Cell retval (1, n);
1463 if (array.is_vector () && (dim == -1
1464 || (dim == 0 && array.columns () == 1)
1465 || (dim == 1 && array.rows () == 1)))
1466 {
1467 for (octave_idx_type i = 0; i < n && ! error_state; i++)
1468 retval(i) = array.index (idx_vector (lb(i) - 1, ub(i)));
1469 }
1470 else
1471 {
1472 const dim_vector dv = array.dims ();
1473 int ndims = dv.length ();
1474 if (dim < 0)
1475 dim = dv.first_non_singleton ();
1476 ndims = std::max (ndims, dim + 1);
1477
1478 Array<idx_vector> idx (ndims, idx_vector::colon);
1479
1480 for (octave_idx_type i = 0; i < n && ! error_state; i++)
1481 {
1482 idx(dim) = idx_vector (lb(i) - 1, ub(i));
1483 retval(i) = array.index (idx);
1484 }
1485 }
1486
1487 return retval;
1488}
1489
1490DEFUN_DLD (cellslices, args, ,
1491 "-*- texinfo -*-\n\
1492@deftypefn {Loadable Function} {@var{sl} =} cellslices (@var{x}, @var{lb}, @var{ub}, @var{dim})\n\
1493Given an array @var{x}, this function produces a cell array of slices from the array\n\
1494determined by the index vectors @var{lb}, @var{ub}, for lower and upper bounds, respectively.\n\
1495In other words, it is equivalent to the following code:\n\
1496\n\
1497@example\n\
1498@group\n\
1499n = length (lb);\n\
1500sl = cell (1, n);\n\
1501for i = 1:length (lb)\n\
1502 sl@{i@} = x(:,@dots{},lb(i):ub(i),@dots{},:);\n\
1503endfor\n\
1504@end group\n\
1505@end example\n\
1506\n\
1507The position of the index is determined by @var{dim}. If not specified, slicing\n\
1508is done along the first non-singleton dimension.\n\
1509@end deftypefn")
1510{
1511 octave_value retval;
1512 int nargin = args.length ();
1513 if (nargin == 3 || nargin == 4)
1514 {
1515 octave_value x = args(0);
1516 Array<octave_idx_type> lb = args(1).octave_idx_type_vector_value ();
1517 Array<octave_idx_type> ub = args(2).octave_idx_type_vector_value ();
1518 int dim = -1;
1519 if (nargin == 4)
1520 {
1521 dim = args(3).int_value () - 1;
1522 if (dim < 0)
1523 error ("cellslices: dim must be a valid dimension");
1524 }
1525
1526 if (! error_state)
1527 {
1528 if (lb.length () != ub.length ())
1529 error ("cellslices: the lengths of lb and ub must match");
1530 else
1531 {
1532 Cell retcell;
1533 if (! x.is_sparse_type () && x.is_matrix_type ())
1534 {
1535 // specialize for some dense arrays.
1536 if (x.is_bool_type ())
1537 retcell = do_cellslices_nda (x.bool_array_value (), lb, ub, dim);
1538 else if (x.is_char_matrix ())
1539 retcell = do_cellslices_nda (x.char_array_value (), lb, ub, dim);
1540 else if (x.is_integer_type ())
1541 {
1542 if (x.is_int8_type ())
1543 retcell = do_cellslices_nda (x.int8_array_value (), lb, ub, dim);
1544 else if (x.is_int16_type ())
1545 retcell = do_cellslices_nda (x.int16_array_value (), lb, ub, dim);
1546 else if (x.is_int32_type ())
1547 retcell = do_cellslices_nda (x.int32_array_value (), lb, ub, dim);
1548 else if (x.is_int64_type ())
1549 retcell = do_cellslices_nda (x.int64_array_value (), lb, ub, dim);
1550 else if (x.is_uint8_type ())
1551 retcell = do_cellslices_nda (x.uint8_array_value (), lb, ub, dim);
1552 else if (x.is_uint16_type ())
1553 retcell = do_cellslices_nda (x.uint16_array_value (), lb, ub, dim);
1554 else if (x.is_uint32_type ())
1555 retcell = do_cellslices_nda (x.uint32_array_value (), lb, ub, dim);
1556 else if (x.is_uint64_type ())
1557 retcell = do_cellslices_nda (x.uint64_array_value (), lb, ub, dim);
1558 }
1559 else if (x.is_complex_type ())
1560 {
1561 if (x.is_single_type ())
1562 retcell = do_cellslices_nda (x.float_complex_array_value (), lb, ub, dim);
1563 else
1564 retcell = do_cellslices_nda (x.complex_array_value (), lb, ub, dim);
1565 }
1566 else
1567 {
1568 if (x.is_single_type ())
1569 retcell = do_cellslices_nda (x.float_array_value (), lb, ub, dim);
1570 else
1571 retcell = do_cellslices_nda (x.array_value (), lb, ub, dim);
1572 }
1573 }
1574 else
1575 {
1576 // generic code.
1577 octave_idx_type n = lb.length ();
1578 retcell = Cell (1, n);
1579 const dim_vector dv = x.dims ();
1580 int ndims = dv.length ();
1581 if (dim < 0)
1582 dim = dv.first_non_singleton ();
1583 ndims = std::max (ndims, dim + 1);
1584 octave_value_list idx (ndims, octave_value::magic_colon_t);
1585 for (octave_idx_type i = 0; i < n && ! error_state; i++)
1586 {
1587 idx(dim) = Range (lb(i), ub(i));
1588 retcell(i) = x.do_index_op (idx);
1589 }
1590 }
1591 if (! error_state)
1592 retval = retcell;
1593 }
1594 }
1595 }
1596 else
1597 print_usage ();
1598
1599 return retval;
1600}
1601
1602/*
1603%!test
1604%! m = [1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12];
1605%! c = cellslices (m, [1, 2], [2, 3], 2);
1606%! assert (c, {[1, 2; 5, 6; 9, 10], [2, 3; 6, 7; 10, 11]});
1607*/