Implement diagview

This commit is contained in:
CismonX 2019-09-16 03:53:01 +08:00
parent 1e992fe67b
commit c95c44399a
14 changed files with 371 additions and 71 deletions

View File

@ -8,6 +8,7 @@
#include "dense.hh"
#include "mat.hh"
#include "subview_mat.hh"
#include "diagonal.hh"
#include "mapval.hh"
#include "stream_utils.hh"
@ -118,8 +119,8 @@ namespace php_arma
native->replace(zval_get_scalar<T>(old_value), zval_get_scalar<T>(new_value));
}
template <typename T, typename T1>
PHP_ARMA_METHOD(base, transform, T, T1)
template <typename T, typename ChildT>
PHP_ARMA_METHOD(base, transform, T, ChildT)
{
zend_fcall_info fci;
zend_fcall_info_cache fcc;
@ -127,38 +128,43 @@ namespace php_arma
Z_PARAM_FUNC(fci, fcc);
ZEND_PARSE_PARAMETERS_END();
zval params[1], retval;
fci.param_count = 1;
fci.params = params;
fci.retval = &retval;
if constexpr (util::is_any_v<ChildT, diagview<T>, spdiagview<T>>) {
throw_error("This method is not yet supported on diagview");
return;
} else {
zval params[1], retval;
fci.param_count = 1;
fci.params = params;
fci.retval = &retval;
if constexpr (std::is_same_v<T, cx_double>) {
// prevent segfault in `zval_set_scalar()`..
ZVAL_UNDEF(&params[0]);
}
auto native = THIS_NATIVE;
native->transform([&fci, &fcc](auto&& val) {
zval_set_scalar(&fci.params[0], val);
zend_call_function(&fci, &fcc);
if (!zval_check_scalar<T>(fci.retval)) {
zval_ptr_dtor(fci.retval);
return T();
}
T retval = zval_get_scalar<T>(fci.retval);
if constexpr (std::is_same_v<T, cx_double>) {
zval_ptr_dtor(fci.retval);
// prevent segfault in `zval_set_scalar()`..
ZVAL_UNDEF(&params[0]);
}
return retval;
});
if constexpr (std::is_same_v<T, cx_double>) {
zval_ptr_dtor(&params[0]);
auto native = THIS_NATIVE;
native->transform([&fci, &fcc](auto&& val) {
zval_set_scalar(&fci.params[0], val);
zend_call_function(&fci, &fcc);
if (!zval_check_scalar<T>(fci.retval)) {
zval_ptr_dtor(fci.retval);
return T();
}
T retval = zval_get_scalar<T>(fci.retval);
if constexpr (std::is_same_v<T, cx_double>) {
zval_ptr_dtor(fci.retval);
}
return retval;
});
if constexpr (std::is_same_v<T, cx_double>) {
zval_ptr_dtor(&params[0]);
}
}
}
template <typename T, typename T1>
PHP_ARMA_METHOD(base, forEach, T, T1)
template <typename T, typename ChildT>
PHP_ARMA_METHOD(base, forEach, T, ChildT)
{
zend_fcall_info fci;
zend_fcall_info_cache fcc;
@ -166,18 +172,23 @@ namespace php_arma
Z_PARAM_FUNC(fci, fcc);
ZEND_PARSE_PARAMETERS_END();
zval params[1], retval;
fci.param_count = 1;
fci.params = params;
fci.retval = &retval;
if constexpr (util::is_any_v<ChildT, diagview<T>, spdiagview<T>>) {
throw_error("This method is not yet supported on diagview");
return;
} else {
zval params[1], retval;
fci.param_count = 1;
fci.params = params;
fci.retval = &retval;
auto native = THIS_NATIVE;
native->for_each([&fci, &fcc](auto&& val) {
ZVAL_OBJ(&fci.params[0], mapval_dense<T>::create(&val));
zend_call_function(&fci, &fcc);
zval_ptr_dtor(&fci.params[0]);
zval_ptr_dtor(fci.retval);
});
auto native = THIS_NATIVE;
native->for_each([&fci, &fcc](auto&& val) {
ZVAL_OBJ(&fci.params[0], mapval_dense<T>::create(&val));
zend_call_function(&fci, &fcc);
zval_ptr_dtor(&fci.params[0]);
zval_ptr_dtor(fci.retval);
});
}
}
template <typename T, typename T1>
@ -336,4 +347,5 @@ namespace php_arma
PHP_ARMA_INSTANTIATE(base, mat);
PHP_ARMA_INSTANTIATE(base, subview_mat);
PHP_ARMA_INSTANTIATE(base, diagview);
}

View File

@ -8,6 +8,7 @@
#include "base.hh"
#include "mat.hh"
#include "subview_mat.hh"
#include "diagonal.hh"
#include <armadillo>
@ -95,8 +96,8 @@ namespace php_arma
native->fill(zval_get_scalar<T>(value));
}
template <typename T, typename T1>
PHP_ARMA_METHOD(dense, imbue, T, T1)
template <typename T, typename ChildT>
PHP_ARMA_METHOD(dense, imbue, T, ChildT)
{
zend_fcall_info fci;
zend_fcall_info_cache fcc;
@ -104,22 +105,27 @@ namespace php_arma
Z_PARAM_FUNC(fci, fcc);
ZEND_PARSE_PARAMETERS_END();
zval retval;
fci.retval = &retval;
if constexpr (std::is_same_v<ChildT, diagview<T>>) {
throw_error("This method is not yet supported on diagview");
return;
} else {
zval retval;
fci.retval = &retval;
auto native = THIS_NATIVE;
native->imbue([&fci, &fcc]() {
zend_call_function(&fci, &fcc);
if (!zval_check_scalar<T>(fci.retval)) {
zval_ptr_dtor(fci.retval);
return T();
}
T retval = zval_get_scalar<T>(fci.retval);
if constexpr (std::is_same_v<T, cx_double>) {
zval_ptr_dtor(fci.retval);
}
return retval;
});
auto native = THIS_NATIVE;
native->imbue([&fci, &fcc]() {
zend_call_function(&fci, &fcc);
if (!zval_check_scalar<T>(fci.retval)) {
zval_ptr_dtor(fci.retval);
return T();
}
T retval = zval_get_scalar<T>(fci.retval);
if constexpr (std::is_same_v<T, cx_double>) {
zval_ptr_dtor(fci.retval);
}
return retval;
});
}
}
template <typename T, typename T1>
@ -141,4 +147,5 @@ namespace php_arma
PHP_ARMA_INSTANTIATE(dense, mat);
PHP_ARMA_INSTANTIATE(dense, subview_mat);
PHP_ARMA_INSTANTIATE(dense, diagview);
}

View File

@ -10,6 +10,7 @@
#include "mapval.hh"
#include "mat.hh"
#include "subview_mat.hh"
#include "diagonal.hh"
#include <armadillo>
@ -129,7 +130,23 @@ namespace php_arma
template <typename T, typename T1>
PHP_ARMA_METHOD(dense_matrix, diag, T, T1)
{
zend_long k = 0;
ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(k)
ZEND_PARSE_PARAMETERS_END();
auto native = THIS_NATIVE;
zend_object *zobj;
try {
zobj = diagview<T>::create(std::move(native->diag(k)));
} catch (const std::logic_error& err) {
// requested diagonal out of bounds
throw_exception(err.what());
return;
}
RETVAL_OBJ(zobj);
}
template <typename T, typename T1>

66
src/diagonal.cc Normal file
View File

@ -0,0 +1,66 @@
//
// php-armadillo/diagonal.cc
//
// @Author CismonX
//
#include "diagonal.hh"
#include "base.hh"
#include "non_resizable.hh"
#include "subview.hh"
#include "dense.hh"
// #include "sparse.hh"
#include <armadillo>
namespace php_arma
{
template <typename T, bool B>
PHP_ARMA_START_ME(diagonal, T, B)
PHP_ARMA_ME_EX(linear, __invoke, ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER)
PHP_ARMA_ME_EX(linear, at, ZEND_ACC_PUBLIC)
PHP_ARMA_END_ME();
template <typename T, bool IsSparse>
void diagonal<T, IsSparse>::ce_init(zend_class_entry *parent_ce)
{
// TODO: edit this line when sparse is added.
using parent_t = std::conditional_t<IsSparse, dense<T, diagonal>, dense<T, diagonal>>;
ce = class_register<php_name::val>(parent_ce, fentry_list_concat(
PHP_ARMA_FENTRY((base <T, diagonal>::me)),
PHP_ARMA_FENTRY((parent_t ::me)),
PHP_ARMA_FENTRY((non_resizable<T, diagonal>::me)),
PHP_ARMA_FENTRY(me)
));
ce->create_object = object_non_constructible;
object_handlers_init(&handlers);
handlers.offset = sizeof(native_t);
handlers.clone_obj = nullptr;
handlers.count_elements = base<T, diagonal>::count_elements;
}
PHP_ARMA_NAME_DECLARE(diagview, "DDiagView", double);
PHP_ARMA_NAME_DECLARE(diagview, "IDiagView", zend_long);
PHP_ARMA_NAME_DECLARE(diagview, "CxDDiagView", cx_double);
PHP_ARMA_NAME_DECLARE(spdiagview, "SpDDiagView", double);
PHP_ARMA_NAME_DECLARE(spdiagview, "SpIDiagView", zend_long);
PHP_ARMA_NAME_DECLARE(spdiagview, "SpCxDDiagView", cx_double);
void diagonal_init()
{
diagonal_ce = interface_register<diagonal_php_name>(base_ce, non_resizable_ce, subview_ce);
diagview_ce = abstract_class_register<diagview_php_name>(diagonal_ce, dense_ce);
diagview<double >::ce_init(diagview_ce);
diagview<zend_long>::ce_init(diagview_ce);
diagview<cx_double>::ce_init(diagview_ce);
// spdiagview_ce = abstract_class_register<spdiagview_php_name>(diagonal_ce, sparse_ce);
// spdiagview<double >::ce_init(spdiagview_ce);
// spdiagview<zend_long>::ce_init(spdiagview_ce);
// spdiagview<cx_double>::ce_init(spdiagview_ce);
}
}

70
src/diagonal.hh Normal file
View File

@ -0,0 +1,70 @@
//
// php-armadillo/diagonal.hh
//
// @Author CismonX
//
#ifndef PHP_ARMA_DIAGONAL_HH
#define PHP_ARMA_DIAGONAL_HH
#include "php_arma.hh"
#include "complex.hh"
#include "shared_methods.hh"
namespace arma
{
template <typename T> class diagview;
template <typename T> class spdiagview;
template <typename T> class get_pod_type;
}
namespace php_arma
{
template <typename T> struct mat;
template <typename T, typename T1> struct linear;
template <typename T, bool IsSparse>
struct diagonal
{
using native_t = std::conditional_t<IsSparse, arma::spdiagview<T>, arma::diagview<T>>;
using orig_t = mat<T>;
using subview_t = mat<T>;
using with_int_elem_t = mat<zend_long>;
friend void diagonal_init();
template <typename... Ts>
zend_always_inline
static zend_object *create(Ts&&... args)
{
return object_create_ctor<native_t>(ce, &handlers, args...);
}
PHP_ARMA_CE_HANDLRES_DECLARE();
private:
PHP_ARMA_COMMON_DECLARE();
using linear = linear_element_acccess<T, diagonal>;
static void ce_init(zend_class_entry*);
};
template <typename T>
using diagview = diagonal<T, false>;
template <typename T>
using spdiagview = diagonal<T, true>;
void diagonal_init();
constexpr const char diagonal_php_name[] = "Diagonal";
constexpr const char diagview_php_name[] = "DiagView";
constexpr const char spdiagview_php_name[] = "SpDiagView";
inline zend_class_entry *diagonal_ce;
inline zend_class_entry *diagview_ce;
inline zend_class_entry *spdiagview_ce;
}
#endif // !PHP_ARMA_DIAGONAL_HH

View File

View File

View File

@ -6,6 +6,7 @@
#include "non_resizable.hh"
#include "subview_mat.hh"
#include "diagonal.hh"
#include <armadillo>
@ -30,4 +31,5 @@ namespace php_arma
}
PHP_ARMA_INSTANTIATE(non_resizable, subview_mat);
PHP_ARMA_INSTANTIATE(non_resizable, diagview);
}

View File

@ -22,6 +22,7 @@
#include "functions.hh"
#include "mat.hh"
#include "subview_mat.hh"
#include "diagonal.hh"
#ifdef PHP_ARMA_OPERATORS
#include "operators.hh"
@ -49,6 +50,7 @@ namespace php_arma
dense_non_resizable_matrix_init();
mat_init();
subview_mat_init();
diagonal_init();
#ifdef PHP_ARMA_OPERATORS
operators_init();

View File

@ -36,6 +36,9 @@
#define PHP_ARMA_ME(func, flags) \
ZEND_FENTRY(func, zif_##func, nullptr, flags)
#define PHP_ARMA_ME_EX(cls, func, flags) \
ZEND_FENTRY(func, cls::zif_##func, nullptr, flags)
#define PHP_ARMA_METHOD(cls, func, ...) \
void ZEND_FASTCALL cls<__VA_ARGS__>::zif_##func(INTERNAL_FUNCTION_PARAMETERS)
@ -88,7 +91,7 @@
#define PHP_ARMA_OPERATOR_EX2(base, child, func) \
if (instanceof_function(ce, child::ce)) { \
using base_t = arg_type<void(base)>::type; \
using base_t = util::arg_type<void(base)>::type; \
return base_t::operators::func(zv1, zv2, rv); \
}
@ -97,7 +100,7 @@
#define PHP_ARMA_ASSIGN_OPERATOR_EX2(base, child, func) \
if (instanceof_function(ce, child::ce)) { \
using base_t = arg_type<void(base)>::type; \
using base_t = util::arg_type<void(base)>::type; \
auto v = base_t::operators::func(zv1, zv2, zv1); \
if (rv) { \
ZVAL_COPY(rv, zv1); \
@ -127,12 +130,27 @@ namespace php_arma
{
extern zend_module_entry module_entry;
/// Utilities.
namespace util
{
/// Hack for extracting type from a parentheses-enclosed macro argument.
template <typename T>
struct arg_type;
template <typename T, typename U>
struct arg_type<T(U)>
{
using type = U;
};
template <typename T, typename... Ts>
constexpr auto is_any_v = std::disjunction_v<std::is_same<T, Ts>...>;
}
/// Helpers for compile-time string concatenation for better module startup performance.
namespace str
{
template <size_t... S>
using seq = std::integer_sequence<size_t, S...>;
template <size_t N>
using seq_impl = std::make_integer_sequence<size_t, N>;
@ -145,7 +163,6 @@ namespace php_arma
template <const char*, typename, const char*, typename>
struct concat_impl;
template <const char *Str1, size_t... Len1, const char *Str2, size_t... Len2>
struct concat_impl<Str1, seq<Len1...>, Str2, seq<Len2...>>
{
@ -177,17 +194,6 @@ namespace php_arma
};
}
/// Hack for extracting type from a parentheses-enclosed macro argument.
template <typename T>
struct arg_type;
template <typename T, typename U>
struct arg_type<T(U)>
{
using type = U;
};
/// Helper functions for throwing exceptions when errors occur.
zend_always_inline

52
src/shared_methods.cc Normal file
View File

@ -0,0 +1,52 @@
//
// php-armadillo/shared_methods.cc
//
// @Author CismonX
//
#include "shared_methods.hh"
#include "mapval.hh"
#include "diagonal.hh"
#include <armadillo>
namespace php_arma
{
template <typename T, typename ChildT>
PHP_ARMA_METHOD(linear_element_acccess, __invoke, T, ChildT)
{
zend_long i;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_LONG(i)
ZEND_PARSE_PARAMETERS_END();
auto native = THIS_NATIVE;
zend_object *zobj;
try {
zobj = mapval_dense<T>::create(&native->operator()(i));
} catch (const std::logic_error& err) {
// Index out of bounds.
throw_exception(err.what());
return;
}
RETVAL_OBJ(zobj);
}
template <typename T, typename ChildT>
PHP_ARMA_METHOD(linear_element_acccess, at, T, ChildT)
{
zend_long i;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_LONG(i)
ZEND_PARSE_PARAMETERS_END();
auto native = THIS_NATIVE;
auto zobj = mapval_dense<T>::create(&native->operator[](i));
RETVAL_OBJ(zobj);
}
PHP_ARMA_INSTANTIATE(linear_element_acccess, diagview);
}

24
src/shared_methods.hh Normal file
View File

@ -0,0 +1,24 @@
//
// php-armadillo/shared_methods.hh
//
// @Author CismonX
//
#ifndef PHP_ARMA_SHARED_METHODS_HH
#define PHP_ARMA_SHARED_METHODS_HH
#include "php_arma.hh"
namespace php_arma
{
template <typename T, typename ChildT>
struct linear_element_acccess
{
using native_t = typename ChildT::native_t;
static PHP_FUNCTION(__invoke);
static PHP_FUNCTION(at);
};
}
#endif // !PHP_ARMA_SHARED_METHODS_HH

View File

@ -9,5 +9,21 @@ namespace Arma\Internal;
*/
interface Diagonal extends Base, NonResizable, Subview
{
/**
* Read/write access to the n-th element.
*
* @param int $idx
* @return Scalar
*/
function __invoke($idx);
/**
* Read/write access to the n-th element.
*
* Without a bounds check. Not recommended for use unless your code has been thoroughly debugged.
*
* @param int $idx
* @return Scalar
*/
function at($idx);
}

26
tests/018-diagview.phpt Normal file
View File

@ -0,0 +1,26 @@
--TEST--
Test for class `DiagView`.
--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");
$diag = $mat->diag();
$diag->rawPrint();
$diag->at(1)->setTo($diag->at(0)->val());
$mat->rawPrint();
?>
--EXPECT--
1
4
1 2
3 1