Mercurial > octave
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