1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 #  include "config.h"
28 #endif
29 
30 #include <istream>
31 #include <ostream>
32 #include <vector>
33 
34 #include "dNDArray.h"
35 #include "fNDArray.h"
36 
37 #include "data-conv.h"
38 #include "lo-ieee.h"
39 #include "lo-specfun.h"
40 #include "lo-mappers.h"
41 #include "mx-base.h"
42 #include "mach-info.h"
43 #include "oct-locbuf.h"
44 
45 #include "errwarn.h"
46 #include "mxarray.h"
47 #include "ovl.h"
48 #include "oct-hdf5.h"
49 #include "oct-stream.h"
50 #include "ops.h"
51 #include "ov-base.h"
52 #include "ov-base-mat.h"
53 #include "ov-base-mat.cc"
54 #include "ov-complex.h"
55 #include "ov-cx-mat.h"
56 #include "ov-flt-cx-mat.h"
57 #include "ov-re-mat.h"
58 #include "ov-scalar.h"
59 #include "pr-output.h"
60 
61 #include "byte-swap.h"
62 #include "ls-oct-text.h"
63 #include "ls-hdf5.h"
64 #include "ls-utils.h"
65 
66 
67 template class octave_base_matrix<ComplexNDArray>;
68 
69 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_complex_matrix,
70                                      "complex matrix", "double");
71 
72 static octave_base_value *
default_numeric_demotion_function(const octave_base_value & a)73 default_numeric_demotion_function (const octave_base_value& a)
74 {
75   const octave_complex_matrix& v = dynamic_cast<const octave_complex_matrix&> (a);
76 
77   return new octave_float_complex_matrix (v.float_complex_array_value ());
78 }
79 
80 octave_base_value::type_conv_info
numeric_demotion_function(void) const81 octave_complex_matrix::numeric_demotion_function (void) const
82 {
83   return octave_base_value::type_conv_info
84            (default_numeric_demotion_function,
85             octave_float_complex_matrix::static_type_id ());
86 }
87 
88 octave_base_value *
try_narrowing_conversion(void)89 octave_complex_matrix::try_narrowing_conversion (void)
90 {
91   octave_base_value *retval = nullptr;
92 
93   if (matrix.numel () == 1)
94     {
95       Complex c = matrix (0);
96 
97       if (c.imag () == 0.0)
98         retval = new octave_scalar (c.real ());
99       else
100         retval = new octave_complex (c);
101     }
102   else if (matrix.all_elements_are_real ())
103     retval = new octave_matrix (::real (matrix));
104 
105   return retval;
106 }
107 
108 double
double_value(bool force_conversion) const109 octave_complex_matrix::double_value (bool force_conversion) const
110 {
111   if (! force_conversion)
112     warn_implicit_conversion ("Octave:imag-to-real",
113                               "complex matrix", "real scalar");
114 
115   if (rows () == 0 || columns () == 0)
116     err_invalid_conversion ("complex matrix", "real scalar");
117 
118   warn_implicit_conversion ("Octave:array-to-scalar",
119                             "complex matrix", "real scalar");
120 
121   return std::real (matrix(0, 0));
122 }
123 
124 float
float_value(bool force_conversion) const125 octave_complex_matrix::float_value (bool force_conversion) const
126 {
127   if (! force_conversion)
128     warn_implicit_conversion ("Octave:imag-to-real",
129                               "complex matrix", "real scalar");
130 
131   if (rows () == 0 || columns () == 0)
132     err_invalid_conversion ("complex matrix", "real scalar");
133 
134   warn_implicit_conversion ("Octave:array-to-scalar",
135                             "complex matrix", "real scalar");
136 
137   return std::real (matrix(0, 0));
138 }
139 
140 NDArray
array_value(bool force_conversion) const141 octave_complex_matrix::array_value (bool force_conversion) const
142 {
143   NDArray retval;
144 
145   if (! force_conversion)
146     warn_implicit_conversion ("Octave:imag-to-real",
147                               "complex matrix", "real matrix");
148 
149   retval = ::real (matrix);
150 
151   return retval;
152 }
153 
154 Matrix
matrix_value(bool force_conversion) const155 octave_complex_matrix::matrix_value (bool force_conversion) const
156 {
157   Matrix retval;
158 
159   if (! force_conversion)
160     warn_implicit_conversion ("Octave:imag-to-real",
161                               "complex matrix", "real matrix");
162 
163   retval = ::real (ComplexMatrix (matrix));
164 
165   return retval;
166 }
167 
168 FloatMatrix
float_matrix_value(bool force_conversion) const169 octave_complex_matrix::float_matrix_value (bool force_conversion) const
170 {
171   FloatMatrix retval;
172 
173   if (! force_conversion)
174     warn_implicit_conversion ("Octave:imag-to-real",
175                               "complex matrix", "real matrix");
176 
177   retval = ::real (ComplexMatrix (matrix));
178 
179   return retval;
180 }
181 
182 Complex
complex_value(bool) const183 octave_complex_matrix::complex_value (bool) const
184 {
185   if (rows () == 0 || columns () == 0)
186     err_invalid_conversion ("complex matrix", "complex scalar");
187 
188   warn_implicit_conversion ("Octave:array-to-scalar",
189                             "complex matrix", "complex scalar");
190 
191   return matrix(0, 0);
192 }
193 
194 FloatComplex
float_complex_value(bool) const195 octave_complex_matrix::float_complex_value (bool) const
196 {
197   float tmp = lo_ieee_float_nan_value ();
198 
199   FloatComplex retval (tmp, tmp);
200 
201   if (rows () == 0 || columns () == 0)
202     err_invalid_conversion ("complex matrix", "complex scalar");
203 
204   warn_implicit_conversion ("Octave:array-to-scalar",
205                             "complex matrix", "complex scalar");
206 
207   retval = matrix(0, 0);
208 
209   return retval;
210 }
211 
212 ComplexMatrix
complex_matrix_value(bool) const213 octave_complex_matrix::complex_matrix_value (bool) const
214 {
215   return ComplexMatrix (matrix);
216 }
217 
218 FloatComplexMatrix
float_complex_matrix_value(bool) const219 octave_complex_matrix::float_complex_matrix_value (bool) const
220 {
221   return FloatComplexMatrix (ComplexMatrix (matrix));
222 }
223 
224 boolNDArray
bool_array_value(bool warn) const225 octave_complex_matrix::bool_array_value (bool warn) const
226 {
227   if (matrix.any_element_is_nan ())
228     octave::err_nan_to_logical_conversion ();
229   if (warn && (! matrix.all_elements_are_real ()
230                || real (matrix).any_element_not_one_or_zero ()))
231     warn_logical_conversion ();
232 
233   return mx_el_ne (matrix, Complex (0.0));
234 }
235 
236 charNDArray
char_array_value(bool frc_str_conv) const237 octave_complex_matrix::char_array_value (bool frc_str_conv) const
238 {
239   charNDArray retval;
240 
241   if (! frc_str_conv)
242     warn_implicit_conversion ("Octave:num-to-str",
243                               "complex matrix", "string");
244   else
245     {
246       retval = charNDArray (dims ());
247       octave_idx_type nel = numel ();
248 
249       for (octave_idx_type i = 0; i < nel; i++)
250         retval.elem (i) = static_cast<char> (std::real (matrix.elem (i)));
251     }
252 
253   return retval;
254 }
255 
256 FloatComplexNDArray
float_complex_array_value(bool) const257 octave_complex_matrix::float_complex_array_value (bool) const
258 {
259   return FloatComplexNDArray (matrix);
260 }
261 
262 SparseMatrix
sparse_matrix_value(bool force_conversion) const263 octave_complex_matrix::sparse_matrix_value (bool force_conversion) const
264 {
265   SparseMatrix retval;
266 
267   if (! force_conversion)
268     warn_implicit_conversion ("Octave:imag-to-real",
269                               "complex matrix", "real matrix");
270 
271   retval = SparseMatrix (::real (ComplexMatrix (matrix)));
272 
273   return retval;
274 }
275 
276 SparseComplexMatrix
sparse_complex_matrix_value(bool) const277 octave_complex_matrix::sparse_complex_matrix_value (bool) const
278 {
279   return SparseComplexMatrix (ComplexMatrix (matrix));
280 }
281 
282 octave_value
as_double(void) const283 octave_complex_matrix::as_double (void) const
284 {
285   return matrix;
286 }
287 
288 octave_value
as_single(void) const289 octave_complex_matrix::as_single (void) const
290 {
291   return FloatComplexNDArray (matrix);
292 }
293 
294 octave_value
diag(octave_idx_type k) const295 octave_complex_matrix::diag (octave_idx_type k) const
296 {
297   octave_value retval;
298   if (k == 0 && matrix.ndims () == 2
299       && (matrix.rows () == 1 || matrix.columns () == 1))
300     retval = ComplexDiagMatrix (DiagArray2<Complex> (matrix));
301   else
302     retval = octave_base_matrix<ComplexNDArray>::diag (k);
303 
304   return retval;
305 }
306 
307 octave_value
diag(octave_idx_type m,octave_idx_type n) const308 octave_complex_matrix::diag (octave_idx_type m, octave_idx_type n) const
309 {
310   if (matrix.ndims () != 2
311       || (matrix.rows () != 1 && matrix.columns () != 1))
312     error ("diag: expecting vector argument");
313 
314   ComplexMatrix mat (matrix);
315 
316   return mat.diag (m, n);
317 }
318 
319 bool
save_ascii(std::ostream & os)320 octave_complex_matrix::save_ascii (std::ostream& os)
321 {
322   dim_vector dv = dims ();
323   if (dv.ndims () > 2)
324     {
325       ComplexNDArray tmp = complex_array_value ();
326 
327       os << "# ndims: " << dv.ndims () << "\n";
328 
329       for (int i = 0; i < dv.ndims (); i++)
330         os << ' ' << dv(i);
331 
332       os << "\n" << tmp;
333     }
334   else
335     {
336       // Keep this case, rather than use generic code above for backward
337       // compatibility.  Makes load_ascii much more complex!!
338       os << "# rows: " << rows () << "\n"
339          << "# columns: " << columns () << "\n";
340 
341       os << complex_matrix_value ();
342     }
343 
344   return true;
345 }
346 
347 bool
load_ascii(std::istream & is)348 octave_complex_matrix::load_ascii (std::istream& is)
349 {
350   string_vector keywords(2);
351 
352   keywords[0] = "ndims";
353   keywords[1] = "rows";
354 
355   std::string kw;
356   octave_idx_type val = 0;
357 
358   if (! extract_keyword (is, keywords, kw, val, true))
359     error ("load: failed to extract number of rows and columns");
360 
361   if (kw == "ndims")
362     {
363       int mdims = static_cast<int> (val);
364 
365       if (mdims < 0)
366         error ("load: failed to extract number of dimensions");
367 
368       dim_vector dv;
369       dv.resize (mdims);
370 
371       for (int i = 0; i < mdims; i++)
372         is >> dv(i);
373 
374       if (! is)
375         error ("load: failed to read dimensions");
376 
377       ComplexNDArray tmp(dv);
378 
379       is >> tmp;
380 
381       if (! is)
382         error ("load: failed to load matrix constant");
383 
384       matrix = tmp;
385     }
386   else if (kw == "rows")
387     {
388       octave_idx_type nr = val;
389       octave_idx_type nc = 0;
390 
391       if (nr < 0 || ! extract_keyword (is, "columns", nc) || nc < 0)
392         error ("load: failed to extract number of rows and columns");
393 
394       if (nr > 0 && nc > 0)
395         {
396           ComplexMatrix tmp (nr, nc);
397           is >> tmp;
398           if (! is)
399             error ("load: failed to load matrix constant");
400 
401           matrix = tmp;
402         }
403       else if (nr == 0 || nc == 0)
404         matrix = ComplexMatrix (nr, nc);
405       else
406         panic_impossible ();
407     }
408   else
409     panic_impossible ();
410 
411   return true;
412 }
413 
414 bool
save_binary(std::ostream & os,bool save_as_floats)415 octave_complex_matrix::save_binary (std::ostream& os, bool save_as_floats)
416 {
417   dim_vector dv = dims ();
418   if (dv.ndims () < 1)
419     return false;
420 
421   // Use negative value for ndims to differentiate with old format!!
422   int32_t tmp = - dv.ndims ();
423   os.write (reinterpret_cast<char *> (&tmp), 4);
424   for (int i = 0; i < dv.ndims (); i++)
425     {
426       tmp = dv(i);
427       os.write (reinterpret_cast<char *> (&tmp), 4);
428     }
429 
430   ComplexNDArray m = complex_array_value ();
431   save_type st = LS_DOUBLE;
432   if (save_as_floats)
433     {
434       if (m.too_large_for_float ())
435         {
436           warning ("save: some values too large to save as floats --");
437           warning ("save: saving as doubles instead");
438         }
439       else
440         st = LS_FLOAT;
441     }
442   else if (dv.numel () > 4096) // FIXME: make this configurable.
443     {
444       double max_val, min_val;
445       if (m.all_integers (max_val, min_val))
446         st = get_save_type (max_val, min_val);
447     }
448 
449   const Complex *mtmp = m.data ();
450   write_doubles (os, reinterpret_cast<const double *> (mtmp), st,
451                  2 * dv.numel ());
452 
453   return true;
454 }
455 
456 bool
load_binary(std::istream & is,bool swap,octave::mach_info::float_format fmt)457 octave_complex_matrix::load_binary (std::istream& is, bool swap,
458                                     octave::mach_info::float_format fmt)
459 {
460   char tmp;
461   int32_t mdims;
462   if (! is.read (reinterpret_cast<char *> (&mdims), 4))
463     return false;
464   if (swap)
465     swap_bytes<4> (&mdims);
466   if (mdims < 0)
467     {
468       mdims = - mdims;
469       int32_t di;
470       dim_vector dv;
471       dv.resize (mdims);
472 
473       for (int i = 0; i < mdims; i++)
474         {
475           if (! is.read (reinterpret_cast<char *> (&di), 4))
476             return false;
477           if (swap)
478             swap_bytes<4> (&di);
479           dv(i) = di;
480         }
481 
482       // Convert an array with a single dimension to be a row vector.
483       // Octave should never write files like this, other software
484       // might.
485 
486       if (mdims == 1)
487         {
488           mdims = 2;
489           dv.resize (mdims);
490           dv(1) = dv(0);
491           dv(0) = 1;
492         }
493 
494       if (! is.read (reinterpret_cast<char *> (&tmp), 1))
495         return false;
496 
497       ComplexNDArray m(dv);
498       Complex *im = m.fortran_vec ();
499       read_doubles (is, reinterpret_cast<double *> (im),
500                     static_cast<save_type> (tmp), 2 * dv.numel (), swap, fmt);
501 
502       if (! is)
503         return false;
504 
505       matrix = m;
506     }
507   else
508     {
509       int32_t nr, nc;
510       nr = mdims;
511       if (! is.read (reinterpret_cast<char *> (&nc), 4))
512         return false;
513       if (swap)
514         swap_bytes<4> (&nc);
515       if (! is.read (reinterpret_cast<char *> (&tmp), 1))
516         return false;
517       ComplexMatrix m (nr, nc);
518       Complex *im = m.fortran_vec ();
519       octave_idx_type len = nr * nc;
520       read_doubles (is, reinterpret_cast<double *> (im),
521                     static_cast<save_type> (tmp), 2*len, swap, fmt);
522 
523       if (! is)
524         return false;
525 
526       matrix = m;
527     }
528   return true;
529 }
530 
531 bool
save_hdf5(octave_hdf5_id loc_id,const char * name,bool save_as_floats)532 octave_complex_matrix::save_hdf5 (octave_hdf5_id loc_id, const char *name,
533                                   bool save_as_floats)
534 {
535 #if defined (HAVE_HDF5)
536 
537   dim_vector dv = dims ();
538   int empty = save_hdf5_empty (loc_id, name, dv);
539   if (empty)
540     return (empty > 0);
541 
542   int rank = dv.ndims ();
543   hid_t space_hid, data_hid, type_hid;
544   space_hid = data_hid = type_hid = -1;
545   bool retval = true;
546   ComplexNDArray m = complex_array_value ();
547 
548   OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
549 
550   // Octave uses column-major, while HDF5 uses row-major ordering
551   for (int i = 0; i < rank; i++)
552     hdims[i] = dv(rank-i-1);
553 
554   space_hid = H5Screate_simple (rank, hdims, nullptr);
555   if (space_hid < 0) return false;
556 
557   hid_t save_type_hid = H5T_NATIVE_DOUBLE;
558 
559   if (save_as_floats)
560     {
561       if (m.too_large_for_float ())
562         {
563           warning ("save: some values too large to save as floats --");
564           warning ("save: saving as doubles instead");
565         }
566       else
567         save_type_hid = H5T_NATIVE_FLOAT;
568     }
569 #if defined (HAVE_HDF5_INT2FLOAT_CONVERSIONS)
570   // hdf5 currently doesn't support float/integer conversions
571   else
572     {
573       double max_val, min_val;
574 
575       if (m.all_integers (max_val, min_val))
576         save_type_hid
577           = save_type_to_hdf5 (get_save_type (max_val, min_val));
578     }
579 #endif
580 
581   type_hid = hdf5_make_complex_type (save_type_hid);
582   if (type_hid < 0)
583     {
584       H5Sclose (space_hid);
585       return false;
586     }
587 #if defined (HAVE_HDF5_18)
588   data_hid = H5Dcreate (loc_id, name, type_hid, space_hid,
589                         octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
590 #else
591   data_hid = H5Dcreate (loc_id, name, type_hid, space_hid, octave_H5P_DEFAULT);
592 #endif
593   if (data_hid < 0)
594     {
595       H5Sclose (space_hid);
596       H5Tclose (type_hid);
597       return false;
598     }
599 
600   hid_t complex_type_hid = hdf5_make_complex_type (H5T_NATIVE_DOUBLE);
601   if (complex_type_hid < 0) retval = false;
602 
603   if (retval)
604     {
605       Complex *mtmp = m.fortran_vec ();
606       if (H5Dwrite (data_hid, complex_type_hid, octave_H5S_ALL, octave_H5S_ALL,
607                     octave_H5P_DEFAULT, mtmp)
608           < 0)
609         {
610           H5Tclose (complex_type_hid);
611           retval = false;
612         }
613     }
614 
615   H5Tclose (complex_type_hid);
616   H5Dclose (data_hid);
617   H5Tclose (type_hid);
618   H5Sclose (space_hid);
619 
620   return retval;
621 
622 #else
623   octave_unused_parameter (loc_id);
624   octave_unused_parameter (name);
625   octave_unused_parameter (save_as_floats);
626 
627   warn_save ("hdf5");
628 
629   return false;
630 #endif
631 }
632 
633 bool
load_hdf5(octave_hdf5_id loc_id,const char * name)634 octave_complex_matrix::load_hdf5 (octave_hdf5_id loc_id, const char *name)
635 {
636   bool retval = false;
637 
638 #if defined (HAVE_HDF5)
639 
640   dim_vector dv;
641   int empty = load_hdf5_empty (loc_id, name, dv);
642   if (empty > 0)
643     matrix.resize (dv);
644   if (empty)
645     return (empty > 0);
646 
647 #if defined (HAVE_HDF5_18)
648   hid_t data_hid = H5Dopen (loc_id, name, octave_H5P_DEFAULT);
649 #else
650   hid_t data_hid = H5Dopen (loc_id, name);
651 #endif
652   hid_t type_hid = H5Dget_type (data_hid);
653 
654   hid_t complex_type = hdf5_make_complex_type (H5T_NATIVE_DOUBLE);
655 
656   if (! hdf5_types_compatible (type_hid, complex_type))
657     {
658       H5Tclose (complex_type);
659       H5Dclose (data_hid);
660       return false;
661     }
662 
663   hid_t space_id = H5Dget_space (data_hid);
664 
665   hsize_t rank = H5Sget_simple_extent_ndims (space_id);
666 
667   if (rank < 1)
668     {
669       H5Tclose (complex_type);
670       H5Sclose (space_id);
671       H5Dclose (data_hid);
672       return false;
673     }
674 
675   OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
676   OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank);
677 
678   H5Sget_simple_extent_dims (space_id, hdims, maxdims);
679 
680   // Octave uses column-major, while HDF5 uses row-major ordering
681   if (rank == 1)
682     {
683       dv.resize (2);
684       dv(0) = 1;
685       dv(1) = hdims[0];
686     }
687   else
688     {
689       dv.resize (rank);
690       for (hsize_t i = 0, j = rank - 1; i < rank; i++, j--)
691         dv(j) = hdims[i];
692     }
693 
694   ComplexNDArray m (dv);
695   Complex *reim = m.fortran_vec ();
696   if (H5Dread (data_hid, complex_type, octave_H5S_ALL, octave_H5S_ALL,
697                octave_H5P_DEFAULT, reim)
698       >= 0)
699     {
700       retval = true;
701       matrix = m;
702     }
703 
704   H5Tclose (complex_type);
705   H5Sclose (space_id);
706   H5Dclose (data_hid);
707 
708 #else
709   octave_unused_parameter (loc_id);
710   octave_unused_parameter (name);
711 
712   warn_load ("hdf5");
713 #endif
714 
715   return retval;
716 }
717 
718 void
print_raw(std::ostream & os,bool pr_as_read_syntax) const719 octave_complex_matrix::print_raw (std::ostream& os,
720                                   bool pr_as_read_syntax) const
721 {
722   octave_print_internal (os, matrix, pr_as_read_syntax,
723                          current_print_indent_level ());
724 }
725 
726 mxArray *
as_mxArray(void) const727 octave_complex_matrix::as_mxArray (void) const
728 {
729   mxArray *retval = new mxArray (mxDOUBLE_CLASS, dims (), mxCOMPLEX);
730 
731   double *pr = static_cast<double *> (retval->get_data ());
732   double *pi = static_cast<double *> (retval->get_imag_data ());
733 
734   mwSize nel = numel ();
735 
736   const Complex *p = matrix.data ();
737 
738   for (mwIndex i = 0; i < nel; i++)
739     {
740       pr[i] = std::real (p[i]);
741       pi[i] = std::imag (p[i]);
742     }
743 
744   return retval;
745 }
746 
747 octave_value
map(unary_mapper_t umap) const748 octave_complex_matrix::map (unary_mapper_t umap) const
749 {
750   switch (umap)
751     {
752     // Mappers handled specially.
753     case umap_real:
754       return ::real (matrix);
755     case umap_imag:
756       return ::imag (matrix);
757     case umap_conj:
758       return ::conj (matrix);
759 
760     // Special cases for Matlab compatibility.
761     case umap_xtolower:
762     case umap_xtoupper:
763       return matrix;
764 
765 #define ARRAY_METHOD_MAPPER(UMAP, FCN)        \
766     case umap_ ## UMAP:                       \
767       return octave_value (matrix.FCN ())
768 
769     ARRAY_METHOD_MAPPER (abs, abs);
770     ARRAY_METHOD_MAPPER (isnan, isnan);
771     ARRAY_METHOD_MAPPER (isinf, isinf);
772     ARRAY_METHOD_MAPPER (isfinite, isfinite);
773 
774 #define ARRAY_MAPPER(UMAP, TYPE, FCN)                 \
775     case umap_ ## UMAP:                               \
776       return octave_value (matrix.map<TYPE> (FCN))
777 
778     ARRAY_MAPPER (acos, Complex, octave::math::acos);
779     ARRAY_MAPPER (acosh, Complex, octave::math::acosh);
780     ARRAY_MAPPER (angle, double, std::arg);
781     ARRAY_MAPPER (arg, double, std::arg);
782     ARRAY_MAPPER (asin, Complex, octave::math::asin);
783     ARRAY_MAPPER (asinh, Complex, octave::math::asinh);
784     ARRAY_MAPPER (atan, Complex, octave::math::atan);
785     ARRAY_MAPPER (atanh, Complex, octave::math::atanh);
786     ARRAY_MAPPER (erf, Complex, octave::math::erf);
787     ARRAY_MAPPER (erfc, Complex, octave::math::erfc);
788     ARRAY_MAPPER (erfcx, Complex, octave::math::erfcx);
789     ARRAY_MAPPER (erfi, Complex, octave::math::erfi);
790     ARRAY_MAPPER (dawson, Complex, octave::math::dawson);
791     ARRAY_MAPPER (ceil, Complex, octave::math::ceil);
792     ARRAY_MAPPER (cos, Complex, std::cos);
793     ARRAY_MAPPER (cosh, Complex, std::cosh);
794     ARRAY_MAPPER (exp, Complex, std::exp);
795     ARRAY_MAPPER (expm1, Complex, octave::math::expm1);
796     ARRAY_MAPPER (fix, Complex, octave::math::fix);
797     ARRAY_MAPPER (floor, Complex, octave::math::floor);
798     ARRAY_MAPPER (log, Complex, std::log);
799     ARRAY_MAPPER (log2, Complex, octave::math::log2);
800     ARRAY_MAPPER (log10, Complex, std::log10);
801     ARRAY_MAPPER (log1p, Complex, octave::math::log1p);
802     ARRAY_MAPPER (round, Complex, octave::math::round);
803     ARRAY_MAPPER (roundb, Complex, octave::math::roundb);
804     ARRAY_MAPPER (signum, Complex, octave::math::signum);
805     ARRAY_MAPPER (sin, Complex, std::sin);
806     ARRAY_MAPPER (sinh, Complex, std::sinh);
807     ARRAY_MAPPER (sqrt, Complex, std::sqrt);
808     ARRAY_MAPPER (tan, Complex, std::tan);
809     ARRAY_MAPPER (tanh, Complex, std::tanh);
810     ARRAY_MAPPER (isna, bool, octave::math::isna);
811 
812     default:
813       return octave_base_value::map (umap);
814     }
815 }
816