From 6cf646a6d0846d0bae109d5dfcfaa40a316f7d8d Mon Sep 17 00:00:00 2001 From: CismonX Date: Thu, 30 May 2019 00:50:39 +0800 Subject: [PATCH] Implement `Mat::fromArray()`. Add tests. --- src/mat.cc | 50 +++++++++++++++++++++++++++++++++++++++ src/mat.hh | 1 + src/php_arma.hh | 3 +++ stubs/Mat.php | 10 ++++++++ stubs/internal/Vector.php | 8 +++++++ tests/003-mat-init.phpt | 48 +++++++++++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+) create mode 100644 tests/003-mat-init.phpt diff --git a/src/mat.cc b/src/mat.cc index eddaba2..368f857 100644 --- a/src/mat.cc +++ b/src/mat.cc @@ -48,10 +48,60 @@ namespace php_arma RETVAL_OBJ(zobj); } + template + PHP_ARMA_METHOD(mat, fromArray, T) + { + zval *arr; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_DEREF(arr) + ZEND_PARSE_PARAMETERS_END(); + + auto num_rows = zend_hash_num_elements(Z_ARR_P(arr)); + if (UNEXPECTED(num_rows == 0)) { + RETURN_OBJ(create()); + } + auto first_row = zend_hash_index_find(Z_ARR_P(arr), 0); + if (UNEXPECTED(first_row == nullptr || Z_TYPE_P(first_row) != IS_ARRAY)) { + zend_throw_exception(zend_ce_error, "bad input matrix", 0); + RETURN_NULL(); + } + auto num_cols = zend_hash_num_elements(Z_ARR_P(first_row)); + if (UNEXPECTED(num_rows == 0)) { + RETURN_OBJ(create()); + } + + auto zobj = create(num_rows, num_cols); + auto native = ZOBJ_NATIVE(zobj); + size_t idx_row = 0; + size_t idx_col = 0; + ZEND_HASH_FOREACH_VAL(Z_ARR_P(arr), zval *row) + if (idx_row > num_rows - 1) { + break; + } + if (UNEXPECTED(Z_TYPE_P(row) != IS_ARRAY)) { + zend_throw_exception(zend_ce_error, "bad input matrix", 0); + break; + } + ZEND_HASH_FOREACH_VAL(Z_ARR_P(row), zval *elem) + if (idx_col > num_cols - 1) { + break; + } + if (UNEXPECTED(!zval_check_scalar(elem))) { + break; + } + native->at(idx_row, idx_col++) = zval_get_scalar(elem); + ZEND_HASH_FOREACH_END(); + ++idx_row; + idx_col = 0; + ZEND_HASH_FOREACH_END(); + RETVAL_OBJ(zobj); + } + template PHP_ARMA_START_ME(mat, T) PHP_ARMA_ME(init, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ARMA_ME(fromString, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ARMA_ME(fromArray, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ARMA_END_ME(); template diff --git a/src/mat.hh b/src/mat.hh index 042c2ea..ab6b2bf 100644 --- a/src/mat.hh +++ b/src/mat.hh @@ -40,6 +40,7 @@ namespace php_arma static PHP_FUNCTION(init); static PHP_FUNCTION(fromString); + static PHP_FUNCTION(fromArray); static void ce_init(zend_class_entry*); }; diff --git a/src/php_arma.hh b/src/php_arma.hh index 866e694..0b20c6e 100644 --- a/src/php_arma.hh +++ b/src/php_arma.hh @@ -80,11 +80,14 @@ Z_PARAM_LONG_EX2(dest, _dummy, 0, 1, 0) #define Z_PARAM_STR_DEREF(dest) \ Z_PARAM_STR_EX2(dest, 0, 1, 0) +#define Z_PARAM_ARRAY_DEREF(dest) \ + Z_PARAM_ARRAY_EX2(dest, 0, 1, 0) #else // Before PHP 7.2, parameter is dereferenced by default here. #define Z_PARAM_DOUBLE_DEREF Z_PARAM_DOUBLE #define Z_PARAM_LONG_DEREF Z_PARAM_LONG #define Z_PARAM_STR_DEREF Z_PARAM_STR +#define Z_PARAM_ARRAY_DEREF Z_PARAM_ARRAY #endif #ifdef PHP_ARMA_OPERATORS diff --git a/stubs/Mat.php b/stubs/Mat.php index 2b6a170..2950dcd 100644 --- a/stubs/Mat.php +++ b/stubs/Mat.php @@ -30,4 +30,14 @@ abstract class Mat implements Internal\DenseResizableMatrix static function fromString($text) { return new static; } + + /** + * Create the matrix from a PHP array. + * + * @param array $arr + * @return Mat + */ + static function fromArray($arr) { + return new static; + } } diff --git a/stubs/internal/Vector.php b/stubs/internal/Vector.php index 10d3a74..6d35106 100644 --- a/stubs/internal/Vector.php +++ b/stubs/internal/Vector.php @@ -30,6 +30,14 @@ interface Vector */ static function fromString($text); + /** + * Create the vector from a PHP array. + * + * @param array $arr + * @return static + */ + static function fromArray($arr); + // Vector subview /** diff --git a/tests/003-mat-init.phpt b/tests/003-mat-init.phpt new file mode 100644 index 0000000..dd8136c --- /dev/null +++ b/tests/003-mat-init.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test for initialization of `Mat` +--SKIPIF-- + +--FILE-- +nRows()], + [4, $mat->nCols()], + [3 * 4, $mat->nElem()], + [0., $mat->max()], + [0., $mat->min()] +); + +$mat1 = Arma\IMat::fromString('1 2; 3 4'); +batch_assert('initialization from string of `Arma\\Mat`', + [2, $mat1->nRows()], + [2, $mat1->nCols()], + [2 * 2, $mat1->nElem()], + [1, $mat1(0, 0)->val()], + [2, $mat1(0, 1)->val()], + [3, $mat1(1, 0)->val()], + [4, $mat1(1, 1)->val()] +); + +$mat2 = Arma\IMat::fromArray([ + [1, 2], + [3, 4] +]); +batch_assert('initialization from array of `Arma\\Mat`', + [2, $mat2->nRows()], + [2, $mat2->nCols()], + [2 * 2, $mat2->nElem()], + [1, $mat2(0, 0)->val()], + [2, $mat2(0, 1)->val()], + [3, $mat2(1, 0)->val()], + [4, $mat2(1, 1)->val()] +); + +?> +--EXPECT--