changeset 25393:b860a7e526cf

refactor evaluation of tree_matrix object * pt-tm-const.h, pt-tm-const.cc (tm_info): New class to hold common info about tm_row_const and tm_const objects. (tm_row_const, tm_const): Derive from tm_info. Rewrite concat functions. * pt-eval.cc (tree_evaluator::visit_matrix): Move most code to tm_const::concat function. * pt-tm-const.cc: Move tests here from pt-mat.cc.
author John W. Eaton <jwe@octave.org>
date Wed, 16 May 2018 19:01:14 -0400
parents 55047ed5419b
children a656665b671d
files libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-mat.cc libinterp/parse-tree/pt-tm-const.cc libinterp/parse-tree/pt-tm-const.h
diffstat 4 files changed, 825 insertions(+), 922 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/parse-tree/pt-eval.cc	Wed May 16 12:17:25 2018 -0400
+++ b/libinterp/parse-tree/pt-eval.cc	Wed May 16 19:01:14 2018 -0400
@@ -1697,200 +1697,9 @@
   void
   tree_evaluator::visit_matrix (tree_matrix& expr)
   {
-    octave_value retval = Matrix ();
-
-    bool all_strings_p = false;
-    bool all_sq_strings_p = false;
-    bool all_dq_strings_p = false;
-    bool all_empty_p = false;
-    bool all_real_p = false;
-    bool any_sparse_p = false;
-    bool any_class_p = false;
-    bool frc_str_conv = false;
-
     tm_const tmp (expr, *this);
 
-    if (tmp && ! tmp.empty ())
-      {
-        dim_vector dv = tmp.dims ();
-        all_strings_p = tmp.all_strings_p ();
-        all_sq_strings_p = tmp.all_sq_strings_p ();
-        all_dq_strings_p = tmp.all_dq_strings_p ();
-        all_empty_p = tmp.all_empty_p ();
-        all_real_p = tmp.all_real_p ();
-        any_sparse_p = tmp.any_sparse_p ();
-        any_class_p = tmp.any_class_p ();
-        frc_str_conv = tmp.some_strings_p ();
-
-        // Try to speed up the common cases.
-
-        std::string result_type = tmp.class_name ();
-
-        if (any_class_p)
-          {
-            retval = do_class_concat (tmp);
-          }
-        else if (result_type == "double")
-          {
-            if (any_sparse_p)
-              {
-                if (all_real_p)
-                  retval = do_single_type_concat<SparseMatrix> (dv, tmp);
-                else
-                  retval = do_single_type_concat<SparseComplexMatrix> (dv, tmp);
-              }
-            else
-              {
-                if (all_real_p)
-                  retval = do_single_type_concat<NDArray> (dv, tmp);
-                else
-                  retval = do_single_type_concat<ComplexNDArray> (dv, tmp);
-              }
-          }
-        else if (result_type == "single")
-          {
-            if (all_real_p)
-              retval = do_single_type_concat<FloatNDArray> (dv, tmp);
-            else
-              retval = do_single_type_concat<FloatComplexNDArray> (dv, tmp);
-          }
-        else if (result_type == "char")
-          {
-            char type = (all_dq_strings_p ? '"' : '\'');
-
-            if (! all_strings_p)
-              warn_implicit_conversion ("Octave:num-to-str",
-                                        "numeric", result_type);
-            else
-              maybe_warn_string_concat (all_dq_strings_p, all_sq_strings_p);
-
-            charNDArray result (dv, m_string_fill_char);
-
-            single_type_concat<charNDArray> (result, tmp);
-
-            retval = octave_value (result, type);
-          }
-        else if (result_type == "logical")
-          {
-            if (any_sparse_p)
-              retval = do_single_type_concat<SparseBoolMatrix> (dv, tmp);
-            else
-              retval = do_single_type_concat<boolNDArray> (dv, tmp);
-          }
-        else if (result_type == "int8")
-          retval = do_single_type_concat<int8NDArray> (dv, tmp);
-        else if (result_type == "int16")
-          retval = do_single_type_concat<int16NDArray> (dv, tmp);
-        else if (result_type == "int32")
-          retval = do_single_type_concat<int32NDArray> (dv, tmp);
-        else if (result_type == "int64")
-          retval = do_single_type_concat<int64NDArray> (dv, tmp);
-        else if (result_type == "uint8")
-          retval = do_single_type_concat<uint8NDArray> (dv, tmp);
-        else if (result_type == "uint16")
-          retval = do_single_type_concat<uint16NDArray> (dv, tmp);
-        else if (result_type == "uint32")
-          retval = do_single_type_concat<uint32NDArray> (dv, tmp);
-        else if (result_type == "uint64")
-          retval = do_single_type_concat<uint64NDArray> (dv, tmp);
-        else if (result_type == "cell")
-          retval = do_single_type_concat<Cell> (dv, tmp);
-        else if (result_type == "struct")
-          retval = do_single_type_concat<octave_map> (dv, tmp);
-        else
-          {
-            // The line below might seem crazy, since we take a copy of
-            // the first argument, resize it to be empty and then resize
-            // it to be full.  This is done since it means that there is
-            // no recopying of data, as would happen if we used a single
-            // resize.  It should be noted that resize operation is also
-            // significantly slower than the do_cat_op function, so it
-            // makes sense to have an empty matrix and copy all data.
-            //
-            // We might also start with a empty octave_value using
-            //
-            //    ctmp = octave::type_info::lookup_type
-            //          (tmp.begin() -> begin() -> type_name());
-            //
-            // and then directly resize.  However, for some types there
-            // might be some additional setup needed, and so this should
-            // be avoided.
-
-            octave_value ctmp;
-
-            // Find the first non-empty object
-
-            if (any_sparse_p)
-              {
-                // Start with sparse matrix to avoid issues memory issues
-                // with things like [ones(1,4),sprandn(1e8,4,1e-4)]
-                if (all_real_p)
-                  ctmp = octave_sparse_matrix ().resize (dv);
-                else
-                  ctmp = octave_sparse_complex_matrix ().resize (dv);
-              }
-            else
-              {
-                for (tm_row_const& row : tmp)
-                  {
-                    octave_quit ();
-
-                    for (auto& elt : row)
-                      {
-                        octave_quit ();
-
-                        ctmp = elt;
-
-                        if (! ctmp.all_zero_dims ())
-                          goto found_non_empty;
-                      }
-                  }
-
-                ctmp = (*(tmp.begin () -> begin ()));
-
-              found_non_empty:
-
-                if (! all_empty_p)
-                  ctmp = ctmp.resize (dim_vector (0,0)).resize (dv);
-              }
-
-            // Now, extract the values from the individual elements and
-            // insert them in the result matrix.
-
-            type_info& ti = m_interpreter.get_type_info ();
-
-            int dv_len = dv.ndims ();
-            octave_idx_type ntmp = (dv_len > 1 ? dv_len : 2);
-            Array<octave_idx_type> ra_idx (dim_vector (ntmp, 1), 0);
-
-            for (tm_row_const& row : tmp)
-              {
-                octave_quit ();
-
-                for (auto& elt : row)
-                  {
-                    octave_quit ();
-
-                    if (elt.isempty ())
-                      continue;
-
-                    ctmp = do_cat_op (ti, ctmp, elt, ra_idx);
-
-                    ra_idx (1) += elt.columns ();
-                  }
-
-                ra_idx (0) += row.rows ();
-                ra_idx (1) = 0;
-              }
-
-            retval = ctmp;
-
-            if (frc_str_conv && ! retval.is_string ())
-              retval = retval.convert_to_str ();
-          }
-      }
-
-    push_result (retval);
+    push_result (tmp.concat (m_string_fill_char));
   }
 
   void
--- a/libinterp/parse-tree/pt-mat.cc	Wed May 16 12:17:25 2018 -0400
+++ b/libinterp/parse-tree/pt-mat.cc	Wed May 16 19:01:14 2018 -0400
@@ -130,230 +130,3 @@
     return new_matrix;
   }
 }
-
-
-/*
-## test concatenation with all zero matrices
-%!assert ([ "" 65*ones(1,10) ], "AAAAAAAAAA")
-%!assert ([ 65*ones(1,10) "" ], "AAAAAAAAAA")
-
-%!test
-%! c = {"foo"; "bar"; "bazoloa"};
-%! assert ([c; "a"; "bc"; "def"], {"foo"; "bar"; "bazoloa"; "a"; "bc"; "def"});
-
-%!assert (class ([int64(1), int64(1)]), "int64")
-%!assert (class ([int64(1), int32(1)]), "int64")
-%!assert (class ([int64(1), int16(1)]), "int64")
-%!assert (class ([int64(1), int8(1)]), "int64")
-%!assert (class ([int64(1), uint64(1)]), "int64")
-%!assert (class ([int64(1), uint32(1)]), "int64")
-%!assert (class ([int64(1), uint16(1)]), "int64")
-%!assert (class ([int64(1), uint8(1)]), "int64")
-%!assert (class ([int64(1), single(1)]), "int64")
-%!assert (class ([int64(1), double(1)]), "int64")
-%!assert (class ([int64(1), cell(1)]), "cell")
-%!assert (class ([int64(1), true]), "int64")
-%!assert (class ([int64(1), "a"]), "char")
-
-%!assert (class ([int32(1), int64(1)]), "int32")
-%!assert (class ([int32(1), int32(1)]), "int32")
-%!assert (class ([int32(1), int16(1)]), "int32")
-%!assert (class ([int32(1), int8(1)]), "int32")
-%!assert (class ([int32(1), uint64(1)]), "int32")
-%!assert (class ([int32(1), uint32(1)]), "int32")
-%!assert (class ([int32(1), uint16(1)]), "int32")
-%!assert (class ([int32(1), uint8(1)]), "int32")
-%!assert (class ([int32(1), single(1)]), "int32")
-%!assert (class ([int32(1), double(1)]), "int32")
-%!assert (class ([int32(1), cell(1)]), "cell")
-%!assert (class ([int32(1), true]), "int32")
-%!assert (class ([int32(1), "a"]), "char")
-
-%!assert (class ([int16(1), int64(1)]), "int16")
-%!assert (class ([int16(1), int32(1)]), "int16")
-%!assert (class ([int16(1), int16(1)]), "int16")
-%!assert (class ([int16(1), int8(1)]), "int16")
-%!assert (class ([int16(1), uint64(1)]), "int16")
-%!assert (class ([int16(1), uint32(1)]), "int16")
-%!assert (class ([int16(1), uint16(1)]), "int16")
-%!assert (class ([int16(1), uint8(1)]), "int16")
-%!assert (class ([int16(1), single(1)]), "int16")
-%!assert (class ([int16(1), double(1)]), "int16")
-%!assert (class ([int16(1), cell(1)]), "cell")
-%!assert (class ([int16(1), true]), "int16")
-%!assert (class ([int16(1), "a"]), "char")
-
-%!assert (class ([int8(1), int64(1)]), "int8")
-%!assert (class ([int8(1), int32(1)]), "int8")
-%!assert (class ([int8(1), int16(1)]), "int8")
-%!assert (class ([int8(1), int8(1)]), "int8")
-%!assert (class ([int8(1), uint64(1)]), "int8")
-%!assert (class ([int8(1), uint32(1)]), "int8")
-%!assert (class ([int8(1), uint16(1)]), "int8")
-%!assert (class ([int8(1), uint8(1)]), "int8")
-%!assert (class ([int8(1), single(1)]), "int8")
-%!assert (class ([int8(1), double(1)]), "int8")
-%!assert (class ([int8(1), cell(1)]), "cell")
-%!assert (class ([int8(1), true]), "int8")
-%!assert (class ([int8(1), "a"]), "char")
-
-%!assert (class ([uint64(1), int64(1)]), "uint64")
-%!assert (class ([uint64(1), int32(1)]), "uint64")
-%!assert (class ([uint64(1), int16(1)]), "uint64")
-%!assert (class ([uint64(1), int8(1)]), "uint64")
-%!assert (class ([uint64(1), uint64(1)]), "uint64")
-%!assert (class ([uint64(1), uint32(1)]), "uint64")
-%!assert (class ([uint64(1), uint16(1)]), "uint64")
-%!assert (class ([uint64(1), uint8(1)]), "uint64")
-%!assert (class ([uint64(1), single(1)]), "uint64")
-%!assert (class ([uint64(1), double(1)]), "uint64")
-%!assert (class ([uint64(1), cell(1)]), "cell")
-%!assert (class ([uint64(1), true]), "uint64")
-%!assert (class ([uint64(1), "a"]), "char")
-
-%!assert (class ([uint32(1), int64(1)]), "uint32")
-%!assert (class ([uint32(1), int32(1)]), "uint32")
-%!assert (class ([uint32(1), int16(1)]), "uint32")
-%!assert (class ([uint32(1), int8(1)]), "uint32")
-%!assert (class ([uint32(1), uint64(1)]), "uint32")
-%!assert (class ([uint32(1), uint32(1)]), "uint32")
-%!assert (class ([uint32(1), uint16(1)]), "uint32")
-%!assert (class ([uint32(1), uint8(1)]), "uint32")
-%!assert (class ([uint32(1), single(1)]), "uint32")
-%!assert (class ([uint32(1), double(1)]), "uint32")
-%!assert (class ([uint32(1), cell(1)]), "cell")
-%!assert (class ([uint32(1), true]), "uint32")
-%!assert (class ([uint32(1), "a"]), "char")
-
-%!assert (class ([uint16(1), int64(1)]), "uint16")
-%!assert (class ([uint16(1), int32(1)]), "uint16")
-%!assert (class ([uint16(1), int16(1)]), "uint16")
-%!assert (class ([uint16(1), int8(1)]), "uint16")
-%!assert (class ([uint16(1), uint64(1)]), "uint16")
-%!assert (class ([uint16(1), uint32(1)]), "uint16")
-%!assert (class ([uint16(1), uint16(1)]), "uint16")
-%!assert (class ([uint16(1), uint8(1)]), "uint16")
-%!assert (class ([uint16(1), single(1)]), "uint16")
-%!assert (class ([uint16(1), double(1)]), "uint16")
-%!assert (class ([uint16(1), cell(1)]), "cell")
-%!assert (class ([uint16(1), true]), "uint16")
-%!assert (class ([uint16(1), "a"]), "char")
-
-%!assert (class ([uint8(1), int64(1)]), "uint8")
-%!assert (class ([uint8(1), int32(1)]), "uint8")
-%!assert (class ([uint8(1), int16(1)]), "uint8")
-%!assert (class ([uint8(1), int8(1)]), "uint8")
-%!assert (class ([uint8(1), uint64(1)]), "uint8")
-%!assert (class ([uint8(1), uint32(1)]), "uint8")
-%!assert (class ([uint8(1), uint16(1)]), "uint8")
-%!assert (class ([uint8(1), uint8(1)]), "uint8")
-%!assert (class ([uint8(1), single(1)]), "uint8")
-%!assert (class ([uint8(1), double(1)]), "uint8")
-%!assert (class ([uint8(1), cell(1)]), "cell")
-%!assert (class ([uint8(1), true]), "uint8")
-%!assert (class ([uint8(1), "a"]), "char")
-
-%!assert (class ([single(1), int64(1)]), "int64")
-%!assert (class ([single(1), int32(1)]), "int32")
-%!assert (class ([single(1), int16(1)]), "int16")
-%!assert (class ([single(1), int8(1)]), "int8")
-%!assert (class ([single(1), uint64(1)]), "uint64")
-%!assert (class ([single(1), uint32(1)]), "uint32")
-%!assert (class ([single(1), uint16(1)]), "uint16")
-%!assert (class ([single(1), uint8(1)]), "uint8")
-%!assert (class ([single(1), single(1)]), "single")
-%!assert (class ([single(1), double(1)]), "single")
-%!assert (class ([single(1), cell(1)]), "cell")
-%!assert (class ([single(1), true]), "single")
-%!assert (class ([single(1), "a"]), "char")
-
-%!assert (class ([double(1), int64(1)]), "int64")
-%!assert (class ([double(1), int32(1)]), "int32")
-%!assert (class ([double(1), int16(1)]), "int16")
-%!assert (class ([double(1), int8(1)]), "int8")
-%!assert (class ([double(1), uint64(1)]), "uint64")
-%!assert (class ([double(1), uint32(1)]), "uint32")
-%!assert (class ([double(1), uint16(1)]), "uint16")
-%!assert (class ([double(1), uint8(1)]), "uint8")
-%!assert (class ([double(1), single(1)]), "single")
-%!assert (class ([double(1), double(1)]), "double")
-%!assert (class ([double(1), cell(1)]), "cell")
-%!assert (class ([double(1), true]), "double")
-%!assert (class ([double(1), "a"]), "char")
-
-%!assert (class ([cell(1), int64(1)]), "cell")
-%!assert (class ([cell(1), int32(1)]), "cell")
-%!assert (class ([cell(1), int16(1)]), "cell")
-%!assert (class ([cell(1), int8(1)]), "cell")
-%!assert (class ([cell(1), uint64(1)]), "cell")
-%!assert (class ([cell(1), uint32(1)]), "cell")
-%!assert (class ([cell(1), uint16(1)]), "cell")
-%!assert (class ([cell(1), uint8(1)]), "cell")
-%!assert (class ([cell(1), single(1)]), "cell")
-%!assert (class ([cell(1), double(1)]), "cell")
-%!assert (class ([cell(1), cell(1)]), "cell")
-%!assert (class ([cell(1), true]), "cell")
-%!assert (class ([cell(1), "a"]), "cell")
-
-%!assert (class ([true, int64(1)]), "int64")
-%!assert (class ([true, int32(1)]), "int32")
-%!assert (class ([true, int16(1)]), "int16")
-%!assert (class ([true, int8(1)]), "int8")
-%!assert (class ([true, uint64(1)]), "uint64")
-%!assert (class ([true, uint32(1)]), "uint32")
-%!assert (class ([true, uint16(1)]), "uint16")
-%!assert (class ([true, uint8(1)]), "uint8")
-%!assert (class ([true, single(1)]), "single")
-%!assert (class ([true, double(1)]), "double")
-%!assert (class ([true, cell(1)]), "cell")
-%!assert (class ([true, true]), "logical")
-%!assert (class ([true, "a"]), "char")
-
-%!assert (class (["a", int64(1)]), "char")
-%!assert (class (["a", int32(1)]), "char")
-%!assert (class (["a", int16(1)]), "char")
-%!assert (class (["a", int8(1)]), "char")
-%!assert (class (["a", int64(1)]), "char")
-%!assert (class (["a", int32(1)]), "char")
-%!assert (class (["a", int16(1)]), "char")
-%!assert (class (["a", int8(1)]), "char")
-%!assert (class (["a", single(1)]), "char")
-%!assert (class (["a", double(1)]), "char")
-%!assert (class (["a", cell(1)]), "cell")
-%!assert (class (["a", true]), "char")
-%!assert (class (["a", "a"]), "char")
-
-%!assert (class ([cell(1), struct("foo", "bar")]), "cell")
-%!error [struct("foo", "bar"), cell(1)]
-
-%!test <*39041> assert (class ([cell(0), struct()]), "cell")
-%!test <51086> assert (class ([struct(), cell(0)]), "struct")
-
-%!assert ([,1], 1)
-%!assert ([1,], 1)
-%!assert ([,1,], 1)
-%!assert ([,1,;;], 1)
-%!assert ([,1,;,;], 1)
-
-%!assert ([1,1], ones (1, 2))
-%!assert ([,1,1], ones (1, 2))
-%!assert ([1,1,], ones (1, 2))
-%!assert ([,1,1,], ones (1, 2))
-%!assert ([,1,1,;;], ones (1, 2))
-%!assert ([,1,1,;,;], ones (1, 2))
-%!assert ([,;,1,1], ones (1, 2))
-
-%!assert ([1;1], ones (2, 1))
-%!assert ([1,;1], ones (2, 1))
-%!assert ([1,;,;1], ones (2, 1))
-
-%!error eval ("[,,]")
-%!error eval ("[,,;,]")
-%!error eval ("[,;,,;,]")
-
-%!assert (isnull ([,]))
-%!assert (isnull ([;]))
-%!assert (isnull ([;;]))
-%!assert (isnull ([;,;]))
-%!assert (isnull ([,;,;,]))
-*/
--- a/libinterp/parse-tree/pt-tm-const.cc	Wed May 16 12:17:25 2018 -0400
+++ b/libinterp/parse-tree/pt-tm-const.cc	Wed May 16 19:01:14 2018 -0400
@@ -59,20 +59,61 @@
 
 namespace octave
 {
-  void
-  tm_row_const::tm_row_const_rep::do_init_element (const octave_value& val,
-                                                   bool& first_elem)
+  void tm_row_const::cellify (void)
   {
-    std::string this_elt_class_nm = val.isobject () ? "class"
-                                                    : val.class_name ();
+    bool elt_changed = false;
+
+    for (auto& elt : m_values)
+      {
+        octave_quit ();
+
+        if (! elt.iscell ())
+          {
+            elt_changed = true;
+
+            if (elt.isempty ())
+              elt = Cell ();
+            else
+              elt = Cell (elt);
+          }
+      }
+
+    if (! elt_changed)
+      return;
+
+    bool first_elem = true;
 
-    m_class_nm = get_concat_class (m_class_nm, this_elt_class_nm);
+    for (const auto& val : m_values)
+      {
+        octave_quit ();
+
+        dim_vector this_elt_dv = val.dims ();
+
+        if (! this_elt_dv.zero_by_zero ())
+          {
+            if (first_elem)
+              {
+                first_elem = false;
+                m_dv = this_elt_dv;
+              }
+            else if (! m_dv.hvcat (this_elt_dv, 1))
+              eval_error ("horizontal dimensions mismatch", m_dv, this_elt_dv);
+          }
+      }
+  }
+
+  void tm_row_const::init_element (const octave_value& val, bool& first_elem)
+  {
+    std::string this_elt_class_name
+      = val.isobject () ? "class" : val.class_name ();
+
+    m_class_name = get_concat_class (m_class_name, this_elt_class_name);
 
     dim_vector this_elt_dv = val.dims ();
 
     if (! this_elt_dv.zero_by_zero ())
       {
-        m_all_mt = false;
+        m_all_empty = false;
 
         if (first_elem)
           {
@@ -85,25 +126,25 @@
     else if (val.iscell ())
       first_elem = false;
 
-    append (val);
+    m_values.push_back (val);
 
-    if (m_all_str && ! val.is_string ())
-      m_all_str = false;
+    if (m_all_strings && ! val.is_string ())
+      m_all_strings = false;
 
-    if (m_all_sq_str && ! val.is_sq_string ())
-      m_all_sq_str = false;
+    if (m_all_sq_strings && ! val.is_sq_string ())
+      m_all_sq_strings = false;
 
-    if (m_all_dq_str && ! val.is_dq_string ())
-      m_all_dq_str = false;
+    if (m_all_dq_strings && ! val.is_dq_string ())
+      m_all_dq_strings = false;
 
-    if (! m_some_str && val.is_string ())
-      m_some_str = true;
+    if (! m_some_strings && val.is_string ())
+      m_some_strings = true;
 
     if (m_all_real && ! val.isreal ())
       m_all_real = false;
 
-    if (m_all_cmplx && ! (val.iscomplex () || val.isreal ()))
-      m_all_cmplx = false;
+    if (m_all_complex && ! (val.iscomplex () || val.isreal ()))
+      m_all_complex = false;
 
     if (! m_any_cell && val.iscell ())
       m_any_cell = true;
@@ -118,48 +159,32 @@
     m_all_1x1 = m_all_1x1 && ! val.issparse () && val.numel () == 1;
   }
 
-  void
-  tm_row_const::tm_row_const_rep::init (const tree_argument_list& row,
-                                        tree_evaluator& tw)
+  void tm_row_const::init (const tree_argument_list& row, tree_evaluator& tw)
   {
-    m_all_str = true;
-    m_all_sq_str = true;
-    m_all_dq_str = true;
-    m_all_real = true;
-    m_all_cmplx = true;
-    m_any_cell = false;
-    m_any_sparse = false;
-    m_any_class = false;
-
     bool first_elem = true;
 
-    for (tree_expression* elt : row)
+    for (auto *elt : row)
       {
         octave_quit ();
 
         octave_value tmp = tw.evaluate (elt);
 
         if (tmp.is_undefined ())
+          return;
+
+        if (tmp.is_cs_list ())
           {
-            m_ok = true;
-            return;
+            octave_value_list tlst = tmp.list_value ();
+
+            for (octave_idx_type i = 0; i < tlst.length (); i++)
+              {
+                octave_quit ();
+
+                init_element (tlst(i), first_elem);
+              }
           }
         else
-          {
-            if (tmp.is_cs_list ())
-              {
-                octave_value_list tlst = tmp.list_value ();
-
-                for (octave_idx_type i = 0; i < tlst.length (); i++)
-                  {
-                    octave_quit ();
-
-                    do_init_element (tlst(i), first_elem);
-                  }
-              }
-            else
-              do_init_element (tmp, first_elem);
-          }
+          init_element (tmp, first_elem);
       }
 
     if (m_any_cell && ! m_any_class && ! m_first_elem_is_struct)
@@ -167,7 +192,7 @@
 
     first_elem = true;
 
-    for (const octave_value& val : *this)
+    for (const auto& val : m_values)
       {
         octave_quit ();
 
@@ -175,7 +200,7 @@
 
         if (! this_elt_dv.zero_by_zero ())
           {
-            m_all_mt = false;
+            m_all_empty = false;
 
             if (first_elem)
               {
@@ -186,129 +211,152 @@
               eval_error ("horizontal dimensions mismatch", m_dv, this_elt_dv);
           }
       }
-
-    m_ok = true;
   }
 
-  void
-  tm_row_const::tm_row_const_rep::cellify (void)
+  octave_value tm_const::concat (char string_fill_char) const
   {
-    bool elt_changed = false;
+    if (m_tm_rows.empty ())
+      return Matrix ();
 
-    for (auto& elt : *this)
-      {
-        octave_quit ();
+    // Try to speed up the common cases.
+
+    std::string result_type = m_class_name;
 
-        if (! elt.iscell ())
+    if (m_any_class)
+      return class_concat ();
+    else if (result_type == "double")
+      {
+        if (m_any_sparse)
           {
-            elt_changed = true;
-
-            if (elt.isempty ())
-              elt = Cell ();
+            if (m_all_real)
+              return sparse_array_concat<SparseMatrix> ();
             else
-              elt = Cell (elt);
+              return sparse_array_concat<SparseComplexMatrix> ();
+          }
+        else
+          {
+            if (m_all_real)
+              return array_concat<NDArray> ();
+            else
+              return array_concat<ComplexNDArray> ();
           }
       }
-
-    if (elt_changed)
+    else if (result_type == "single")
       {
-        bool first_elem = true;
-
-        for (const octave_value& val : *this)
-          {
-            octave_quit ();
-
-            dim_vector this_elt_dv = val.dims ();
+        if (m_all_real)
+          return array_concat<FloatNDArray> ();
+        else
+          return array_concat<FloatComplexNDArray> ();
+      }
+    else if (result_type == "char")
+      {
+        if (! m_all_strings)
+          warn_implicit_conversion ("Octave:num-to-str",
+                                    "numeric", result_type);
+        else
+          maybe_warn_string_concat (m_all_dq_strings, m_all_sq_strings);
 
-            if (! this_elt_dv.zero_by_zero ())
-              {
-                if (first_elem)
-                  {
-                    first_elem = false;
-                    m_dv = this_elt_dv;
-                  }
-                else if (! m_dv.hvcat (this_elt_dv, 1))
-                  eval_error ("horizontal dimensions mismatch", m_dv, this_elt_dv);
-              }
-          }
+        return char_array_concat (string_fill_char);
+      }
+    else if (result_type == "logical")
+      {
+        if (m_any_sparse)
+          return sparse_array_concat<SparseBoolMatrix> ();
+        else
+          return array_concat<boolNDArray> ();
       }
+    else if (result_type == "int8")
+      return array_concat<int8NDArray> ();
+    else if (result_type == "int16")
+      return array_concat<int16NDArray> ();
+    else if (result_type == "int32")
+      return array_concat<int32NDArray> ();
+    else if (result_type == "int64")
+      return array_concat<int64NDArray> ();
+    else if (result_type == "uint8")
+      return array_concat<uint8NDArray> ();
+    else if (result_type == "uint16")
+      return array_concat<uint16NDArray> ();
+    else if (result_type == "uint32")
+      return array_concat<uint32NDArray> ();
+    else if (result_type == "uint64")
+      return array_concat<uint64NDArray> ();
+    else if (result_type == "cell")
+      return array_concat<Cell> ();
+    else if (result_type == "struct")
+      {
+        if (m_all_1x1)
+          return map_concat<octave_scalar_map> ();
+        else
+          return map_concat<octave_map> ();
+      }
+    else
+      return generic_concat ();
   }
 
-  void
-  tm_const::init (const tree_matrix& tm, tree_evaluator& tw)
+  void tm_const::init (const tree_matrix& tm)
   {
-    m_all_str = true;
-    m_all_sq_str = true;
-    m_all_dq_str = true;
-    m_all_real = true;
-    m_all_cmplx = true;
-    m_any_cell = false;
-    m_any_sparse = false;
-    m_any_class = false;
-    m_all_1x1 = ! tm.empty ();
-
     bool first_elem = true;
     bool first_elem_is_struct = false;
 
     // Just eval and figure out if what we have is complex or all strings.
     // We can't check columns until we know that this is a numeric matrix --
     // collections of strings can have elements of different lengths.
-    for (const tree_argument_list* elt : tm)
+
+    for (const auto *elt : tm)
       {
         octave_quit ();
 
-        tm_row_const tmp (*elt, tw);
+        tm_row_const row (*elt, m_evaluator);
 
         if (first_elem)
           {
-            first_elem_is_struct = tmp.first_elem_struct_p ();
+            first_elem_is_struct = row.first_elem_struct_p ();
 
             first_elem = false;
           }
 
-        if (tmp && ! tmp.empty ())
-          {
-            if (m_all_str && ! tmp.all_strings_p ())
-              m_all_str = false;
+        if (row.empty ())
+          break;
 
-            if (m_all_sq_str && ! tmp.all_sq_strings_p ())
-              m_all_sq_str = false;
+        if (m_all_strings && ! row.all_strings_p ())
+          m_all_strings = false;
+
+        if (m_all_sq_strings && ! row.all_sq_strings_p ())
+          m_all_sq_strings = false;
 
-            if (m_all_dq_str && ! tmp.all_dq_strings_p ())
-              m_all_dq_str = false;
+        if (m_all_dq_strings && ! row.all_dq_strings_p ())
+          m_all_dq_strings = false;
 
-            if (! m_some_str && tmp.some_strings_p ())
-              m_some_str = true;
+        if (! m_some_strings && row.some_strings_p ())
+          m_some_strings = true;
 
-            if (m_all_real && ! tmp.all_real_p ())
-              m_all_real = false;
+        if (m_all_real && ! row.all_real_p ())
+          m_all_real = false;
 
-            if (m_all_cmplx && ! tmp.all_complex_p ())
-              m_all_cmplx = false;
+        if (m_all_complex && ! row.all_complex_p ())
+          m_all_complex = false;
 
-            if (m_all_mt && ! tmp.all_empty_p ())
-              m_all_mt = false;
+        if (m_all_empty && ! row.all_empty_p ())
+          m_all_empty = false;
 
-            if (! m_any_cell && tmp.any_cell_p ())
-              m_any_cell = true;
+        if (! m_any_cell && row.any_cell_p ())
+          m_any_cell = true;
 
-            if (! m_any_sparse && tmp.any_sparse_p ())
-              m_any_sparse = true;
-
-            if (! m_any_class && tmp.any_class_p ())
-              m_any_class = true;
+        if (! m_any_sparse && row.any_sparse_p ())
+          m_any_sparse = true;
 
-            m_all_1x1 = m_all_1x1 && tmp.all_1x1_p ();
+        if (! m_any_class && row.any_class_p ())
+          m_any_class = true;
 
-            append (tmp);
-          }
-        else
-          break;
+        m_all_1x1 = m_all_1x1 && row.all_1x1_p ();
+
+        m_tm_rows.push_back (row);
       }
 
     if (m_any_cell && ! m_any_class && ! first_elem_is_struct)
       {
-        for (auto& elt : *this)
+        for (auto& elt : m_tm_rows)
           {
             octave_quit ();
 
@@ -318,19 +366,19 @@
 
     first_elem = true;
 
-    for (tm_row_const& elt : *this)
+    for (const auto& elt : m_tm_rows)
       {
         octave_quit ();
 
         octave_idx_type this_elt_nr = elt.rows ();
         octave_idx_type this_elt_nc = elt.cols ();
 
-        std::string this_elt_class_nm = elt.class_name ();
-        m_class_nm = get_concat_class (m_class_nm, this_elt_class_nm);
+        std::string this_elt_class_name = elt.class_name ();
+        m_class_name = get_concat_class (m_class_name, this_elt_class_name);
 
         dim_vector this_elt_dv = elt.dims ();
 
-        m_all_mt = false;
+        m_all_empty = false;
 
         if (first_elem)
           {
@@ -338,7 +386,7 @@
 
             m_dv = this_elt_dv;
           }
-        else if (m_all_str && m_dv.ndims () == 2
+        else if (m_all_strings && m_dv.ndims () == 2
                  && this_elt_dv.ndims () == 2)
           {
             // This is Octave's specialty.
@@ -359,33 +407,27 @@
         else if ((! m_any_class) && (! m_dv.hvcat (this_elt_dv, 0)))
           eval_error ("vertical dimensions mismatch", m_dv, this_elt_dv);
       }
-
-    m_ok = true;
   }
 
-  template <>
-  octave_value
-  do_single_type_concat<octave_map> (const dim_vector& dv,
-                                     tm_const& tmp)
+  octave_value tm_const::char_array_concat (char string_fill_char) const
   {
-    octave_map result;
+    char type = (m_all_dq_strings ? '"' : '\'');
 
-    if (tmp.all_1x1_p ())
-      single_type_concat<octave_scalar_map> (result, dv, tmp);
-    else
-      single_type_concat<octave_map> (result, dv, tmp);
+    charNDArray result (m_dv, string_fill_char);
 
-    return result;
+    array_concat_internal<charNDArray> (result);
+
+    return octave_value (result, type);
   }
 
-  octave_value do_class_concat (tm_const& tmc)
+  octave_value tm_const::class_concat (void) const
   {
     octave_value retval;
 
-    octave_value_list rows (tmc.length (), octave_value ());
+    octave_value_list rows (m_tm_rows.size (), octave_value ());
 
     octave_idx_type j = 0;
-    for (tm_row_const& tmrc : tmc)
+    for (const auto& tmrc : m_tm_rows)
       {
         octave_quit ();
 
@@ -396,18 +438,493 @@
             octave_value_list row (tmrc.length (), octave_value ());
 
             octave_idx_type i = 0;
-            for (auto& elt : tmrc)
+            for (const auto& elt : tmrc)
               row(i++) = elt;
 
-            rows(j++) = do_class_concat (row, "horzcat", 1);
+            rows(j++) = ::do_class_concat (row, "horzcat", 1);
           }
       }
 
     if (rows.length () == 1)
       retval = rows(0);
     else
-      retval = do_class_concat (rows, "vertcat", 0);
+      retval = ::do_class_concat (rows, "vertcat", 0);
+
+    return retval;
+  }
+
+  octave_value tm_const::generic_concat (void) const
+  {
+    // The line below might seem crazy, since we take a copy of the
+    // first argument, resize it to be empty and then resize it to be
+    // full.  This is done since it means that there is no recopying of
+    // data, as would happen if we used a single resize.  It should be
+    // noted that resize operation is also significantly slower than the
+    // do_cat_op function, so it makes sense to have an empty matrix and
+    // copy all data.
+    //
+    // We might also start with a empty octave_value using
+    //
+    //    ctmp = octave::type_info::lookup_type
+    //          (tmp.begin() -> begin() -> type_name());
+    //
+    // and then directly resize.  However, for some types there might be
+    // some additional setup needed, and so this should be avoided.
+
+    octave_value ctmp;
+
+    // Find the first non-empty object
+
+    if (m_any_sparse)
+      {
+        // Start with sparse matrix to avoid issues memory issues with
+        // things like [ones(1,4),sprandn(1e8,4,1e-4)]
+
+        if (m_all_real)
+          ctmp = octave_sparse_matrix ().resize (m_dv);
+        else
+          ctmp = octave_sparse_complex_matrix ().resize (m_dv);
+      }
+    else
+      {
+        for (const auto& row : m_tm_rows)
+          {
+            octave_quit ();
+
+            for (const auto& elt : row)
+              {
+                octave_quit ();
+
+                ctmp = elt;
+
+                if (! ctmp.all_zero_dims ())
+                  goto found_non_empty;
+              }
+          }
+
+        ctmp = (*(m_tm_rows.begin () -> begin ()));
+
+      found_non_empty:
+
+        if (! m_all_empty)
+          ctmp = ctmp.resize (dim_vector (0,0)).resize (m_dv);
+      }
+
+    // Now, extract the values from the individual elements and insert
+    // them in the result matrix.
+
+    interpreter& interp = m_evaluator.get_interpreter ();
+
+    type_info& ti = interp.get_type_info ();
+
+    int dv_len = m_dv.ndims ();
+    octave_idx_type ntmp = (dv_len > 1 ? dv_len : 2);
+    Array<octave_idx_type> ra_idx (dim_vector (ntmp, 1), 0);
+
+    for (const auto& row : m_tm_rows)
+      {
+        octave_quit ();
+
+        for (const auto& elt : row)
+          {
+            octave_quit ();
+
+            if (elt.isempty ())
+              continue;
+
+            ctmp = do_cat_op (ti, ctmp, elt, ra_idx);
+
+            ra_idx (1) += elt.columns ();
+          }
+
+        ra_idx (0) += row.rows ();
+        ra_idx (1) = 0;
+      }
+
+    octave_value retval = ctmp;
+
+    // If some elements are strings, force the result to be a string.
+
+    if (m_some_strings && ! retval.is_string ())
+      retval = retval.convert_to_str ();
 
     return retval;
   }
+
+  // The result is passed as a parameter to this function so that the
+  // char_array_concat function can create the array externally.
+  // Otherwise, we would need a specialization of this function for
+  // character arrays just to handle string_fill_char.
+
+  template <typename TYPE>
+  void tm_const::array_concat_internal (TYPE& result) const
+  {
+    octave_idx_type r = 0;
+    octave_idx_type c = 0;
+
+    for (const auto& row : m_tm_rows)
+      {
+        // Skip empty arrays to allow looser rules.
+        if (row.dims ().any_zero ())
+          continue;
+
+        for (const auto& elt : row)
+          {
+            octave_quit ();
+
+            TYPE ra = octave_value_extract<TYPE> (elt);
+
+            // Skip empty arrays to allow looser rules.
+
+            if (! ra.isempty ())
+              {
+                result.insert (ra, r, c);
+
+                c += ra.columns ();
+              }
+          }
+
+        r += row.rows ();
+        c = 0;
+      }
+  }
+
+  template <typename TYPE>
+  TYPE tm_const::array_concat (void) const
+  {
+    typedef typename TYPE::element_type ELT_T;
+
+    if (m_dv.any_zero ())
+      return TYPE (m_dv);
+
+    if (m_tm_rows.size () == 1)
+      {
+        // If possible, forward the operation to liboctave.
+        // Single row.
+        const tm_row_const& row = m_tm_rows.front ();
+        if (! (equal_types<ELT_T, char>::value
+               || equal_types<ELT_T, octave_value>::value)
+            && row.all_1x1_p ())
+          {
+            // Optimize all scalars case.
+            TYPE result (m_dv);
+            assert (static_cast<size_t> (result.numel ()) == row.length ());
+            octave_idx_type i = 0;
+            for (const auto& elt : row)
+              result(i++) = octave_value_extract<ELT_T> (elt);
+
+            return result;
+          }
+
+        octave_idx_type ncols = row.length ();
+        octave_idx_type i = 0;
+        OCTAVE_LOCAL_BUFFER (TYPE, array_list, ncols);
+
+        for (const auto& elt : row)
+          {
+            octave_quit ();
+
+            array_list[i++] = octave_value_extract<TYPE> (elt);
+          }
+
+        return TYPE::cat (-2, ncols, array_list);
+      }
+    else
+      {
+        TYPE result (m_dv);
+        array_concat_internal<TYPE> (result);
+        return result;
+      }
+  }
+
+  template <typename TYPE>
+  TYPE tm_const::sparse_array_concat (void) const
+  {
+    if (m_dv.any_zero ())
+      return TYPE (m_dv);
+
+    // Sparse matrices require preallocation for efficient indexing; besides,
+    // only horizontal concatenation can be efficiently handled by indexing.
+    // So we just cat all rows through liboctave, then cat the final column.
+    octave_idx_type nrows = m_tm_rows.size ();
+    octave_idx_type j = 0;
+    OCTAVE_LOCAL_BUFFER (TYPE, sparse_row_list, nrows);
+    for (const auto& row : m_tm_rows)
+      {
+        octave_idx_type ncols = row.length ();
+        octave_idx_type i = 0;
+        OCTAVE_LOCAL_BUFFER (TYPE, sparse_list, ncols);
+
+        for (auto& elt : row)
+          {
+            octave_quit ();
+
+            sparse_list[i] = octave_value_extract<TYPE> (elt);
+            i++;
+          }
+
+        TYPE stmp = TYPE::cat (-2, ncols, sparse_list);
+        sparse_row_list[j] = stmp;
+        j++;
+      }
+
+    return TYPE::cat (-1, nrows, sparse_row_list);
+  }
+
+  template <typename MAP>
+  octave_map tm_const::map_concat (void) const
+  {
+    if (m_dv.any_zero ())
+      return octave_map (m_dv);
+
+    octave_idx_type nrows = m_tm_rows.size ();
+    octave_idx_type j = 0;
+    OCTAVE_LOCAL_BUFFER (octave_map, map_row_list, nrows);
+    for (const auto& row : m_tm_rows)
+      {
+        octave_idx_type ncols = row.length ();
+        octave_idx_type i = 0;
+        OCTAVE_LOCAL_BUFFER (MAP, map_list, ncols);
+
+        for (auto& elt : row)
+          {
+            octave_quit ();
+
+            map_list[i] = octave_value_extract<MAP> (elt);
+            i++;
+          }
+
+        octave_map mtmp = octave_map::cat (-2, ncols, map_list);
+        map_row_list[j] = mtmp;
+        j++;
+      }
+
+    return octave_map::cat (-1, nrows, map_row_list);
+  }
 }
+
+/*
+## test concatenation with all zero matrices
+%!assert ([ "" 65*ones(1,10) ], "AAAAAAAAAA")
+%!assert ([ 65*ones(1,10) "" ], "AAAAAAAAAA")
+
+%!test
+%! c = {"foo"; "bar"; "bazoloa"};
+%! assert ([c; "a"; "bc"; "def"], {"foo"; "bar"; "bazoloa"; "a"; "bc"; "def"});
+
+%!assert (class ([int64(1), int64(1)]), "int64")
+%!assert (class ([int64(1), int32(1)]), "int64")
+%!assert (class ([int64(1), int16(1)]), "int64")
+%!assert (class ([int64(1), int8(1)]), "int64")
+%!assert (class ([int64(1), uint64(1)]), "int64")
+%!assert (class ([int64(1), uint32(1)]), "int64")
+%!assert (class ([int64(1), uint16(1)]), "int64")
+%!assert (class ([int64(1), uint8(1)]), "int64")
+%!assert (class ([int64(1), single(1)]), "int64")
+%!assert (class ([int64(1), double(1)]), "int64")
+%!assert (class ([int64(1), cell(1)]), "cell")
+%!assert (class ([int64(1), true]), "int64")
+%!assert (class ([int64(1), "a"]), "char")
+
+%!assert (class ([int32(1), int64(1)]), "int32")
+%!assert (class ([int32(1), int32(1)]), "int32")
+%!assert (class ([int32(1), int16(1)]), "int32")
+%!assert (class ([int32(1), int8(1)]), "int32")
+%!assert (class ([int32(1), uint64(1)]), "int32")
+%!assert (class ([int32(1), uint32(1)]), "int32")
+%!assert (class ([int32(1), uint16(1)]), "int32")
+%!assert (class ([int32(1), uint8(1)]), "int32")
+%!assert (class ([int32(1), single(1)]), "int32")
+%!assert (class ([int32(1), double(1)]), "int32")
+%!assert (class ([int32(1), cell(1)]), "cell")
+%!assert (class ([int32(1), true]), "int32")
+%!assert (class ([int32(1), "a"]), "char")
+
+%!assert (class ([int16(1), int64(1)]), "int16")
+%!assert (class ([int16(1), int32(1)]), "int16")
+%!assert (class ([int16(1), int16(1)]), "int16")
+%!assert (class ([int16(1), int8(1)]), "int16")
+%!assert (class ([int16(1), uint64(1)]), "int16")
+%!assert (class ([int16(1), uint32(1)]), "int16")
+%!assert (class ([int16(1), uint16(1)]), "int16")
+%!assert (class ([int16(1), uint8(1)]), "int16")
+%!assert (class ([int16(1), single(1)]), "int16")
+%!assert (class ([int16(1), double(1)]), "int16")
+%!assert (class ([int16(1), cell(1)]), "cell")
+%!assert (class ([int16(1), true]), "int16")
+%!assert (class ([int16(1), "a"]), "char")
+
+%!assert (class ([int8(1), int64(1)]), "int8")
+%!assert (class ([int8(1), int32(1)]), "int8")
+%!assert (class ([int8(1), int16(1)]), "int8")
+%!assert (class ([int8(1), int8(1)]), "int8")
+%!assert (class ([int8(1), uint64(1)]), "int8")
+%!assert (class ([int8(1), uint32(1)]), "int8")
+%!assert (class ([int8(1), uint16(1)]), "int8")
+%!assert (class ([int8(1), uint8(1)]), "int8")
+%!assert (class ([int8(1), single(1)]), "int8")
+%!assert (class ([int8(1), double(1)]), "int8")
+%!assert (class ([int8(1), cell(1)]), "cell")
+%!assert (class ([int8(1), true]), "int8")
+%!assert (class ([int8(1), "a"]), "char")
+
+%!assert (class ([uint64(1), int64(1)]), "uint64")
+%!assert (class ([uint64(1), int32(1)]), "uint64")
+%!assert (class ([uint64(1), int16(1)]), "uint64")
+%!assert (class ([uint64(1), int8(1)]), "uint64")
+%!assert (class ([uint64(1), uint64(1)]), "uint64")
+%!assert (class ([uint64(1), uint32(1)]), "uint64")
+%!assert (class ([uint64(1), uint16(1)]), "uint64")
+%!assert (class ([uint64(1), uint8(1)]), "uint64")
+%!assert (class ([uint64(1), single(1)]), "uint64")
+%!assert (class ([uint64(1), double(1)]), "uint64")
+%!assert (class ([uint64(1), cell(1)]), "cell")
+%!assert (class ([uint64(1), true]), "uint64")
+%!assert (class ([uint64(1), "a"]), "char")
+
+%!assert (class ([uint32(1), int64(1)]), "uint32")
+%!assert (class ([uint32(1), int32(1)]), "uint32")
+%!assert (class ([uint32(1), int16(1)]), "uint32")
+%!assert (class ([uint32(1), int8(1)]), "uint32")
+%!assert (class ([uint32(1), uint64(1)]), "uint32")
+%!assert (class ([uint32(1), uint32(1)]), "uint32")
+%!assert (class ([uint32(1), uint16(1)]), "uint32")
+%!assert (class ([uint32(1), uint8(1)]), "uint32")
+%!assert (class ([uint32(1), single(1)]), "uint32")
+%!assert (class ([uint32(1), double(1)]), "uint32")
+%!assert (class ([uint32(1), cell(1)]), "cell")
+%!assert (class ([uint32(1), true]), "uint32")
+%!assert (class ([uint32(1), "a"]), "char")
+
+%!assert (class ([uint16(1), int64(1)]), "uint16")
+%!assert (class ([uint16(1), int32(1)]), "uint16")
+%!assert (class ([uint16(1), int16(1)]), "uint16")
+%!assert (class ([uint16(1), int8(1)]), "uint16")
+%!assert (class ([uint16(1), uint64(1)]), "uint16")
+%!assert (class ([uint16(1), uint32(1)]), "uint16")
+%!assert (class ([uint16(1), uint16(1)]), "uint16")
+%!assert (class ([uint16(1), uint8(1)]), "uint16")
+%!assert (class ([uint16(1), single(1)]), "uint16")
+%!assert (class ([uint16(1), double(1)]), "uint16")
+%!assert (class ([uint16(1), cell(1)]), "cell")
+%!assert (class ([uint16(1), true]), "uint16")
+%!assert (class ([uint16(1), "a"]), "char")
+
+%!assert (class ([uint8(1), int64(1)]), "uint8")
+%!assert (class ([uint8(1), int32(1)]), "uint8")
+%!assert (class ([uint8(1), int16(1)]), "uint8")
+%!assert (class ([uint8(1), int8(1)]), "uint8")
+%!assert (class ([uint8(1), uint64(1)]), "uint8")
+%!assert (class ([uint8(1), uint32(1)]), "uint8")
+%!assert (class ([uint8(1), uint16(1)]), "uint8")
+%!assert (class ([uint8(1), uint8(1)]), "uint8")
+%!assert (class ([uint8(1), single(1)]), "uint8")
+%!assert (class ([uint8(1), double(1)]), "uint8")
+%!assert (class ([uint8(1), cell(1)]), "cell")
+%!assert (class ([uint8(1), true]), "uint8")
+%!assert (class ([uint8(1), "a"]), "char")
+
+%!assert (class ([single(1), int64(1)]), "int64")
+%!assert (class ([single(1), int32(1)]), "int32")
+%!assert (class ([single(1), int16(1)]), "int16")
+%!assert (class ([single(1), int8(1)]), "int8")
+%!assert (class ([single(1), uint64(1)]), "uint64")
+%!assert (class ([single(1), uint32(1)]), "uint32")
+%!assert (class ([single(1), uint16(1)]), "uint16")
+%!assert (class ([single(1), uint8(1)]), "uint8")
+%!assert (class ([single(1), single(1)]), "single")
+%!assert (class ([single(1), double(1)]), "single")
+%!assert (class ([single(1), cell(1)]), "cell")
+%!assert (class ([single(1), true]), "single")
+%!assert (class ([single(1), "a"]), "char")
+
+%!assert (class ([double(1), int64(1)]), "int64")
+%!assert (class ([double(1), int32(1)]), "int32")
+%!assert (class ([double(1), int16(1)]), "int16")
+%!assert (class ([double(1), int8(1)]), "int8")
+%!assert (class ([double(1), uint64(1)]), "uint64")
+%!assert (class ([double(1), uint32(1)]), "uint32")
+%!assert (class ([double(1), uint16(1)]), "uint16")
+%!assert (class ([double(1), uint8(1)]), "uint8")
+%!assert (class ([double(1), single(1)]), "single")
+%!assert (class ([double(1), double(1)]), "double")
+%!assert (class ([double(1), cell(1)]), "cell")
+%!assert (class ([double(1), true]), "double")
+%!assert (class ([double(1), "a"]), "char")
+
+%!assert (class ([cell(1), int64(1)]), "cell")
+%!assert (class ([cell(1), int32(1)]), "cell")
+%!assert (class ([cell(1), int16(1)]), "cell")
+%!assert (class ([cell(1), int8(1)]), "cell")
+%!assert (class ([cell(1), uint64(1)]), "cell")
+%!assert (class ([cell(1), uint32(1)]), "cell")
+%!assert (class ([cell(1), uint16(1)]), "cell")
+%!assert (class ([cell(1), uint8(1)]), "cell")
+%!assert (class ([cell(1), single(1)]), "cell")
+%!assert (class ([cell(1), double(1)]), "cell")
+%!assert (class ([cell(1), cell(1)]), "cell")
+%!assert (class ([cell(1), true]), "cell")
+%!assert (class ([cell(1), "a"]), "cell")
+
+%!assert (class ([true, int64(1)]), "int64")
+%!assert (class ([true, int32(1)]), "int32")
+%!assert (class ([true, int16(1)]), "int16")
+%!assert (class ([true, int8(1)]), "int8")
+%!assert (class ([true, uint64(1)]), "uint64")
+%!assert (class ([true, uint32(1)]), "uint32")
+%!assert (class ([true, uint16(1)]), "uint16")
+%!assert (class ([true, uint8(1)]), "uint8")
+%!assert (class ([true, single(1)]), "single")
+%!assert (class ([true, double(1)]), "double")
+%!assert (class ([true, cell(1)]), "cell")
+%!assert (class ([true, true]), "logical")
+%!assert (class ([true, "a"]), "char")
+
+%!assert (class (["a", int64(1)]), "char")
+%!assert (class (["a", int32(1)]), "char")
+%!assert (class (["a", int16(1)]), "char")
+%!assert (class (["a", int8(1)]), "char")
+%!assert (class (["a", int64(1)]), "char")
+%!assert (class (["a", int32(1)]), "char")
+%!assert (class (["a", int16(1)]), "char")
+%!assert (class (["a", int8(1)]), "char")
+%!assert (class (["a", single(1)]), "char")
+%!assert (class (["a", double(1)]), "char")
+%!assert (class (["a", cell(1)]), "cell")
+%!assert (class (["a", true]), "char")
+%!assert (class (["a", "a"]), "char")
+
+%!assert (class ([cell(1), struct("foo", "bar")]), "cell")
+%!error [struct("foo", "bar"), cell(1)]
+
+%!test <*39041> assert (class ([cell(0), struct()]), "cell")
+%!test <51086> assert (class ([struct(), cell(0)]), "struct")
+
+%!assert ([,1], 1)
+%!assert ([1,], 1)
+%!assert ([,1,], 1)
+%!assert ([,1,;;], 1)
+%!assert ([,1,;,;], 1)
+
+%!assert ([1,1], ones (1, 2))
+%!assert ([,1,1], ones (1, 2))
+%!assert ([1,1,], ones (1, 2))
+%!assert ([,1,1,], ones (1, 2))
+%!assert ([,1,1,;;], ones (1, 2))
+%!assert ([,1,1,;,;], ones (1, 2))
+%!assert ([,;,1,1], ones (1, 2))
+
+%!assert ([1;1], ones (2, 1))
+%!assert ([1,;1], ones (2, 1))
+%!assert ([1,;,;1], ones (2, 1))
+
+%!error eval ("[,,]")
+%!error eval ("[,,;,]")
+%!error eval ("[,;,,;,]")
+
+%!assert (isnull ([,]))
+%!assert (isnull ([;]))
+%!assert (isnull ([;;]))
+%!assert (isnull ([;,;]))
+%!assert (isnull ([,;,;,]))
+*/
--- a/libinterp/parse-tree/pt-tm-const.h	Wed May 16 12:17:25 2018 -0400
+++ b/libinterp/parse-tree/pt-tm-const.h	Wed May 16 19:01:14 2018 -0400
@@ -25,11 +25,12 @@
 
 #include "octave-config.h"
 
+#include <list>
+#include <memory>
 #include <string>
 
 #include "Array.h"
 #include "Sparse.h"
-#include "base-list.h"
 
 #include "data.h"
 #include "dim-vector.h"
@@ -43,395 +44,198 @@
   class tree_evaluator;
   class tree_matrix;
 
-  // General matrices.  This list type is much more work to handle than
-  // constant matrices, but it allows us to construct matrices from
-  // other matrices, variables, and functions.
-
-  // But first, some internal classes that make our job much easier.
-
-  class
-  tm_row_const
-  {
-  private:
-
-    class
-    tm_row_const_rep : public base_list<octave_value>
-    {
-    public:
-
-      tm_row_const_rep (void)
-        : m_count (1), m_dv (0, 0), m_all_str (false),
-          m_all_sq_str (false), m_all_dq_str (false),
-          m_some_str (false), m_all_real (false), m_all_cmplx (false),
-          m_all_mt (true), m_any_cell (false), m_any_sparse (false),
-          m_any_class (false), m_all_1x1 (false),
-          m_first_elem_is_struct (false), m_class_nm (), m_ok (false)
-      { }
-
-      tm_row_const_rep (const tree_argument_list& row, tree_evaluator& tw)
-        : m_count (1), m_dv (0, 0), m_all_str (false), m_all_sq_str (false),
-          m_some_str (false), m_all_real (false), m_all_cmplx (false),
-          m_all_mt (true), m_any_cell (false), m_any_sparse (false),
-          m_any_class (false), m_all_1x1 (! row.empty ()),
-          m_first_elem_is_struct (false), m_class_nm (), m_ok (false)
-      { init (row, tw); }
-
-      ~tm_row_const_rep (void) = default;
-
-      refcount<int> m_count;
-
-      dim_vector m_dv;
-
-      bool m_all_str;
-      bool m_all_sq_str;
-      bool m_all_dq_str;
-      bool m_some_str;
-      bool m_all_real;
-      bool m_all_cmplx;
-      bool m_all_mt;
-      bool m_any_cell;
-      bool m_any_sparse;
-      bool m_any_class;
-      bool m_all_1x1;
-      bool m_first_elem_is_struct;
-
-      std::string m_class_nm;
-
-      bool m_ok;
-
-      void do_init_element (const octave_value&, bool&);
-
-      void init (const tree_argument_list&, tree_evaluator& tw);
-
-      void cellify (void);
-
-    private:
-
-      tm_row_const_rep (const tm_row_const_rep&);
-
-      tm_row_const_rep& operator = (const tm_row_const_rep&);
-
-    };
-
-  public:
-
-    typedef tm_row_const_rep::iterator iterator;
-    typedef tm_row_const_rep::const_iterator const_iterator;
+  // Evaluate tree_matrix objects and convert them to octave_value
+  // arrays (full and sparse numeric, char, cell, struct, class and
+  // anything else that works like an array).  Use a separate class
+  // (tm_const) and pass the evaluator object to it instead of doing
+  // all this work in tree_evaluator::visit_matrix because the job is
+  // fairly large and requires extra data (stored in the tm_info
+  // class) for each row and for the overall array.
 
-    tm_row_const (void)
-      : m_rep (nullptr) { }
-
-    tm_row_const (const tree_argument_list& row, tree_evaluator& tw)
-      : m_rep (new tm_row_const_rep (row, tw)) { }
-
-    tm_row_const (const tm_row_const& x)
-      : m_rep (x.m_rep)
-    {
-      if (m_rep)
-        m_rep->m_count++;
-    }
-
-    tm_row_const& operator = (const tm_row_const& x)
-    {
-      if (this != &x && m_rep != x.m_rep)
-        {
-          if (m_rep && --m_rep->m_count == 0)
-            delete m_rep;
-
-          m_rep = x.m_rep;
-
-          if (m_rep)
-            m_rep->m_count++;
-        }
-
-      return *this;
-    }
-
-    ~tm_row_const (void)
-    {
-      if (m_rep && --m_rep->m_count == 0)
-        delete m_rep;
-    }
-
-    octave_idx_type rows (void) { return m_rep->m_dv(0); }
-    octave_idx_type cols (void) { return m_rep->m_dv(1); }
+  // Evaluate all elements of the array, recording info about each
+  // row, then create summary info for the full array.  Compute the
+  // result type and dimension first before copying values.
 
-    bool empty (void) const { return m_rep->empty (); }
-
-    size_t length (void) const { return m_rep->length (); }
-
-    dim_vector dims (void) { return m_rep->m_dv; }
-
-    bool all_strings_p (void) const { return m_rep->m_all_str; }
-    bool all_sq_strings_p (void) const { return m_rep->m_all_sq_str; }
-    bool all_dq_strings_p (void) const { return m_rep->m_all_dq_str; }
-    bool some_strings_p (void) const { return m_rep->m_some_str; }
-    bool all_real_p (void) const { return m_rep->m_all_real; }
-    bool all_complex_p (void) const { return m_rep->m_all_cmplx; }
-    bool all_empty_p (void) const { return m_rep->m_all_mt; }
-    bool any_cell_p (void) const { return m_rep->m_any_cell; }
-    bool any_sparse_p (void) const { return m_rep->m_any_sparse; }
-    bool any_class_p (void) const { return m_rep->m_any_class; }
-    bool all_1x1_p (void) const { return m_rep->m_all_1x1; }
-    bool first_elem_struct_p (void) const { return m_rep->m_first_elem_is_struct; }
+  // FIXME: Handle overloading of horzcat and vertcat for for built-in
+  // types.
 
-    std::string class_name (void) const { return m_rep->m_class_nm; }
-
-    void cellify (void) { m_rep->cellify (); }
-
-    operator bool () const { return (m_rep && m_rep->m_ok); }
-
-    iterator begin (void) { return m_rep->begin (); }
-    const_iterator begin (void) const { return m_rep->begin (); }
+  // Summary info about the current row or matrix.
 
-    iterator end (void) { return m_rep->end (); }
-    const_iterator end (void) const { return m_rep->end (); }
-
-  private:
-
-    tm_row_const_rep *m_rep;
-  };
-
-  class
-  tm_const : public base_list<tm_row_const>
+  class tm_info
   {
   public:
 
-    tm_const (const tree_matrix& tm, tree_evaluator& tw)
-      : m_dv (0, 0), m_all_str (false), m_all_sq_str (false),
-        m_all_dq_str (false),
-        m_some_str (false), m_all_real (false), m_all_cmplx (false),
-        m_all_mt (true), m_any_cell (false), m_any_sparse (false),
-        m_any_class (false), m_class_nm (), m_ok (false)
-    { init (tm, tw); }
-
-    ~tm_const (void) = default;
-
-    octave_idx_type rows (void) const { return m_dv.elem (0); }
-    octave_idx_type cols (void) const { return m_dv.elem (1); }
+    tm_info (bool obj_is_empty)
+      : m_dv (0, 0), m_all_strings (true), m_all_sq_strings (true),
+        m_all_dq_strings (true), m_some_strings (false),
+        m_all_real (true), m_all_complex (true), m_all_empty (true),
+        m_any_cell (false), m_any_sparse (false),
+        m_any_class (false), m_all_1x1 (! obj_is_empty),
+        m_first_elem_is_struct (false), m_class_name ()
+    { }
 
     dim_vector dims (void) const { return m_dv; }
 
-    bool all_strings_p (void) const { return m_all_str; }
-    bool all_sq_strings_p (void) const { return m_all_sq_str; }
-    bool all_dq_strings_p (void) const { return m_all_dq_str; }
-    bool some_strings_p (void) const { return m_some_str; }
+    octave_idx_type rows (void) const { return m_dv(0); }
+    octave_idx_type cols (void) const { return m_dv(1); }
+
+    bool all_strings_p (void) const { return m_all_strings; }
+    bool all_sq_strings_p (void) const { return m_all_sq_strings; }
+    bool all_dq_strings_p (void) const { return m_all_dq_strings; }
+    bool some_strings_p (void) const { return m_some_strings; }
     bool all_real_p (void) const { return m_all_real; }
-    bool all_complex_p (void) const { return m_all_cmplx; }
-    bool all_empty_p (void) const { return m_all_mt; }
+    bool all_complex_p (void) const { return m_all_complex; }
+    bool all_empty_p (void) const { return m_all_empty; }
     bool any_cell_p (void) const { return m_any_cell; }
     bool any_sparse_p (void) const { return m_any_sparse; }
     bool any_class_p (void) const { return m_any_class; }
     bool all_1x1_p (void) const { return m_all_1x1; }
+    bool first_elem_struct_p (void) const { return m_first_elem_is_struct; }
 
-    std::string class_name (void) const { return m_class_nm; }
+    std::string class_name (void) const { return m_class_name; }
+
+  protected:
+
+    // Size of this row or matrix after evaluation.
+    dim_vector m_dv;
+
+    // Are all elements character strings?
+    bool m_all_strings;
+
+    // Are all elements double-quoted character strings?
+    bool m_all_sq_strings;
+
+    // Are all elements single-quoted character strings?
+    bool m_all_dq_strings;
+
+    // Are any elements character strings?
+    bool m_some_strings;
+
+    // Are all elements real valued?
+    bool m_all_real;
+
+    // Are all elements complex valued?
+    bool m_all_complex;
+
+    // Are all elements empty?
+    bool m_all_empty;
+
+    // Are any elements cells?
+    bool m_any_cell;
+
+    // Are any elements sparse arrays?
+    bool m_any_sparse;
+
+    // Are any elements sparse class objects?
+    bool m_any_class;
+
+    // Do all elements have dimensions 1x1?
+    bool m_all_1x1;
 
-    operator bool () const { return m_ok; }
+    // Is the first element a struct?
+    bool m_first_elem_is_struct;
+
+    // Class name of result.
+    std::string m_class_name;
+  };
+
+  class tm_row_const : public tm_info
+  {
+  public:
+
+    typedef std::list<octave_value>::iterator iterator;
+    typedef std::list<octave_value>::const_iterator const_iterator;
+
+    tm_row_const (void) = delete;
+
+    tm_row_const (const tree_argument_list& row, tree_evaluator& tw)
+      : tm_info (row.empty ()), m_values ()
+    {
+      init (row, tw);
+    }
+
+    tm_row_const (const tm_row_const&) = default;
+
+    tm_row_const& operator = (const tm_row_const&) = delete;
+
+    ~tm_row_const (void) = default;
+
+    iterator begin (void) { return m_values.begin (); }
+    const_iterator begin (void) const { return m_values.begin (); }
+
+    iterator end (void) { return m_values.end (); }
+    const_iterator end (void) const { return m_values.end (); }
+
+    bool empty (void) const { return m_values.empty (); }
+
+    size_t length (void) const { return m_values.size (); }
+
+    void cellify (void);
 
   private:
 
-    dim_vector m_dv;
+    std::list<octave_value> m_values;
 
-    bool m_all_str;
-    bool m_all_sq_str;
-    bool m_all_dq_str;
-    bool m_some_str;
-    bool m_all_real;
-    bool m_all_cmplx;
-    bool m_all_mt;
-    bool m_any_cell;
-    bool m_any_sparse;
-    bool m_any_class;
-    bool m_all_1x1;
+    void init_element (const octave_value&, bool&);
 
-    std::string m_class_nm;
-
-    bool m_ok;
-
-    tm_const (void);
-
-    tm_const (const tm_const&);
-
-    tm_const& operator = (const tm_const&);
-
-    void init (const tree_matrix& tm, tree_evaluator& tw);
+    void init (const tree_argument_list&, tree_evaluator& tw);
   };
 
-  template <typename TYPE, typename T>
-  void
-  single_type_concat (Array<T>& result, tm_const& tmp)
-  {
-    octave_idx_type r = 0;
-    octave_idx_type c = 0;
-
-    for (tm_row_const& row : tmp)
-      {
-        // Skip empty arrays to allow looser rules.
-        if (row.dims ().any_zero ())
-          continue;
-
-        for (auto& elt : row)
-          {
-            octave_quit ();
-
-            TYPE ra = octave_value_extract<TYPE> (elt);
-
-            // Skip empty arrays to allow looser rules.
-
-            if (! ra.isempty ())
-              {
-                result.insert (ra, r, c);
-
-                c += ra.columns ();
-              }
-          }
-
-        r += row.rows ();
-        c = 0;
-      }
-  }
-
-  template <typename TYPE, typename T>
-  void
-  single_type_concat (Array<T>& result, const dim_vector& dv,
-                      tm_const& tmp)
-  {
-    if (dv.any_zero ())
-      {
-        result = Array<T> (dv);
-        return;
-      }
-
-    if (tmp.length () == 1)
-      {
-        // If possible, forward the operation to liboctave.
-        // Single row.
-        tm_row_const& row = tmp.front ();
-        if (! (equal_types<T, char>::value || equal_types<T, octave_value>::value)
-            && row.all_1x1_p ())
-          {
-            // Optimize all scalars case.
-            result.clear (dv);
-            assert (static_cast<size_t> (result.numel ()) == row.length ());
-            octave_idx_type i = 0;
-            for (const auto& elt : row)
-              result(i++) = octave_value_extract<T> (elt);
-
-            return;
-          }
-
-        octave_idx_type ncols = row.length ();
-        octave_idx_type i = 0;
-        OCTAVE_LOCAL_BUFFER (Array<T>, array_list, ncols);
-
-        for (const auto& elt : row)
-          {
-            octave_quit ();
-
-            array_list[i++] = octave_value_extract<TYPE> (elt);
-          }
-
-        result = Array<T>::cat (-2, ncols, array_list);
-      }
-    else
-      {
-        result = Array<T> (dv);
-        single_type_concat<TYPE> (result, tmp);
-      }
-  }
-
-  template <typename TYPE, typename T>
-  void
-  single_type_concat (Sparse<T>& result, const dim_vector& dv,
-                      tm_const& tmp)
+  class tm_const : public tm_info
   {
-    if (dv.any_zero ())
-      {
-        result = Sparse<T> (dv);
-        return;
-      }
+  public:
+
+    typedef std::list<tm_row_const>::iterator iterator;
+    typedef std::list<tm_row_const>::const_iterator const_iterator;
+
+    tm_const (void) = delete;
+
+    tm_const (const tree_matrix& tm, tree_evaluator& tw)
+      : tm_info (tm.empty ()), m_evaluator (tw), m_tm_rows ()
+    {
+      init (tm);
+    }
 
-    // Sparse matrices require preallocation for efficient indexing; besides,
-    // only horizontal concatenation can be efficiently handled by indexing.
-    // So we just cat all rows through liboctave, then cat the final column.
-    octave_idx_type nrows = tmp.length ();
-    octave_idx_type j = 0;
-    OCTAVE_LOCAL_BUFFER (Sparse<T>, sparse_row_list, nrows);
-    for (tm_row_const& row : tmp)
-      {
-        octave_idx_type ncols = row.length ();
-        octave_idx_type i = 0;
-        OCTAVE_LOCAL_BUFFER (Sparse<T>, sparse_list, ncols);
+    // No copying!
+
+    tm_const (const tm_const&) = delete;
+
+    tm_const& operator = (const tm_const&) = delete;
 
-        for (auto& elt : row)
-          {
-            octave_quit ();
+    ~tm_const (void) = default;
 
-            sparse_list[i] = octave_value_extract<TYPE> (elt);
-            i++;
-          }
+    octave_value concat (char string_fill_char) const;
 
-        Sparse<T> stmp = Sparse<T>::cat (-2, ncols, sparse_list);
-        sparse_row_list[j] = stmp;
-        j++;
-      }
+  private:
 
-    result = Sparse<T>::cat (-1, nrows, sparse_row_list);
-  }
+    tree_evaluator& m_evaluator;
 
-  template <typename MAP>
-  void
-  single_type_concat (octave_map& result, const dim_vector& dv,
-                      tm_const& tmp)
-  {
-    if (dv.any_zero ())
-      {
-        result = octave_map (dv);
-        return;
-      }
+    // The list of lists of octave_value objects that contain the
+    // values of elements in each row of the tree_matrix object we are
+    // evaluating.
+
+    std::list<tm_row_const> m_tm_rows;
 
-    octave_idx_type nrows = tmp.length ();
-    octave_idx_type j = 0;
-    OCTAVE_LOCAL_BUFFER (octave_map, map_row_list, nrows);
-    for (tm_row_const& row : tmp)
-      {
-        octave_idx_type ncols = row.length ();
-        octave_idx_type i = 0;
-        OCTAVE_LOCAL_BUFFER (MAP, map_list, ncols);
+    void init (const tree_matrix& tm);
 
-        for (auto& elt : row)
-          {
-            octave_quit ();
+    octave_value char_array_concat (char string_fill_char) const;
+
+    octave_value class_concat (void) const;
 
-            map_list[i] = octave_value_extract<MAP> (elt);
-            i++;
-          }
+    octave_value generic_concat (void) const;
 
-        octave_map mtmp = octave_map::cat (-2, ncols, map_list);
-        map_row_list[j] = mtmp;
-        j++;
-      }
-
-    result = octave_map::cat (-1, nrows, map_row_list);
-  }
+    template <typename TYPE>
+    void array_concat_internal (TYPE& result) const;
 
-  template <typename TYPE>
-  octave_value
-  do_single_type_concat (const dim_vector& dv, tm_const& tmp)
-  {
-    TYPE result;
-
-    single_type_concat<TYPE> (result, dv, tmp);
+    template <typename TYPE>
+    TYPE array_concat (void) const;
 
-    return result;
-  }
+    template <typename TYPE>
+    TYPE sparse_array_concat (void) const;
 
-  template <>
-  octave_value
-  do_single_type_concat<octave_map> (const dim_vector& dv,
-                                     tm_const& tmp);
-
-  extern octave_value do_class_concat (tm_const& tmc);
+    template <typename MAP>
+    octave_map map_concat (void) const;
+  };
 }
 
 #endif