// // php-armadillo/complex.hh // // @Author CismonX // #ifndef PHP_ARMA_COMPLEX_HH #define PHP_ARMA_COMPLEX_HH #include "common/php_arma.hh" #include #ifdef PHP_ARMA_OPERATORS #if PHP_VERSION_ID < 70300 // Before PHP 7.3, `-$a` is compile to `MUL -1, $a, ~` instead of `MUL $a, -1, ~`. #define CONVERT_NEG_1_TO_COMPLEX \ else if (Z_TYPE_P(zv1) == IS_LONG && Z_LVAL_P(zv1) == -1) { \ std::swap(zv1, zv2); \ } #else #define CONVERT_NEG_1_TO_COMPLEX #endif // For non-assignment operators, first operand can be of type double. #define PHP_ARMA_COMPLEX_OPERATOR_EX(type, func) \ if (instanceof_function(ce, complex::ce)) { \ if (zval_is_scalar(zv1)) { \ std::swap(zv1, zv2); \ } \ CONVERT_NEG_1_TO_COMPLEX \ else if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { \ return false; \ } \ ZVAL_UNDEF(rv); \ return complex::operators::func(zv1, zv2, rv); \ } #define PHP_ARMA_COMPLEX_OPERATOR(func) \ PHP_ARMA_OPERATOR_BEGIN(complex_ce) \ PHP_ARMA_COMPLEX_OPERATOR_EX(double, func) \ PHP_ARMA_OPERATOR_END() #define PHP_ARMA_COMPLEX_ASSIGN_OPERATOR_EX(type, func) \ PHP_ARMA_ASSIGN_OPERATOR_EX(complex, func) #define PHP_ARMA_COMPLEX_ASSIGN_OPERATOR(func) \ PHP_ARMA_OPERATOR_BEGIN(complex_ce) \ PHP_ARMA_COMPLEX_ASSIGN_OPERATOR_EX(double, func) \ PHP_ARMA_OPERATOR_END() #endif // PHP_ARMA_OPERATORS using cx_double = std::complex; namespace php_arma { template struct complex { using native_t = std::complex; struct operators; friend void complex_init(); PHP_ARMA_CE_HANDLRES_DECLARE(); private: PHP_ARMA_COMMON_DECLARE(); static PHP_FUNCTION(__construct); static PHP_FUNCTION(fromPolar); static PHP_FUNCTION(add); static PHP_FUNCTION(sub); static PHP_FUNCTION(neg); static PHP_FUNCTION(mul); static PHP_FUNCTION(div); static PHP_FUNCTION(abs); static PHP_FUNCTION(arg); static PHP_FUNCTION(norm); static PHP_FUNCTION(conj); static PHP_FUNCTION(exp); static PHP_FUNCTION(log); static PHP_FUNCTION(log10); static PHP_FUNCTION(pow); static PHP_FUNCTION(sqrt); static PHP_FUNCTION(sin); static PHP_FUNCTION(cos); static PHP_FUNCTION(tan); static PHP_FUNCTION(asin); static PHP_FUNCTION(acos); static PHP_FUNCTION(atan); static PHP_FUNCTION(sinh); static PHP_FUNCTION(cosh); static PHP_FUNCTION(tanh); static PHP_FUNCTION(asinh); static PHP_FUNCTION(acosh); static PHP_FUNCTION(atanh); static PHP_FUNCTION(__toString); static zval *read_dimension(zval*, zval*, int, zval*); static zobj_write_prop_ret_t write_property(zval*, zval*, zval*, void**); static int compare_objects(zval*, zval*); static void ce_init(zend_class_entry*); }; void complex_init(); constexpr const char complex_php_name[] = "Complex"; constexpr const char real_property_name[] = "real"; constexpr const char imag_property_name[] = "imag"; inline zend_class_entry *complex_ce; /// Helper functions for handling cx_double types. template <> zend_always_inline bool zval_is_scalar(zval *zv) { return Z_TYPE_P(zv) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zv), PHP_ARMA_CE(complex, double)); } template <> zend_always_inline const char *scalar_type_name() { return ZSTR_VAL(PHP_ARMA_CE(complex, double)->name); } template <> zend_always_inline cx_double zval_get_scalar(zval *zv) { auto zobj = Z_OBJ_P(zv); return { Z_DVAL_P(OBJ_PROP_NUM(zobj, 0)), Z_DVAL_P(OBJ_PROP_NUM(zobj, 1)) }; } zend_always_inline void zval_set_scalar(zval *zv, const cx_double& val) { auto zobj = object_create(PHP_ARMA_CE(complex, double), PHP_ARMA_HANDLERS(complex, double)); ZVAL_DOUBLE(OBJ_PROP_NUM(zobj, 0), val.real()); ZVAL_DOUBLE(OBJ_PROP_NUM(zobj, 1), val.imag()); zval_ptr_dtor(zv); ZVAL_OBJ(zv, zobj); } template zend_always_inline void ex_bad_type(zval *zv) { auto real_type_name = scalar_type_name(); auto complex_type_name = scalar_type_name::native_t>(); auto expected = (char*)malloc((strlen(real_type_name) + strlen(complex_type_name) + 5) * sizeof(char)); sprintf(expected, "%s or %s", real_type_name, complex_type_name); auto got = Z_TYPE_P(zv) == IS_OBJECT ? Z_OBJNAME_P(zv) : zend_get_type_by_const(Z_TYPE_P(zv)); ex_bad_type(expected, got); free(expected); } template struct complex::operators { zend_always_inline static bool add(zval *zv1, zval *zv2, zval *retval) { auto v1 = zval_get_scalar(zv1); if (zval_is_scalar(zv2)) { zval_set_scalar(retval, v1 + zval_get_scalar(zv2)); return true; } if (zval_is_scalar(zv2)) { zval_set_scalar(retval, v1 + zval_get_scalar(zv2)); return true; } return false; } zend_always_inline static bool sub(zval *zv1, zval *zv2, zval *retval) { auto v1 = zval_get_scalar(zv1); if (zval_is_scalar(zv2)) { zval_set_scalar(retval, v1 - zval_get_scalar(zv2)); return true; } if (zval_is_scalar(zv2)) { zval_set_scalar(retval, v1 - zval_get_scalar(zv2)); return true; } return false; } zend_always_inline static void neg(zval *zv, zval *retval) { auto v = zval_get_scalar(zv); zval_set_scalar(retval, -v); } zend_always_inline static bool mul(zval *zv1, zval *zv2, zval *retval) { auto v1 = zval_get_scalar(zv1); if (zval_is_scalar(zv2)) { zval_set_scalar(retval, v1 * zval_get_scalar(zv2)); return true; } if (zval_is_scalar(zv2)) { zval_set_scalar(retval, v1 * zval_get_scalar(zv2)); return true; } if (Z_TYPE_P(zv2) == IS_LONG && Z_LVAL_P(zv2) == -1) { // Negation operator is compiled as multiplication to -1. neg(zv1, retval); return true; } return false; } zend_always_inline static bool div(zval *zv1, zval *zv2, zval *retval) { auto v1 = zval_get_scalar(zv1); if (zval_is_scalar(zv2)) { zval_set_scalar(retval, v1 / zval_get_scalar(zv2)); return true; } if (zval_is_scalar(zv2)) { zval_set_scalar(retval, v1 / zval_get_scalar(zv2)); return true; } return false; } zend_always_inline static bool pow(zval *zv1, zval *zv2, zval *retval) { auto v1 = zval_get_scalar(zv1); if (zval_is_scalar(zv2)) { zval_set_scalar(retval, std::pow(v1, zval_get_scalar(zv2))); return true; } if (zval_is_scalar(zv2)) { zval_set_scalar(retval, std::pow(v1, zval_get_scalar(zv2))); return true; } return false; } zend_always_inline static bool conj(zval *zv, zval *unused, zval *retval) { zval_set_scalar(retval, std::conj(zval_get_scalar(zv))); return true; } }; } #endif // !PHP_ARMA_COMPLEX_HH