GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
ov-fcn-inline.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2004-2013 David Bateman
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 In addition to the terms of the GPL, you are permitted to link
22 this program with any Open Source program, as defined by the
23 Open Source Initiative (www.opensource.org)
24 
25 */
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 #include <istream>
32 #include <iostream>
33 #include <sstream>
34 #include <vector>
35 
36 #include "oct-locbuf.h"
37 
38 #include "defun.h"
39 #include "error.h"
40 #include "gripes.h"
41 #include "oct-map.h"
42 #include "ov-base.h"
43 #include "ov-fcn-inline.h"
44 #include "ov-usr-fcn.h"
45 #include "pr-output.h"
46 #include "variables.h"
47 #include "parse.h"
48 #include "toplev.h"
49 
50 #include "byte-swap.h"
51 #include "ls-ascii-helper.h"
52 #include "ls-oct-ascii.h"
53 #include "ls-hdf5.h"
54 #include "ls-utils.h"
55 
57 
59  "inline function",
60  "function_handle");
61 
63  const string_vector& a,
64  const std::string& n)
65  : octave_fcn_handle (n), iftext (f), ifargs (a)
66 {
67  // Form a string representing the function.
68 
69  std::ostringstream buf;
70 
71  buf << "@(";
72 
73  for (int i = 0; i < ifargs.length (); i++)
74  {
75  if (i > 0)
76  buf << ", ";
77 
78  buf << ifargs(i);
79  }
80 
81  buf << ") " << iftext;
82 
83  int parse_status;
84  octave_value anon_fcn_handle = eval_string (buf.str (), true, parse_status);
85 
86  if (parse_status == 0)
87  {
88  octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value ();
89 
90  if (fh)
91  {
92  fcn = fh->fcn_val ();
93 
95 
96  if (uf)
97  {
99 
100  if (curr_fcn)
101  {
102  symbol_table::scope_id parent_scope
103  = curr_fcn->parent_fcn_scope ();
104 
105  if (parent_scope < 0)
106  parent_scope = curr_fcn->scope ();
107 
108  uf->stash_parent_fcn_scope (parent_scope);
109  }
110  }
111  }
112  }
113 
114  if (fcn.is_undefined ())
115  error ("inline: unable to define function");
116 }
117 
118 // This function is supplied to allow a Matlab style class structure
119 // to be returned..
122 {
124 
125  m.assign ("version", 1.0);
126  m.assign ("isEmpty", 0.0);
127  m.assign ("expr", fcn_text ());
128 
129  string_vector args = fcn_arg_names ();
130 
131  m.assign ("numArgs", args.length ());
132  m.assign ("args", args);
133 
134  std::ostringstream buf;
135 
136  for (int i = 0; i < args.length (); i++)
137  buf << args(i) << " = INLINE_INPUTS_{" << i + 1 << "}; ";
138 
139  m.assign ("inputExpr", buf.str ());
140 
141  return m;
142 }
143 
144 bool
146 {
147  os << "# nargs: " << ifargs.length () << "\n";
148  for (int i = 0; i < ifargs.length (); i++)
149  os << ifargs(i) << "\n";
150  if (nm.length () < 1)
151  // Write an invalid value to flag empty fcn handle name.
152  os << "0\n";
153  else
154  os << nm << "\n";
155  os << iftext << "\n";
156  return true;
157 }
158 
159 bool
161 {
162  int nargs;
163  if (extract_keyword (is, "nargs", nargs, true))
164  {
165  ifargs.resize (nargs);
166  for (int i = 0; i < nargs; i++)
167  is >> ifargs(i);
168  is >> nm;
169  if (nm == "0")
170  nm = "";
171 
173 
174  std::string buf;
175 
176  if (is)
177  {
178 
179  // Get a line of text whitespace characters included,
180  // leaving newline in the stream.
181  buf = read_until_newline (is, true);
182  }
183 
184  iftext = buf;
185 
186  octave_fcn_inline tmp (iftext, ifargs, nm);
187  fcn = tmp.fcn;
188 
189  return true;
190  }
191  else
192  return false;
193 }
194 
195 bool
196 octave_fcn_inline::save_binary (std::ostream& os, bool&)
197 {
198  int32_t tmp = ifargs.length ();
199  os.write (reinterpret_cast<char *> (&tmp), 4);
200  for (int i = 0; i < ifargs.length (); i++)
201  {
202  tmp = ifargs(i).length ();
203  os.write (reinterpret_cast<char *> (&tmp), 4);
204  os.write (ifargs(i).c_str (), ifargs(i).length ());
205  }
206  tmp = nm.length ();
207  os.write (reinterpret_cast<char *> (&tmp), 4);
208  os.write (nm.c_str (), nm.length ());
209  tmp = iftext.length ();
210  os.write (reinterpret_cast<char *> (&tmp), 4);
211  os.write (iftext.c_str (), iftext.length ());
212  return true;
213 }
214 
215 bool
216 octave_fcn_inline::load_binary (std::istream& is, bool swap,
218 {
219  int32_t nargs;
220  if (! is.read (reinterpret_cast<char *> (&nargs), 4))
221  return false;
222  if (swap)
223  swap_bytes<4> (&nargs);
224 
225  if (nargs < 1)
226  return false;
227  else
228  {
229  int32_t tmp;
230  ifargs.resize (nargs);
231  for (int i = 0; i < nargs; i++)
232  {
233  if (! is.read (reinterpret_cast<char *> (&tmp), 4))
234  return false;
235  if (swap)
236  swap_bytes<4> (&tmp);
237 
238  OCTAVE_LOCAL_BUFFER (char, ctmp, tmp+1);
239  is.read (ctmp, tmp);
240  ifargs(i) = std::string (ctmp);
241 
242  if (! is)
243  return false;
244  }
245 
246  if (! is.read (reinterpret_cast<char *> (&tmp), 4))
247  return false;
248  if (swap)
249  swap_bytes<4> (&tmp);
250 
251  OCTAVE_LOCAL_BUFFER (char, ctmp1, tmp+1);
252  is.read (ctmp1, tmp);
253  nm = std::string (ctmp1);
254 
255  if (! is)
256  return false;
257 
258  if (! is.read (reinterpret_cast<char *> (&tmp), 4))
259  return false;
260  if (swap)
261  swap_bytes<4> (&tmp);
262 
263  OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1);
264  is.read (ctmp2, tmp);
265  iftext = std::string (ctmp2);
266 
267  if (! is)
268  return false;
269 
271  fcn = ftmp.fcn;
272  }
273  return true;
274 }
275 
276 #if defined (HAVE_HDF5)
277 bool
278 octave_fcn_inline::save_hdf5 (hid_t loc_id, const char *name,
279  bool /* save_as_floats */)
280 {
281  hid_t group_hid = -1;
282 #if HAVE_HDF5_18
283  group_hid = H5Gcreate (loc_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
284 #else
285  group_hid = H5Gcreate (loc_id, name, 0);
286 #endif
287  if (group_hid < 0 ) return false;
288 
289  size_t len = 0;
290  for (int i = 0; i < ifargs.length (); i++)
291  if (len < ifargs(i).length ())
292  len = ifargs(i).length ();
293 
294  hid_t space_hid = -1, data_hid = -1, type_hid = -1;;
295  bool retval = true;
296 
297  // FIXME: Is there a better way of saving string vectors,
298  // than a null padded matrix?
299 
300  OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
301 
302  // Octave uses column-major, while HDF5 uses row-major ordering
303  hdims[1] = ifargs.length ();
304  hdims[0] = len + 1;
305 
306  space_hid = H5Screate_simple (2, hdims, 0);
307  if (space_hid < 0)
308  {
309  H5Gclose (group_hid);
310  return false;
311  }
312 #if HAVE_HDF5_18
313  data_hid = H5Dcreate (group_hid, "args", H5T_NATIVE_CHAR, space_hid,
314  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
315 #else
316  data_hid = H5Dcreate (group_hid, "args", H5T_NATIVE_CHAR, space_hid,
317  H5P_DEFAULT);
318 #endif
319  if (data_hid < 0)
320  {
321  H5Sclose (space_hid);
322  H5Gclose (group_hid);
323  return false;
324  }
325 
326  OCTAVE_LOCAL_BUFFER (char, s, ifargs.length () * (len + 1));
327 
328  // Save the args as a null teminated list
329  for (int i = 0; i < ifargs.length (); i++)
330  {
331  const char * cptr = ifargs(i).c_str ();
332  for (size_t j = 0; j < ifargs(i).length (); j++)
333  s[i*(len+1)+j] = *cptr++;
334  s[ifargs(i).length ()] = '\0';
335  }
336 
337  retval = H5Dwrite (data_hid, H5T_NATIVE_CHAR, H5S_ALL, H5S_ALL,
338  H5P_DEFAULT, s) >= 0;
339 
340  H5Dclose (data_hid);
341  H5Sclose (space_hid);
342 
343  if (!retval)
344  {
345  H5Gclose (group_hid);
346  return false;
347  }
348 
349  // attach the type of the variable
350  type_hid = H5Tcopy (H5T_C_S1);
351  H5Tset_size (type_hid, nm.length () + 1);
352  if (type_hid < 0)
353  {
354  H5Gclose (group_hid);
355  return false;
356  }
357 
358  hdims[0] = 0;
359  space_hid = H5Screate_simple (0 , hdims, 0);
360  if (space_hid < 0)
361  {
362  H5Tclose (type_hid);
363  H5Gclose (group_hid);
364  return false;
365  }
366 #if HAVE_HDF5_18
367  data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
368  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
369 #else
370  data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid, H5P_DEFAULT);
371 #endif
372  if (data_hid < 0 || H5Dwrite (data_hid, type_hid, H5S_ALL, H5S_ALL,
373  H5P_DEFAULT, nm.c_str ()) < 0)
374  {
375  H5Sclose (space_hid);
376  H5Tclose (type_hid);
377  H5Gclose (group_hid);
378  return false;
379  }
380  H5Dclose (data_hid);
381 
382  // attach the type of the variable
383  H5Tset_size (type_hid, iftext.length () + 1);
384  if (type_hid < 0)
385  {
386  H5Gclose (group_hid);
387  return false;
388  }
389 
390 #if HAVE_HDF5_18
391  data_hid = H5Dcreate (group_hid, "iftext", type_hid, space_hid,
392  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
393 #else
394  data_hid = H5Dcreate (group_hid, "iftext", type_hid, space_hid,
395  H5P_DEFAULT);
396 #endif
397  if (data_hid < 0 || H5Dwrite (data_hid, type_hid, H5S_ALL, H5S_ALL,
398  H5P_DEFAULT, iftext.c_str ()) < 0)
399  {
400  H5Sclose (space_hid);
401  H5Tclose (type_hid);
402  H5Gclose (group_hid);
403  return false;
404  }
405 
406  H5Dclose (data_hid);
407  H5Sclose (space_hid);
408  H5Tclose (type_hid);
409  H5Gclose (group_hid);
410 
411  return retval;
412 }
413 
414 bool
415 octave_fcn_inline::load_hdf5 (hid_t loc_id, const char *name)
416 {
417  hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id;
418  hsize_t rank;
419  int slen;
420 
421 #if HAVE_HDF5_18
422  group_hid = H5Gopen (loc_id, name, H5P_DEFAULT);
423 #else
424  group_hid = H5Gopen (loc_id, name);
425 #endif
426  if (group_hid < 0 ) return false;
427 
428 #if HAVE_HDF5_18
429  data_hid = H5Dopen (group_hid, "args", H5P_DEFAULT);
430 #else
431  data_hid = H5Dopen (group_hid, "args");
432 #endif
433  space_hid = H5Dget_space (data_hid);
434  rank = H5Sget_simple_extent_ndims (space_hid);
435 
436  if (rank != 2)
437  {
438  H5Dclose (data_hid);
439  H5Sclose (space_hid);
440  H5Gclose (group_hid);
441  return false;
442  }
443 
444  OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
445  OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank);
446 
447  H5Sget_simple_extent_dims (space_hid, hdims, maxdims);
448 
449  ifargs.resize (hdims[1]);
450 
451  OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]);
452 
453  if (H5Dread (data_hid, H5T_NATIVE_UCHAR, H5S_ALL, H5S_ALL,
454  H5P_DEFAULT, s1) < 0)
455  {
456  H5Dclose (data_hid);
457  H5Sclose (space_hid);
458  H5Gclose (group_hid);
459  return false;
460  }
461 
462  H5Dclose (data_hid);
463  H5Sclose (space_hid);
464 
465  for (size_t i = 0; i < hdims[1]; i++)
466  ifargs(i) = std::string (s1 + i*hdims[0]);
467 
468 #if HAVE_HDF5_18
469  data_hid = H5Dopen (group_hid, "nm", H5P_DEFAULT);
470 #else
471  data_hid = H5Dopen (group_hid, "nm");
472 #endif
473 
474  if (data_hid < 0)
475  {
476  H5Gclose (group_hid);
477  return false;
478  }
479 
480  type_hid = H5Dget_type (data_hid);
481  type_class_hid = H5Tget_class (type_hid);
482 
483  if (type_class_hid != H5T_STRING)
484  {
485  H5Tclose (type_hid);
486  H5Dclose (data_hid);
487  H5Gclose (group_hid);
488  return false;
489  }
490 
491  space_hid = H5Dget_space (data_hid);
492  rank = H5Sget_simple_extent_ndims (space_hid);
493 
494  if (rank != 0)
495  {
496  H5Sclose (space_hid);
497  H5Tclose (type_hid);
498  H5Dclose (data_hid);
499  H5Gclose (group_hid);
500  return false;
501  }
502 
503  slen = H5Tget_size (type_hid);
504  if (slen < 0)
505  {
506  H5Sclose (space_hid);
507  H5Tclose (type_hid);
508  H5Dclose (data_hid);
509  H5Gclose (group_hid);
510  return false;
511  }
512 
513  OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen);
514 
515  // create datatype for (null-terminated) string to read into:
516  st_id = H5Tcopy (H5T_C_S1);
517  H5Tset_size (st_id, slen);
518 
519  if (H5Dread (data_hid, st_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, nm_tmp) < 0)
520  {
521  H5Sclose (space_hid);
522  H5Tclose (type_hid);
523  H5Gclose (group_hid);
524  return false;
525  }
526  H5Tclose (st_id);
527  H5Dclose (data_hid);
528  nm = nm_tmp;
529 
530 #if HAVE_HDF5_18
531  data_hid = H5Dopen (group_hid, "iftext", H5P_DEFAULT);
532 #else
533  data_hid = H5Dopen (group_hid, "iftext");
534 #endif
535 
536  if (data_hid < 0)
537  {
538  H5Gclose (group_hid);
539  return false;
540  }
541 
542  type_hid = H5Dget_type (data_hid);
543  type_class_hid = H5Tget_class (type_hid);
544 
545  if (type_class_hid != H5T_STRING)
546  {
547  H5Tclose (type_hid);
548  H5Dclose (data_hid);
549  H5Gclose (group_hid);
550  return false;
551  }
552 
553  space_hid = H5Dget_space (data_hid);
554  rank = H5Sget_simple_extent_ndims (space_hid);
555 
556  if (rank != 0)
557  {
558  H5Sclose (space_hid);
559  H5Tclose (type_hid);
560  H5Dclose (data_hid);
561  H5Gclose (group_hid);
562  return false;
563  }
564 
565  slen = H5Tget_size (type_hid);
566  if (slen < 0)
567  {
568  H5Sclose (space_hid);
569  H5Tclose (type_hid);
570  H5Dclose (data_hid);
571  H5Gclose (group_hid);
572  return false;
573  }
574 
575  OCTAVE_LOCAL_BUFFER (char, iftext_tmp, slen);
576 
577  // create datatype for (null-terminated) string to read into:
578  st_id = H5Tcopy (H5T_C_S1);
579  H5Tset_size (st_id, slen);
580 
581  if (H5Dread (data_hid, st_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, iftext_tmp) < 0)
582  {
583  H5Sclose (space_hid);
584  H5Tclose (type_hid);
585  H5Gclose (group_hid);
586  return false;
587  }
588  H5Tclose (st_id);
589  H5Dclose (data_hid);
590  iftext = iftext_tmp;
591 
593  fcn = ftmp.fcn;
594 
595  return true;
596 }
597 #endif
598 
599 void
600 octave_fcn_inline::print (std::ostream& os, bool pr_as_read_syntax) const
601 {
602  print_raw (os, pr_as_read_syntax);
603  newline (os);
604 }
605 
606 void
607 octave_fcn_inline::print_raw (std::ostream& os, bool pr_as_read_syntax) const
608 {
609  std::ostringstream buf;
610 
611  if (nm.empty ())
612  buf << "f(";
613  else
614  buf << nm << "(";
615 
616  for (int i = 0; i < ifargs.length (); i++)
617  {
618  if (i)
619  buf << ", ";
620 
621  buf << ifargs(i);
622  }
623 
624  buf << ") = " << iftext;
625 
626  octave_print_internal (os, buf.str (), pr_as_read_syntax,
628 }
629 
632 {
633  return octave_value (fcn_text (), type);
634 }
635 
636 DEFUNX ("inline", Finline, args, ,
637  "-*- texinfo -*-\n\
638 @deftypefn {Built-in Function} {} inline (@var{str})\n\
639 @deftypefnx {Built-in Function} {} inline (@var{str}, @var{arg1}, @dots{})\n\
640 @deftypefnx {Built-in Function} {} inline (@var{str}, @var{n})\n\
641 Create an inline function from the character string @var{str}.\n\
642 If called with a single argument, the arguments of the generated\n\
643 function are extracted from the function itself. The generated\n\
644 function arguments will then be in alphabetical order. It should\n\
645 be noted that i, and j are ignored as arguments due to the\n\
646 ambiguity between their use as a variable or their use as an inbuilt\n\
647 constant. All arguments followed by a parenthesis are considered\n\
648 to be functions. If no arguments are found, a function taking a single\n\
649 argument named @code{x} will be created.\n\
650 \n\
651 If the second and subsequent arguments are character strings,\n\
652 they are the names of the arguments of the function.\n\
653 \n\
654 If the second argument is an integer @var{n}, the arguments are\n\
655 @qcode{\"x\"}, @qcode{\"P1\"}, @dots{}, @qcode{\"P@var{N}\"}.\n\
656 @seealso{argnames, formula, vectorize}\n\
657 @end deftypefn")
658 {
659  octave_value retval;
660 
661  int nargin = args.length ();
662 
663  if (nargin > 0)
664  {
665  if (args(0).is_string ())
666  {
667  std::string fun = args(0).string_value ();
668  string_vector fargs;
669 
670  if (nargin == 1)
671  {
672  bool is_arg = false;
673  bool in_string = false;
674  std::string tmp_arg;
675  size_t i = 0;
676  size_t fun_length = fun.length ();
677 
678  while (i < fun_length)
679  {
680  bool terminate_arg = false;
681  char c = fun[i++];
682 
683  if (in_string)
684  {
685  if (c == '\'' || c == '\"')
686  in_string = false;
687  }
688  else if (c == '\'' || c == '\"')
689  {
690  in_string = true;
691  if (is_arg)
692  terminate_arg = true;
693  }
694  else if (! isalpha (c) && c != '_')
695  if (! is_arg)
696  continue;
697  else if (isdigit (c))
698  tmp_arg.append (1, c);
699  else
700  {
701  // Before we do anything remove trailing whitespaces.
702  while (i < fun_length && isspace (c))
703  c = fun[i++];
704 
705  // Do we have a variable or a function?
706  if (c != '(')
707  terminate_arg = true;
708  else
709  {
710  tmp_arg = std::string ();
711  is_arg = false;
712  }
713  }
714  else if (! is_arg)
715  {
716  if (c == 'e' || c == 'E')
717  {
718  // possible number in exponent form, not arg
719  if (isdigit (fun[i])
720  || fun[i] == '-' || fun[i] == '+')
721  continue;
722  }
723  is_arg = true;
724  tmp_arg.append (1, c);
725  }
726  else
727  {
728  tmp_arg.append (1, c);
729  }
730 
731  if (terminate_arg || (i == fun_length && is_arg))
732  {
733  bool have_arg = false;
734 
735  for (int j = 0; j < fargs.length (); j++)
736  if (tmp_arg == fargs (j))
737  {
738  have_arg = true;
739  break;
740  }
741 
742  if (! have_arg && tmp_arg != "i" && tmp_arg != "j" &&
743  tmp_arg != "NaN" && tmp_arg != "nan" &&
744  tmp_arg != "Inf" && tmp_arg != "inf" &&
745  tmp_arg != "NA" && tmp_arg != "pi" &&
746  tmp_arg != "e" && tmp_arg != "eps")
747  fargs.append (tmp_arg);
748 
749  tmp_arg = std::string ();
750  is_arg = false;
751  }
752  }
753 
754  // Sort the arguments into ascii order.
755  fargs.sort ();
756 
757  if (fargs.length () == 0)
758  fargs.append (std::string ("x"));
759 
760  }
761  else if (nargin == 2 && args(1).is_numeric_type ())
762  {
763  if (! args(1).is_scalar_type ())
764  {
765  error ("inline: N must be an integer");
766  return retval;
767  }
768 
769  int n = args(1).int_value ();
770 
771  if (! error_state)
772  {
773  if (n >= 0)
774  {
775  fargs.resize (n+1);
776 
777  fargs(0) = "x";
778 
779  for (int i = 1; i < n+1; i++)
780  {
781  std::ostringstream buf;
782  buf << "P" << i;
783  fargs(i) = buf.str ();
784  }
785  }
786  else
787  {
788  error ("inline: N must be a positive integer or zero");
789  return retval;
790  }
791  }
792  else
793  {
794  error ("inline: N must be an integer");
795  return retval;
796  }
797  }
798  else
799  {
800  fargs.resize (nargin - 1);
801 
802  for (int i = 1; i < nargin; i++)
803  {
804  if (args(i).is_string ())
805  {
806  std::string s = args(i).string_value ();
807  fargs(i-1) = s;
808  }
809  else
810  {
811  error ("inline: expecting string arguments");
812  return retval;
813  }
814  }
815  }
816 
817  retval = octave_value (new octave_fcn_inline (fun, fargs));
818  }
819  else
820  error ("inline: STR argument must be a string");
821  }
822  else
823  print_usage ();
824 
825  return retval;
826 }
827 
828 /*
829 %!shared fn
830 %! fn = inline ("x.^2 + 1");
831 %!assert (feval (fn, 6), 37)
832 %!assert (fn (6), 37)
833 %!assert (feval (inline ("sum (x(:))"), [1 2; 3 4]), 10)
834 %!assert (feval (inline ("sqrt (x^2 + y^2)", "x", "y"), 3, 4), 5)
835 %!assert (feval (inline ("exp (P1*x) + P2", 3), 3, 4, 5), exp(3*4) + 5)
836 
837 ## Test input validation
838 %!error inline ()
839 %!error <STR argument must be a string> inline (1)
840 %!error <N must be an integer> inline ("2", ones (2,2))
841 %!error <N must be a positive integer> inline ("2", -1)
842 %!error <expecting string arguments> inline ("2", "x", -1, "y")
843 */
844 
845 DEFUN (formula, args, ,
846  "-*- texinfo -*-\n\
847 @deftypefn {Built-in Function} {} formula (@var{fun})\n\
848 Return a character string representing the inline function @var{fun}.\n\
849 Note that @code{char (@var{fun})} is equivalent to\n\
850 @code{formula (@var{fun})}.\n\
851 @seealso{argnames, inline, vectorize}\n\
852 @end deftypefn")
853 {
854  octave_value retval;
855 
856  int nargin = args.length ();
857 
858  if (nargin == 1)
859  {
860  octave_fcn_inline* fn = args(0).fcn_inline_value (true);
861 
862  if (fn)
863  retval = octave_value (fn->fcn_text ());
864  else
865  error ("formula: FUN must be an inline function");
866  }
867  else
868  print_usage ();
869 
870  return retval;
871 }
872 
873 /*
874 %!assert (formula (fn), "x.^2 + 1")
875 %!assert (formula (fn), char (fn))
876 
877 ## Test input validation
878 %!error formula ()
879 %!error formula (1, 2)
880 %!error <FUN must be an inline function> formula (1)
881 */
882 
883 DEFUN (argnames, args, ,
884  "-*- texinfo -*-\n\
885 @deftypefn {Built-in Function} {} argnames (@var{fun})\n\
886 Return a cell array of character strings containing the names of\n\
887 the arguments of the inline function @var{fun}.\n\
888 @seealso{inline, formula, vectorize}\n\
889 @end deftypefn")
890 {
891  octave_value retval;
892 
893  int nargin = args.length ();
894 
895  if (nargin == 1)
896  {
897  octave_fcn_inline *fn = args(0).fcn_inline_value (true);
898 
899  if (fn)
900  {
901  string_vector t1 = fn->fcn_arg_names ();
902 
903  Cell t2 (dim_vector (t1.length (), 1));
904 
905  for (int i = 0; i < t1.length (); i++)
906  t2(i) = t1(i);
907 
908  retval = t2;
909  }
910  else
911  error ("argnames: FUN must be an inline function");
912  }
913  else
914  print_usage ();
915 
916  return retval;
917 }
918 
919 /*
920 %!assert (argnames (fn), {"x"})
921 %!assert (argnames (inline ("1e-3*y + 2e4*z")), {"y"; "z"})
922 %!assert (argnames (inline ("2", 2)), {"x"; "P1"; "P2"})
923 
924 ## Test input validation
925 %!error argnames ()
926 %!error argnames (1, 2)
927 %!error <FUN must be an inline function> argnames (1)
928 */
929 
930 DEFUN (vectorize, args, ,
931  "-*- texinfo -*-\n\
932 @deftypefn {Built-in Function} {} vectorize (@var{fun})\n\
933 Create a vectorized version of the inline function @var{fun}\n\
934 by replacing all occurrences of @code{*}, @code{/}, etc., with\n\
935 @code{.*}, @code{./}, etc.\n\
936 \n\
937 This may be useful, for example, when using inline functions with\n\
938 numerical integration or optimization where a vector-valued function\n\
939 is expected.\n\
940 \n\
941 @example\n\
942 @group\n\
943 fcn = vectorize (inline (\"x^2 - 1\"))\n\
944  @result{} fcn = f(x) = x.^2 - 1\n\
945 quadv (fcn, 0, 3)\n\
946  @result{} 6\n\
947 @end group\n\
948 @end example\n\
949 @seealso{inline, formula, argnames}\n\
950 @end deftypefn")
951 {
952  octave_value retval;
953 
954  int nargin = args.length ();
955 
956  if (nargin == 1)
957  {
958  std::string old_func;
959  octave_fcn_inline* old = 0;
960  bool func_is_string = true;
961 
962  if (args(0).is_string ())
963  old_func = args(0).string_value ();
964  else
965  {
966  old = args(0).fcn_inline_value (true);
967  func_is_string = false;
968 
969  if (old)
970  old_func = old->fcn_text ();
971  else
972  error ("vectorize: FUN must be a string or inline function");
973  }
974 
975  if (! error_state)
976  {
977  std::string new_func;
978  size_t i = 0;
979 
980  while (i < old_func.length ())
981  {
982  std::string t1 = old_func.substr (i, 1);
983 
984  if (t1 == "*" || t1 == "/" || t1 == "\\" || t1 == "^")
985  {
986  if (i && old_func.substr (i-1, 1) != ".")
987  new_func.append (".");
988 
989  // Special case for ** operator.
990  if (t1 == "*" && i < (old_func.length () - 1)
991  && old_func.substr (i+1, 1) == "*")
992  {
993  new_func.append ("*");
994  i++;
995  }
996  }
997  new_func.append (t1);
998  i++;
999  }
1000 
1001  if (func_is_string)
1002  retval = octave_value (new_func);
1003  else
1004  retval = octave_value (new octave_fcn_inline
1005  (new_func, old->fcn_arg_names ()));
1006  }
1007  }
1008  else
1009  print_usage ();
1010 
1011  return retval;
1012 }
1013 
1014 /*
1015 %!assert (char (vectorize (fn)), "x.^2 + 1")
1016 %!assert (char (vectorize (inline ("1e-3*y + 2e4*z"))), "1e-3.*y + 2e4.*z")
1017 %!assert (char (vectorize (inline ("2**x^5"))), "2.**x.^5")
1018 %!assert (vectorize ("x.^2 + 1"), "x.^2 + 1")
1019 %!assert (vectorize ("1e-3*y + 2e4*z"), "1e-3.*y + 2e4.*z")
1020 %!assert (vectorize ("2**x^5"), "2.**x.^5")
1021 
1022 ## Test input validation
1023 %!error vectorize ()
1024 %!error vectorize (1, 2)
1025 %!error <FUN must be a string or inline function> vectorize (1)
1026 */