// // php-armadillo/base.hh // // @Author CismonX // #ifndef PHP_ARMA_BASE_HH #define PHP_ARMA_BASE_HH #include "common/php_arma.hh" #include "complex.hh" #ifdef PHP_ARMA_OPERATORS #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() #endif // PHP_ARMA_OPERATORS namespace php_arma { template struct base { using native_t = typename ChildT::native_t; struct operators; template zend_always_inline static bool arithmetic_op(zval *zv1, zval *zv2, zval *return_value, F&& func) { bool zv2_is_scalar = false; if (Z_TYPE_P(zv2) != IS_OBJECT) { if constexpr (AcceptScalar && !std::is_same_v) { if (EXPECTED(zval_is_scalar(zv2))) { zv2_is_scalar = true; goto op_body; } } throw_exception_ex(zend_ce_type_error, "bad arithmetic operation, unsupported type"); return false; } op_body: using n_orig_t = typename ChildT::orig_t::native_t; using n_subview_t = typename ChildT::subview_t::native_t; try { std::optional ret; auto o1 = Z_OBJ_P(zv1); decltype(o1) o2; if (zv2_is_scalar) { if constexpr (AcceptScalar) { auto scalar_val = zval_get_scalar(zv2); if (Z_OBJCE_P(zv1) == ChildT::orig_t::ce) { ret = func(to_native_object(o1), &scalar_val); } else if (EXPECTED(Z_OBJCE_P(zv1) == ChildT::subview_t::ce)) { ret = func(to_native_object(o1), &scalar_val); } else { goto not_orig_or_subview; } } else { __builtin_unreachable(); } } else { o2 = Z_OBJ_P(zv2); if (Z_OBJCE_P(zv1) == ChildT::orig_t::ce) { if (Z_OBJCE_P(zv2) == ChildT::orig_t::ce) { ret = func(to_native_object(o1), to_native_object(o2)); } else if (EXPECTED(Z_OBJCE_P(zv2) == ChildT::subview_t::ce)) { ret = func(to_native_object(o1), to_native_object(o2)); } else { goto not_orig_or_subview; } } else if (EXPECTED(Z_OBJCE_P(zv1) == ChildT::subview_t::ce)) { if (Z_OBJCE_P(zv2) == ChildT::orig_t::ce) { ret = func(to_native_object(o1), to_native_object(o2)); } else if (EXPECTED(Z_OBJCE_P(zv2) == ChildT::subview_t::ce)) { ret = func(to_native_object(o1), to_native_object(o2)); } else { goto not_orig_or_subview; } } else { not_orig_or_subview: throw_exception_ex(zend_ce_type_error, "bad arithmetic operation, object types should be the same"); 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) { throw_error(err.what()); return false; } } zend_always_inline static int count_elements(zval *zv, zend_long *count) { *count = Z_NATIVE_OBJ_P(zv)->n_elem; return SUCCESS; } PHP_ARMA_COMMON_DECLARE(); private: static PHP_FUNCTION(nElem); static PHP_FUNCTION(add); static PHP_FUNCTION(sub); static PHP_FUNCTION(neg); static PHP_FUNCTION(mul); static PHP_FUNCTION(dotMul); static PHP_FUNCTION(div); static PHP_FUNCTION(replace); static PHP_FUNCTION(transform); static PHP_FUNCTION(forEach); static PHP_FUNCTION(min); static PHP_FUNCTION(max); static PHP_FUNCTION(indexMin); static PHP_FUNCTION(indexMax); static PHP_FUNCTION(isEmpty); static PHP_FUNCTION(isFinite); static PHP_FUNCTION(isZero); static PHP_FUNCTION(hasInf); static PHP_FUNCTION(hasNan); static PHP_FUNCTION(print); static PHP_FUNCTION(rawPrint); static PHP_FUNCTION(__toString); static PHP_FUNCTION(__debugInfo); }; void base_init(); constexpr const char base_php_name[] = "Base"; inline zend_class_entry *base_ce; template struct base::operators { static bool add(zval *zv1, zval *zv2, zval *retval) { return arithmetic_op(zv1, zv2, retval, [](auto v1, auto v2) { return (*v1 + *v2).eval(); }); } static bool sub(zval *zv1, zval *zv2, zval *retval) { return arithmetic_op(zv1, zv2, retval, [](auto v1, auto v2) { return (*v1 - *v2).eval(); }); } static bool neg(zval *zv, zval *retval) { return arithmetic_op(zv, zv, retval, [](auto v1, auto v2) { return (-*v1).eval(); }); } 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).eval(); }); } static bool dotMul(zval *zv1, zval *zv2, zval *retval) { return arithmetic_op(zv1, zv2, retval, [](auto v1, auto v2) { return (*v1 % *v2).eval(); }); } static bool div(zval *zv1, zval *zv2, zval *retval) { return arithmetic_op(zv1, zv2, retval, [](auto v1, auto v2) { return (*v1 / *v2).eval(); }); } }; } #endif // !PHP_ARMA_BASE_HH