implement arithmetic operations and corresponding operator overloading

This commit is contained in:
CismonX 2019-08-19 04:24:08 +08:00
parent cc8bafe60b
commit 1eb45fe29a
5 changed files with 161 additions and 2 deletions

View File

@ -48,7 +48,7 @@ namespace php_arma
template <typename T, typename T1>
PHP_ARMA_METHOD(base, neg, T, T1)
{
operators::neg(&EX(This), return_value);
}
template <typename T, typename T1>
@ -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 <typename T, typename T1>

View File

@ -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<type, cls<type>>), cls<type>, 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<type, cls<type>>), cls<type>, 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 <typename T, typename ChildT>
@ -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<false>(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<true>(zv1, zv2, retval, [](auto v1, auto v2) {
return *v1 * *v2;
});

View File

@ -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),

34
tests/014-arithmetic.phpt Normal file
View File

@ -0,0 +1,34 @@
--TEST--
Test for methods performing arithmetic operations.
--SKIPIF--
<?php
require_once 'includes/loaded.php';
is_php_arma_loaded();
?>
--FILE--
<?php
require_once 'includes/assert.php';
$mat = Arma\IMat::fromString('1 2; 3 4');
$mat1 = Arma\IMat::fromString('5 6; 7 8');
function str2mat($str) {
return Arma\IMat::fromString($str);
}
batch_assert_mat('arithmetic operations',
[str2mat('6 8; 10 12'), $mat->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--

View File

@ -0,0 +1,47 @@
--TEST--
Test for operator overloading of arithmetic operations.
--SKIPIF--
<?php
require_once 'includes/loaded.php';
require_once 'includes/supports.php';
if (is_php_arma_loaded()) {
supports_operator_overloading();
}
?>
--FILE--
<?php
require_once 'includes/assert.php';
$mat = Arma\IMat::init(2, 2, Arma\Fill::RANDN);
$mat1 = Arma\IMat::init(2, 2, Arma\Fill::RANDN);
$mat1->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--