diff --git a/src/base.cc b/src/base.cc index 3440122..9c27046 100644 --- a/src/base.cc +++ b/src/base.cc @@ -48,7 +48,7 @@ namespace php_arma template PHP_ARMA_METHOD(base, neg, T, T1) { - + operators::neg(&EX(This), return_value); } template @@ -59,7 +59,7 @@ namespace php_arma Z_PARAM_ZVAL(other) ZEND_PARSE_PARAMETERS_END(); - operators::add(&EX(This), other, return_value); + operators::mul(&EX(This), other, return_value); } template diff --git a/src/base.hh b/src/base.hh index 8f5085e..1bfdd79 100644 --- a/src/base.hh +++ b/src/base.hh @@ -10,6 +10,34 @@ #include "php_arma.hh" #include "complex.hh" +#define PHP_ARMA_BASE_OPERATOR_EX2(cls, type, func) \ + PHP_ARMA_OPERATOR_EX2((base>), cls, func) + +#define PHP_ARMA_BASE_OPERATOR_EX(cls, func) \ + PHP_ARMA_BASE_OPERATOR_EX2(cls, double, func) \ + PHP_ARMA_BASE_OPERATOR_EX2(cls, zend_long, func) \ + PHP_ARMA_BASE_OPERATOR_EX2(cls, cx_double, func) + +#define PHP_ARMA_BASE_OPERATOR(func) \ + PHP_ARMA_OPERATOR_BEGIN(base_ce) \ + PHP_ARMA_BASE_OPERATOR_EX(mat, func) \ + PHP_ARMA_BASE_OPERATOR_EX(subview_mat, func) \ + PHP_ARMA_OPERATOR_END() + +#define PHP_ARMA_BASE_ASSIGN_OPERATOR_EX2(cls, type, func) \ + PHP_ARMA_ASSIGN_OPERATOR_EX2((base>), cls, func) + +#define PHP_ARMA_BASE_ASSIGN_OPERATOR_EX(cls, func) \ + PHP_ARMA_BASE_ASSIGN_OPERATOR_EX2(cls, double, func) \ + PHP_ARMA_BASE_ASSIGN_OPERATOR_EX2(cls, zend_long, func) \ + PHP_ARMA_BASE_ASSIGN_OPERATOR_EX2(cls, cx_double, func) + +#define PHP_ARMA_BASE_ASSIGN_OPERATOR(func) \ + PHP_ARMA_OPERATOR_BEGIN(base_ce) \ + PHP_ARMA_BASE_ASSIGN_OPERATOR_EX(mat, func) \ + PHP_ARMA_BASE_ASSIGN_OPERATOR_EX(subview_mat, func) \ + PHP_ARMA_OPERATOR_END() + namespace php_arma { template @@ -52,6 +80,8 @@ namespace php_arma } else { goto not_orig_or_subview; } + } else { + __builtin_unreachable(); } } else { if (Z_OBJCE_P(zv1) == ChildT::orig_t::ce) { @@ -76,6 +106,10 @@ namespace php_arma return false; } } + if (zv1 == return_value) { + // Release first operand in case of assign operator to prevent memory leak. + zval_ptr_dtor(zv1); + } RETVAL_OBJ(ChildT::orig_t::create(std::move(ret))); return true; } catch (const std::logic_error& err) { @@ -139,8 +173,23 @@ namespace php_arma }); } + static bool neg(zval *zv, zval *retval) + { + return arithmetic_op(zv, zv, retval, [](auto v1, auto v2) { + return -*v1; + }); + } + static bool mul(zval *zv1, zval *zv2, zval *retval) { +#if PHP_VERSION_ID >= 70300 + if (Z_TYPE_P(zv2) == IS_LONG && Z_LVAL_P(zv2) == -1) { + return neg(zv1, retval); +#else + if (Z_TYPE_P(zv1) == IS_LONG && Z_LVAL_P(zv1) == -1) { + return neg(zv2, retval); +#endif + } return arithmetic_op(zv1, zv2, retval, [](auto v1, auto v2) { return *v1 * *v2; }); diff --git a/src/operators.cc b/src/operators.cc index c5f027e..acc0e61 100644 --- a/src/operators.cc +++ b/src/operators.cc @@ -7,6 +7,7 @@ #include "php_arma.hh" #include "operators.hh" #include "complex.hh" +#include "base.hh" #include "dense.hh" #include "mat.hh" #include "subview_mat.hh" @@ -91,6 +92,7 @@ namespace php_arma { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_OPERATOR(add); + PHP_ARMA_BASE_OPERATOR(add); return false; }); @@ -100,6 +102,7 @@ namespace php_arma { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_ASSIGN_OPERATOR(add); + PHP_ARMA_BASE_ASSIGN_OPERATOR(add); return false; }); @@ -109,6 +112,7 @@ namespace php_arma { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_OPERATOR(sub); + PHP_ARMA_BASE_OPERATOR(sub); return false; }); @@ -118,6 +122,7 @@ namespace php_arma { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_ASSIGN_OPERATOR(sub); + PHP_ARMA_BASE_ASSIGN_OPERATOR(sub); return false; }); @@ -127,6 +132,7 @@ namespace php_arma { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_OPERATOR(mul); + PHP_ARMA_BASE_OPERATOR(mul); PHP_ARMA_MAPVAL_OPERATOR(mul); return false; @@ -137,6 +143,7 @@ namespace php_arma { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_ASSIGN_OPERATOR(mul); + PHP_ARMA_BASE_ASSIGN_OPERATOR(mul); return false; }); @@ -146,6 +153,7 @@ namespace php_arma { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_OPERATOR(div); + PHP_ARMA_BASE_OPERATOR(div); return false; }); @@ -155,6 +163,25 @@ namespace php_arma { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_ASSIGN_OPERATOR(div); + PHP_ARMA_BASE_ASSIGN_OPERATOR(div); + + return false; + }); + } + + int mod_handler(zend_execute_data *execute_data) + { + return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { + PHP_ARMA_BASE_OPERATOR(dotMul); + + return false; + }); + } + + int mod_assign_handler(zend_execute_data *execute_data) + { + return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { + PHP_ARMA_BASE_ASSIGN_OPERATOR(dotMul); return false; }); @@ -243,6 +270,8 @@ namespace php_arma std::make_tuple(ZEND_ASSIGN_MUL, mul_assign_handler), std::make_tuple(ZEND_DIV, div_handler), std::make_tuple(ZEND_ASSIGN_DIV, div_assign_handler), + std::make_tuple(ZEND_MOD, mod_handler), + std::make_tuple(ZEND_ASSIGN_MOD, mod_assign_handler), std::make_tuple(ZEND_POW, pow_handler), std::make_tuple(ZEND_ASSIGN_POW, pow_assign_handler), std::make_tuple(ZEND_BW_NOT, bw_not_handler), diff --git a/tests/014-arithmetic.phpt b/tests/014-arithmetic.phpt new file mode 100644 index 0000000..1de702a --- /dev/null +++ b/tests/014-arithmetic.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test for methods performing arithmetic operations. +--SKIPIF-- + +--FILE-- +add($mat1)], + [str2mat('2 3; 4 5'), $mat->add(1)], + [str2mat('-4 -4; -4 -4'), $mat->sub($mat1)], + [str2mat('-1 0; 1 2'), $mat->sub(2)], + [str2mat('-1 -2; -3 -4'), $mat->neg()], + [str2mat('19 22; 43 50'), $mat->mul($mat1)], + [str2mat('2 4; 6 8'), $mat->mul(2)], + [str2mat('5 12; 21 32'), $mat->dotMul($mat1)], + [str2mat('0 0; 0 0'), $mat->div($mat1)], + [str2mat('0 1; 1 2'), $mat->div(2)], +); + +?> +--EXPECT-- diff --git a/tests/015-arithmetic-operators.phpt b/tests/015-arithmetic-operators.phpt new file mode 100644 index 0000000..6495503 --- /dev/null +++ b/tests/015-arithmetic-operators.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test for operator overloading of arithmetic operations. +--SKIPIF-- + +--FILE-- +forEach(function (Arma\MapVal $elem) { + if ($elem->val() == 0) { + $elem->setTo(7); + } +}); + +batch_assert_mat('operator overloading of arithmetic operations', + [$mat->add($mat1), $mat + $mat1], + [$mat->add(1), $mat + 1], + [$mat->sub($mat1), $mat - $mat1], + [$mat->sub(2), $mat - 2], + [$mat->neg(), -$mat], + [$mat->mul($mat1), $mat * $mat1], + [$mat->mul(2), $mat * 2], + [$mat->dotMul($mat1), $mat % $mat1], + [$mat->div($mat1), $mat / $mat1], + [$mat->div(2), $mat / 2] +); + +$mat2 = $mat->div($mat1); +$mat3 = $mat1->add($mat2); +$mat4 = $mat2->mul($mat3); +$mat5 = $mat3->sub($mat4); +$mat1 -= $mat *= $mat1 += $mat /= $mat1; + +batch_assert_mat('operator overloading of arithmetic operations', [$mat5, $mat1]); + +?> +--EXPECT--