/*
 * Copyright (c) 1997-1999 Massachusetts Institute of Technology
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/* This file was automatically generated --- DO NOT EDIT */
/* Generated on Sun Nov  7 20:44:44 EST 1999 */

#include <fftw-int.h>
#include <fftw.h>

/* Generated by: ./genfft -magic-alignment-check -magic-twiddle-load-all -magic-variables 4 -magic-loopi -hc2hc-forward 5 */

/*
 * This function contains 64 FP additions, 40 FP multiplications,
 * (or, 44 additions, 20 multiplications, 20 fused multiply/add),
 * 27 stack variables, and 40 memory accesses
 */
static const fftw_real K250000000 = FFTW_KONST(+0.250000000000000000000000000000000000000000000);
static const fftw_real K559016994 = FFTW_KONST(+0.559016994374947424102293417182819058860154590);
static const fftw_real K587785252 = FFTW_KONST(+0.587785252292473129168705954639072768597652438);
static const fftw_real K951056516 = FFTW_KONST(+0.951056516295153572116439333379382143405698634);

/*
 * Generator Id's :
 * $Id: exprdag.ml,v 1.41 1999/05/26 15:44:14 fftw Exp $
 * $Id: fft.ml,v 1.43 1999/05/17 19:44:18 fftw Exp $
 * $Id: to_c.ml,v 1.25 1999/10/26 21:41:32 stevenj Exp $
 */

void fftw_hc2hc_forward_5(fftw_real *A, const fftw_complex *W, int iostride, int m, int dist)
{
     int i;
     fftw_real *X;
     fftw_real *Y;
     X = A;
     Y = A + (5 * iostride);
     {
	  fftw_real tmp70;
	  fftw_real tmp67;
	  fftw_real tmp68;
	  fftw_real tmp63;
	  fftw_real tmp71;
	  fftw_real tmp66;
	  fftw_real tmp69;
	  fftw_real tmp72;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp70 = X[0];
	  {
	       fftw_real tmp61;
	       fftw_real tmp62;
	       fftw_real tmp64;
	       fftw_real tmp65;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp61 = X[4 * iostride];
	       tmp62 = X[iostride];
	       tmp67 = tmp62 + tmp61;
	       tmp64 = X[2 * iostride];
	       tmp65 = X[3 * iostride];
	       tmp68 = tmp64 + tmp65;
	       tmp63 = tmp61 - tmp62;
	       tmp71 = tmp67 + tmp68;
	       tmp66 = tmp64 - tmp65;
	  }
	  Y[-iostride] = (K951056516 * tmp63) - (K587785252 * tmp66);
	  Y[-2 * iostride] = (K587785252 * tmp63) + (K951056516 * tmp66);
	  X[0] = tmp70 + tmp71;
	  tmp69 = K559016994 * (tmp67 - tmp68);
	  tmp72 = tmp70 - (K250000000 * tmp71);
	  X[iostride] = tmp69 + tmp72;
	  X[2 * iostride] = tmp72 - tmp69;
     }
     X = X + dist;
     Y = Y - dist;
     for (i = 2; i < m; i = i + 2, X = X + dist, Y = Y - dist, W = W + 4) {
	  fftw_real tmp13;
	  fftw_real tmp52;
	  fftw_real tmp42;
	  fftw_real tmp45;
	  fftw_real tmp49;
	  fftw_real tmp50;
	  fftw_real tmp51;
	  fftw_real tmp54;
	  fftw_real tmp53;
	  fftw_real tmp24;
	  fftw_real tmp35;
	  fftw_real tmp36;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp13 = X[0];
	  tmp52 = Y[-4 * iostride];
	  {
	       fftw_real tmp18;
	       fftw_real tmp40;
	       fftw_real tmp34;
	       fftw_real tmp44;
	       fftw_real tmp23;
	       fftw_real tmp41;
	       fftw_real tmp29;
	       fftw_real tmp43;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp15;
		    fftw_real tmp17;
		    fftw_real tmp14;
		    fftw_real tmp16;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp15 = X[iostride];
		    tmp17 = Y[-3 * iostride];
		    tmp14 = c_re(W[0]);
		    tmp16 = c_im(W[0]);
		    tmp18 = (tmp14 * tmp15) - (tmp16 * tmp17);
		    tmp40 = (tmp16 * tmp15) + (tmp14 * tmp17);
	       }
	       {
		    fftw_real tmp31;
		    fftw_real tmp33;
		    fftw_real tmp30;
		    fftw_real tmp32;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp31 = X[3 * iostride];
		    tmp33 = Y[-iostride];
		    tmp30 = c_re(W[2]);
		    tmp32 = c_im(W[2]);
		    tmp34 = (tmp30 * tmp31) - (tmp32 * tmp33);
		    tmp44 = (tmp32 * tmp31) + (tmp30 * tmp33);
	       }
	       {
		    fftw_real tmp20;
		    fftw_real tmp22;
		    fftw_real tmp19;
		    fftw_real tmp21;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp20 = X[4 * iostride];
		    tmp22 = Y[0];
		    tmp19 = c_re(W[3]);
		    tmp21 = c_im(W[3]);
		    tmp23 = (tmp19 * tmp20) - (tmp21 * tmp22);
		    tmp41 = (tmp21 * tmp20) + (tmp19 * tmp22);
	       }
	       {
		    fftw_real tmp26;
		    fftw_real tmp28;
		    fftw_real tmp25;
		    fftw_real tmp27;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp26 = X[2 * iostride];
		    tmp28 = Y[-2 * iostride];
		    tmp25 = c_re(W[1]);
		    tmp27 = c_im(W[1]);
		    tmp29 = (tmp25 * tmp26) - (tmp27 * tmp28);
		    tmp43 = (tmp27 * tmp26) + (tmp25 * tmp28);
	       }
	       tmp42 = tmp40 - tmp41;
	       tmp45 = tmp43 - tmp44;
	       tmp49 = tmp40 + tmp41;
	       tmp50 = tmp43 + tmp44;
	       tmp51 = tmp49 + tmp50;
	       tmp54 = tmp29 - tmp34;
	       tmp53 = tmp18 - tmp23;
	       tmp24 = tmp18 + tmp23;
	       tmp35 = tmp29 + tmp34;
	       tmp36 = tmp24 + tmp35;
	  }
	  X[0] = tmp13 + tmp36;
	  {
	       fftw_real tmp46;
	       fftw_real tmp48;
	       fftw_real tmp39;
	       fftw_real tmp47;
	       fftw_real tmp37;
	       fftw_real tmp38;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp46 = (K951056516 * tmp42) + (K587785252 * tmp45);
	       tmp48 = (K951056516 * tmp45) - (K587785252 * tmp42);
	       tmp37 = K559016994 * (tmp24 - tmp35);
	       tmp38 = tmp13 - (K250000000 * tmp36);
	       tmp39 = tmp37 + tmp38;
	       tmp47 = tmp38 - tmp37;
	       Y[-4 * iostride] = tmp39 - tmp46;
	       X[iostride] = tmp39 + tmp46;
	       X[2 * iostride] = tmp47 - tmp48;
	       Y[-3 * iostride] = tmp47 + tmp48;
	  }
	  Y[0] = tmp51 + tmp52;
	  {
	       fftw_real tmp55;
	       fftw_real tmp60;
	       fftw_real tmp58;
	       fftw_real tmp59;
	       fftw_real tmp56;
	       fftw_real tmp57;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp55 = (K951056516 * tmp53) + (K587785252 * tmp54);
	       tmp60 = (K951056516 * tmp54) - (K587785252 * tmp53);
	       tmp56 = K559016994 * (tmp49 - tmp50);
	       tmp57 = tmp52 - (K250000000 * tmp51);
	       tmp58 = tmp56 + tmp57;
	       tmp59 = tmp57 - tmp56;
	       X[4 * iostride] = -(tmp55 + tmp58);
	       Y[-iostride] = tmp58 - tmp55;
	       X[3 * iostride] = -(tmp59 - tmp60);
	       Y[-2 * iostride] = tmp60 + tmp59;
	  }
     }
     if (i == m) {
	  fftw_real tmp8;
	  fftw_real tmp3;
	  fftw_real tmp6;
	  fftw_real tmp9;
	  fftw_real tmp12;
	  fftw_real tmp11;
	  fftw_real tmp7;
	  fftw_real tmp10;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp8 = X[0];
	  {
	       fftw_real tmp1;
	       fftw_real tmp2;
	       fftw_real tmp4;
	       fftw_real tmp5;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp1 = X[2 * iostride];
	       tmp2 = X[3 * iostride];
	       tmp3 = tmp1 - tmp2;
	       tmp4 = X[4 * iostride];
	       tmp5 = X[iostride];
	       tmp6 = tmp4 - tmp5;
	       tmp9 = tmp3 + tmp6;
	       tmp12 = tmp4 + tmp5;
	       tmp11 = tmp1 + tmp2;
	  }
	  X[2 * iostride] = tmp8 + tmp9;
	  tmp7 = K559016994 * (tmp3 - tmp6);
	  tmp10 = tmp8 - (K250000000 * tmp9);
	  X[0] = tmp7 + tmp10;
	  X[iostride] = tmp10 - tmp7;
	  Y[0] = -((K951056516 * tmp11) + (K587785252 * tmp12));
	  Y[-iostride] = -((K951056516 * tmp12) - (K587785252 * tmp11));
     }
}

static const int twiddle_order[] =
{1, 2, 3, 4};
fftw_codelet_desc fftw_hc2hc_forward_5_desc =
{
     "fftw_hc2hc_forward_5",
     (void (*)()) fftw_hc2hc_forward_5,
     5,
     FFTW_FORWARD,
     FFTW_HC2HC,
     113,
     4,
     twiddle_order,
};


//! \file heat_driver.h
//! Driver for Magnolia's heat transfer solver
#ifndef ENRICO_SURROGATE_HEAT_DRIVER_H
#define ENRICO_SURROGATE_HEAT_DRIVER_H

#include "driver.h"

#include "pugixml.hpp"
#include "xtensor/xtensor.hpp"
#include <gsl/gsl>
#include <mpi.h>

#include <cstddef>

namespace enrico {

class SurrogateHeatDriver : public Driver {
public:
  //! Initializes heat-fluids surrogate with the given MPI communicator.
  //!
  //! \param comm  The MPI communicator used to initialze the surrogate
  //! \param node  XML node containing settings for surrogate
  explicit SurrogateHeatDriver(MPI_Comm comm, pugi::xml_node node);

  //! Solves the heat-fluids surrogate solver
  void solve_step() final;

  //! Number of rings in fuel and clad
  //! \return Number of rings
  std::size_t n_rings() { return n_fuel_rings_ + n_clad_rings_; }

  //! Write data to VTK
  void write_step(int timestep, int iteration) final;

  xt::xtensor<double, 1> temperature() const;

  double temperature(int pin, int axial, int ring) const;

  // Data on fuel pins
  xt::xtensor<double, 2> pin_centers_; //!< (x,y) values for center of fuel pins
  xt::xtensor<double, 1> z_;           //!< Bounding z-values for axial segments
  std::size_t n_pins_;                 //!< number of fuel pins
  std::size_t n_axial_;                //!< number of axial segments

  // Dimensions for a single fuel pin axial segment
  double clad_outer_radius_;     //!< clad outer radius in [cm]
  double clad_inner_radius_;     //!< clad inner radius in [cm]
  double pellet_radius_;         //!< fuel pellet radius in [cm]
  std::size_t n_fuel_rings_{20}; //!< number of fuel rings
  std::size_t n_clad_rings_{2};  //!< number of clad rings

  // solver variables and settings
  double tol_;                    //!< tolerance on convergence
  xt::xtensor<double, 3> source_; //!< heat source for each (axial segment, ring)
  xt::xtensor<double, 1> r_grid_clad_; //!< radii of each clad ring in [cm]
  xt::xtensor<double, 1> r_grid_fuel_; //!< radii of each fuel ring in [cm]

  // visualization
  std::string viz_basename_{
    "heat_surrogate"}; //!< base filename for visualization files (default: magnolia)
  std::string viz_iterations_{
    "none"};                    //!< visualization iterations to write (none, all, final)
  std::string viz_data_{"all"}; //!< visualization data to write
  std::string viz_regions_{"all"}; //!< visualization regions to write
  size_t vtk_radial_res_{20};      //!< radial resolution of resulting vtk files

private:
  //! Create internal arrays used for heat equation solver
  void generate_arrays();

  //!< temperature in [K] for each (axial segment, ring)
  xt::xtensor<double, 3> temperature_;

}; // end SurrogateHeatDriver

} // namespace enrico

#endif //ENRICO_SURROGATE_HEAT_DRIVER_H


#include <php.h>
#include <Zend/zend_interfaces.h>
#include <Zend/zend_exceptions.h>
#include <ext/spl/spl_iterators.h>
#include <ext/spl/spl_exceptions.h>
#include <cblas.h>
#include <Interop/Polite/Math/Matrix.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php_rindow_openblas.h"

static zend_object_handlers rindow_openblas_blas_object_handlers;

// destractor
static void php_rindow_openblas_blas_free_object(zend_object* object)
{
    zend_object_std_dtor(object);
}

// constructor
static zend_object* php_rindow_openblas_blas_create_object(zend_class_entry* class_type) /* {{{ */
{
    zend_object* intern = NULL;

    intern = (zend_object*)ecalloc(1, sizeof(zend_object) + zend_object_properties_size(class_type));

    zend_object_std_init(intern, class_type);
    object_properties_init(intern, class_type);

    intern->handlers = &rindow_openblas_blas_object_handlers;

    return intern;
} /* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function getNumThreads() : int
 {{{ */
static PHP_METHOD(Blas, getNumThreads)
{
    int n;
    n = openblas_get_num_threads();
    RETURN_LONG(n);
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function getNumProcs() : int
 {{{ */
static PHP_METHOD(Blas, getNumProcs)
{
    int n;
    n = openblas_get_num_procs();
    RETURN_LONG(n);
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function getConfig() : string
 {{{ */
static PHP_METHOD(Blas, getConfig)
{
    char *s;
    s = openblas_get_config();
    RETURN_STRING(s);
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function getCorename() : string
 {{{ */
static PHP_METHOD(Blas, getCorename)
{
    char *s;
    s = openblas_get_corename();
    RETURN_STRING(s);
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function scal(
        int $n,
        float $alpha,
        Buffer $X, int $offsetX, int $incX) : void
 {{{ */
static PHP_METHOD(Blas, scal)
{
    php_interop_polite_math_matrix_linear_buffer_t* buffer;
    zend_long n;
    double alpha;
    zval* x=NULL;
    zend_long offsetX;
    zend_long incX;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 5, 5)
        Z_PARAM_LONG(n)
        Z_PARAM_DOUBLE(alpha)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
    ZEND_PARSE_PARAMETERS_END();

    buffer = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(buffer,"x")) {
        return;
    }
    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, buffer,n,offsetX,incX)) {
        return;
    }
    switch (buffer->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_sscal((blasint)n, (float)alpha, &(((float *)buffer->data)[offsetX]), (blasint)incX);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_dscal((blasint)n, (double)alpha, &(((double *)buffer->data)[offsetX]), (blasint)incX);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function axpy(
        int $n,
        float $alpha,
        Buffer $X, int $offsetX, int $incX,
        Buffer $Y, int $offsetY, int $incY ) : void
 {{{ */
static PHP_METHOD(Blas, axpy)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferX;
    php_interop_polite_math_matrix_linear_buffer_t* bufferY;
    zend_long n;
    double alpha;
    zval* x=NULL;
    zend_long offsetX;
    zend_long incX;
    zval* y=NULL;
    zend_long offsetY;
    zend_long incY;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 8, 8)
        Z_PARAM_LONG(n)
        Z_PARAM_DOUBLE(alpha)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
        Z_PARAM_OBJECT(y) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetY)
        Z_PARAM_LONG(incY)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    // Check Buffer X
    bufferX = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(bufferX,"x")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, bufferX,n,offsetX,incX)) {
        return;
    }

    // Check Buffer Y
    bufferY = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(y);
    if(php_rindow_openblas_assert_buffer_type(bufferY,"y")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_Y, bufferY,n,offsetY,incY)) {
        return;
    }

    // Check Buffer X and Y
    if(bufferX->dtype!=bufferY->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for X and Y", 0);
        return;
    }

    switch (bufferX->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_saxpy((blasint)n, (float)alpha,
                &(((float *)bufferX->data)[offsetX]), (blasint)incX,
                &(((float *)bufferY->data)[offsetY]), (blasint)incY);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_daxpy((blasint)n, (double)alpha,
                &(((double *)bufferX->data)[offsetX]), (blasint)incX,
                &(((double *)bufferY->data)[offsetY]), (blasint)incY);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function dot(
        int $n,
        Buffer $X, int $offsetX, int $incX,
        Buffer $Y, int $offsetY, int $incY ) : float
 {{{ */
static PHP_METHOD(Blas, dot)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferX;
    php_interop_polite_math_matrix_linear_buffer_t* bufferY;
    zend_long n;
    zval* x=NULL;
    zend_long offsetX;
    zend_long incX;
    zval* y=NULL;
    zend_long offsetY;
    zend_long incY;
    double result;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 7, 7)
        Z_PARAM_LONG(n)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
        Z_PARAM_OBJECT(y) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetY)
        Z_PARAM_LONG(incY)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    // Check Buffer X
    bufferX = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(bufferX,"x")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, bufferX,n,offsetX,incX)) {
        return;
    }

    // Check Buffer Y
    bufferY = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(y);
    if(php_rindow_openblas_assert_buffer_type(bufferY,"y")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_Y, bufferY,n,offsetY,incY)) {
        return;
    }

    // Check Buffer X and Y
    if(bufferX->dtype!=bufferY->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for X and Y", 0);
        return;
    }

    switch (bufferX->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            result = (double)cblas_sdot((blasint)n,
                &(((float *)bufferX->data)[offsetX]), (blasint)incX,
                &(((float *)bufferY->data)[offsetY]), (blasint)incY);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            result = (double)cblas_ddot((blasint)n,
                &(((double *)bufferX->data)[offsetX]), (blasint)incX,
                &(((double *)bufferY->data)[offsetY]), (blasint)incY);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
    RETURN_DOUBLE(result);
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function asum(
        int $n,
        Buffer $X, int $offsetX, int $incX ) : float
 {{{ */
static PHP_METHOD(Blas, asum)
{
    php_interop_polite_math_matrix_linear_buffer_t* buffer;
    zend_long n;
    zval* x=NULL;
    zend_long offsetX;
    zend_long incX;
    double result;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 4, 4)
        Z_PARAM_LONG(n)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    buffer = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(buffer,"x")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, buffer,n,offsetX,incX)) {
        return;
    }
    switch (buffer->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            result = (double)cblas_sasum((blasint)n, &(((float *)buffer->data)[offsetX]), (blasint)incX);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            result = (double)cblas_dasum((blasint)n, &(((double *)buffer->data)[offsetX]), (blasint)incX);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
    RETURN_DOUBLE(result);
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function iamax(
        int $n,
        Buffer $X, int $offsetX, int $incX ) : int
 {{{ */
static PHP_METHOD(Blas, iamax)
{
    php_interop_polite_math_matrix_linear_buffer_t* buffer;
    zend_long n;
    zval* x=NULL;
    zend_long offsetX;
    zend_long incX;
    zend_long resultIdx;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 4, 4)
        Z_PARAM_LONG(n)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    buffer = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(buffer,"x")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, buffer,n,offsetX,incX)) {
        return;
    }
    switch (buffer->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            resultIdx = (zend_long)cblas_isamax((blasint)n, &(((float *)buffer->data)[offsetX]), (blasint)incX);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            resultIdx = (zend_long)cblas_idamax((blasint)n, &(((double *)buffer->data)[offsetX]), (blasint)incX);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
    RETURN_LONG(resultIdx);
}
/* }}} */

#ifdef OPENBLAS_HAVE_IAMIN
/* Method Rindow\OpenBLAS\Blas::
    public function iamin(
        int $n,
        Buffer $X, int $offsetX, int $incX ) : int
 {{{ */
static PHP_METHOD(Blas, iamin)
{
    php_interop_polite_math_matrix_linear_buffer_t* buffer;
    zend_long n;
    zval* x=NULL;
    zend_long offsetX;
    zend_long incX;
    zend_long resultIdx;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 4, 4)
        Z_PARAM_LONG(n)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    buffer = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(buffer,"x")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, buffer,n,offsetX,incX)) {
        return;
    }
    switch (buffer->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            resultIdx = (zend_long)cblas_isamin((blasint)n, &(((float *)buffer->data)[offsetX]), (blasint)incX);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            resultIdx = (zend_long)cblas_idamin((blasint)n, &(((double *)buffer->data)[offsetX]), (blasint)incX);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
    RETURN_LONG(resultIdx);
}
/* }}} */
#endif

/* Method Rindow\OpenBLAS\Blas::
    public function copy(
        int $n,
        Buffer $X, int $offsetX, int $incX,
        Buffer $Y, int $offsetY, int $incY ) : void
 {{{ */
static PHP_METHOD(Blas, copy)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferX;
    php_interop_polite_math_matrix_linear_buffer_t* bufferY;
    zend_long n;
    zval* x=NULL;
    zend_long offsetX;
    zend_long incX;
    zval* y=NULL;
    zend_long offsetY;
    zend_long incY;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 7, 7)
        Z_PARAM_LONG(n)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
        Z_PARAM_OBJECT(y) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetY)
        Z_PARAM_LONG(incY)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    // Check Buffer X
    bufferX = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(bufferX,"x")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, bufferX,n,offsetX,incX)) {
        return;
    }

    // Check Buffer Y
    bufferY = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(y);
    if(php_rindow_openblas_assert_buffer_type(bufferY,"y")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_Y, bufferY,n,offsetY,incY)) {
        return;
    }

    // Check Buffer X and Y
    if(bufferX->dtype!=bufferY->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for X and Y", 0);
        return;
    }

    switch (bufferX->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_scopy((blasint)n,
                &(((float *)bufferX->data)[offsetX]), (blasint)incX,
                &(((float *)bufferY->data)[offsetY]), (blasint)incY);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_dcopy((blasint)n,
                &(((double *)bufferX->data)[offsetX]), (blasint)incX,
                &(((double *)bufferY->data)[offsetY]), (blasint)incY);
            break;
        default:
            {
                zend_long i,idX,idY;
                int valueSize;
                uint8_t *x,*y;
                valueSize = php_rindow_openblas_common_dtype_to_valuesize(bufferX->dtype);
                x = php_rindow_openblas_get_address(bufferX,offsetX,valueSize);
                y = php_rindow_openblas_get_address(bufferY,offsetY,valueSize);
                if(incX==1 && incY==1) {
                    memcpy(y,x,valueSize*n);
                } else {
                    for(i=0,idX=0,idY=0; i<n; i++,idX+=incX,idY+=incY) {
                        memcpy(&y[valueSize*idY],&x[valueSize*idX],valueSize);
                    }
                }
            }
            break;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function nrm2(
        int $n,
        Buffer $X, int $offsetX, int $incX ) : float
 {{{ */
static PHP_METHOD(Blas, nrm2)
{
    php_interop_polite_math_matrix_linear_buffer_t* buffer;
    zend_long n;
    zval* x=NULL;
    zend_long offsetX;
    zend_long incX;
    double result;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 4, 4)
        Z_PARAM_LONG(n)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    buffer = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(buffer,"x")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, buffer,n,offsetX,incX)) {
        return;
    }
    switch (buffer->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            result = (double)cblas_snrm2((blasint)n, &(((float *)buffer->data)[offsetX]), (blasint)incX);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            result = (double)cblas_dnrm2((blasint)n, &(((double *)buffer->data)[offsetX]), (blasint)incX);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
    RETURN_DOUBLE(result);
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function rotg(
        Buffer $A, int $offsetA,
        Buffer $B, int $offsetB,
        Buffer $C, int $offsetC,
        Buffer $S, int $offsetS,
    ) : void
 {{{ */
static PHP_METHOD(Blas, rotg)
{
    zval* a;
    zend_long offsetA;
    zval* b;
    zend_long offsetB;
    zval* c;
    zend_long offsetC;
    zval* s;
    zend_long offsetS;
    php_interop_polite_math_matrix_linear_buffer_t* bufferA;
    php_interop_polite_math_matrix_linear_buffer_t* bufferB;
    php_interop_polite_math_matrix_linear_buffer_t* bufferC;
    php_interop_polite_math_matrix_linear_buffer_t* bufferS;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 8, 8)
        Z_PARAM_OBJECT(a) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetA)
        Z_PARAM_OBJECT(b) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetB)
        Z_PARAM_OBJECT(c) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetC)
        Z_PARAM_OBJECT(s) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetS)
    ZEND_PARSE_PARAMETERS_END();

    // Check Buffer A
    bufferA = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(a);
    if(php_rindow_openblas_assert_buffer_type(bufferA,"a")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, bufferA,1,offsetA,1)) {
        return;
    }

    // Check Buffer B
    bufferB = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(b);
    if(php_rindow_openblas_assert_buffer_type(bufferB,"b")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_Y, bufferB,1,offsetB,1)) {
        return;
    }

    // Check Buffer C
    bufferC = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(c);
    if(php_rindow_openblas_assert_buffer_type(bufferC,"c")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, bufferC,1,offsetC,1)) {
        return;
    }

    // Check Buffer S
    bufferS = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(s);
    if(php_rindow_openblas_assert_buffer_type(bufferS,"s")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_Y, bufferS,1,offsetS,1)) {
        return;
    }

    // Check Buffer A and B and C and S
    if(bufferA->dtype!=bufferB->dtype || bufferB->dtype!=bufferC->dtype ||
        bufferC->dtype!=bufferS->dtype || bufferS->dtype!=bufferA->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for A,B,C and S", 0);
        return;
    }

    switch (bufferA->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_srotg(
                &(((float *)bufferA->data)[offsetA]),
                &(((float *)bufferB->data)[offsetB]),
                &(((float *)bufferC->data)[offsetC]),
                &(((float *)bufferS->data)[offsetS])
            );
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_drotg(
                &(((double *)bufferA->data)[offsetA]),
                &(((double *)bufferB->data)[offsetB]),
                &(((double *)bufferC->data)[offsetC]),
                &(((double *)bufferS->data)[offsetS])
            );
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function rot(
        int $n,
        Buffer $X, int $offsetX, int $incX,
        Buffer $Y, int $offsetY, int $incY,
        Buffer $C, int $offsetC,
        Buffer $S, int $offsetS,
    ) : void
 {{{ */
static PHP_METHOD(Blas, rot)
{
    zend_long n;
    zval* x;
    zend_long offsetX;
    zend_long incX;
    zval* y;
    zend_long offsetY;
    zend_long incY;
    zval* c;
    zend_long offsetC;
    zval* s;
    zend_long offsetS;
    php_interop_polite_math_matrix_linear_buffer_t* bufferX;
    php_interop_polite_math_matrix_linear_buffer_t* bufferY;
    php_interop_polite_math_matrix_linear_buffer_t* bufferC;
    php_interop_polite_math_matrix_linear_buffer_t* bufferS;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 11, 11)
        Z_PARAM_LONG(n)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
        Z_PARAM_OBJECT(y) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetY)
        Z_PARAM_LONG(incY)
        Z_PARAM_OBJECT(c) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetC)
        Z_PARAM_OBJECT(s) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetS)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    // Check Buffer X
    bufferX = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(bufferX,"x")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, bufferX,n,offsetX,incX)) {
        return;
    }

    // Check Buffer Y
    bufferY = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(y);
    if(php_rindow_openblas_assert_buffer_type(bufferY,"y")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_Y, bufferY,n,offsetY,incY)) {
        return;
    }

    // Check Buffer C
    bufferC = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(c);
    if(php_rindow_openblas_assert_buffer_type(bufferC,"c")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, bufferC,1,offsetC,1)) {
        return;
    }

    // Check Buffer S
    bufferS = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(s);
    if(php_rindow_openblas_assert_buffer_type(bufferS,"s")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_Y, bufferS,1,offsetS,1)) {
        return;
    }

    // Check Buffer X and Y
    if(bufferX->dtype!=bufferY->dtype||bufferY->dtype!=bufferC->dtype||
        bufferC->dtype!=bufferS->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for X,Y,C and S", 0);
        return;
    }

    switch (bufferX->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_srot((blasint)n,
                &(((float *)bufferX->data)[offsetX]), (blasint)incX,
                &(((float *)bufferY->data)[offsetY]), (blasint)incY,
                ((float *)bufferC->data)[offsetC],
                ((float *)bufferS->data)[offsetS]
            );
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_drot((blasint)n,
                &(((double *)bufferX->data)[offsetX]), (blasint)incX,
                &(((double *)bufferY->data)[offsetY]), (blasint)incY,
                ((double *)bufferC->data)[offsetC],
                ((double *)bufferS->data)[offsetS]
            );
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function swap(
        int $n,
        Buffer $X, int $offsetX, int $incX,
        Buffer $Y, int $offsetY, int $incY ) : void
 {{{ */
static PHP_METHOD(Blas, swap)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferX;
    php_interop_polite_math_matrix_linear_buffer_t* bufferY;
    zend_long n;
    zval* x=NULL;
    zend_long offsetX;
    zend_long incX;
    zval* y=NULL;
    zend_long offsetY;
    zend_long incY;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 7, 7)
        Z_PARAM_LONG(n)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
        Z_PARAM_OBJECT(y) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetY)
        Z_PARAM_LONG(incY)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    // Check Buffer X
    bufferX = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(bufferX,"x")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_X, bufferX,n,offsetX,incX)) {
        return;
    }

    // Check Buffer Y
    bufferY = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(y);
    if(php_rindow_openblas_assert_buffer_type(bufferY,"y")) {
        return;
    }
    if(php_rindow_openblas_assert_vector_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_Y, bufferY,n,offsetY,incY)) {
        return;
    }

    // Check Buffer X and Y
    if(bufferX->dtype!=bufferY->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for X and Y", 0);
        return;
    }

    switch (bufferX->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_sswap((blasint)n,
                &(((float *)bufferX->data)[offsetX]), (blasint)incX,
                &(((float *)bufferY->data)[offsetY]), (blasint)incY);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_dswap((blasint)n,
                &(((double *)bufferX->data)[offsetX]), (blasint)incX,
                &(((double *)bufferY->data)[offsetY]), (blasint)incY);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function gemv(
        int $order,
        int $trans,
        int $m,
        int $n,
        float $alpha,
        Buffer $A, int $offsetA, int $ldA,
        Buffer $X, int $offsetX, int $incX,
        float $beta,
        Buffer $Y, int $offsetY, int $incY ) : void
 {{{ */
static PHP_METHOD(Blas, gemv)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferA;
    php_interop_polite_math_matrix_linear_buffer_t* bufferX;
    php_interop_polite_math_matrix_linear_buffer_t* bufferY;
    zend_long order;
    zend_long trans;
    zend_long m;
    zend_long n;
    double alpha;
    zval* a=NULL;
    zend_long offsetA;
    zend_long ldA;
    zval* x=NULL;
    zend_long offsetX;
    zend_long incX;
    double beta;
    zval* y=NULL;
    zend_long offsetY;
    zend_long incY;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 15, 15)
        Z_PARAM_LONG(order)
        Z_PARAM_LONG(trans)
        Z_PARAM_LONG(m)
        Z_PARAM_LONG(n)
        Z_PARAM_DOUBLE(alpha)
        Z_PARAM_OBJECT(a) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetA)
        Z_PARAM_LONG(ldA)
        Z_PARAM_OBJECT(x) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetX)
        Z_PARAM_LONG(incX)
        Z_PARAM_DOUBLE(beta)
        Z_PARAM_OBJECT(y) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetY)
        Z_PARAM_LONG(incY)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_M, m)) {
        return;
    }
    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    // Check Buffer A
    bufferA = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(a);
    if(php_rindow_openblas_assert_buffer_type(bufferA,"a")) {
        return;
    }
    if(php_rindow_openblas_assert_matrix_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_A, bufferA,m,n,offsetA,ldA)) {
        return;
    }

    // Check Buffer X
    bufferX = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(x);
    if(php_rindow_openblas_assert_buffer_type(bufferX,"x")) {
        return;
    }

    // Check Buffer Y
    bufferY = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(y);
    if(php_rindow_openblas_assert_buffer_type(bufferY,"y")) {
        return;
    }

    // Check Buffer size X and Y
    {
        zend_long rows,cols;
        if(trans==CblasNoTrans || trans==CblasConjNoTrans ) {
            rows = m; cols = n;
        } else if(trans==CblasTrans || trans==CblasConjTrans) {
            rows = n; cols = m;
        } else {
            zend_throw_exception(spl_ce_RuntimeException, "unknown transpose mode for bufferA.", 0);
            return;
        }
        if(php_rindow_openblas_assert_vector_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_X, bufferX,cols,offsetX,incX)) {
            return;
        }
        if(php_rindow_openblas_assert_vector_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_Y, bufferY,rows,offsetY,incY)) {
            return;
        }
    }

    // Check Buffer A and X and Y
    if(bufferA->dtype!=bufferX->dtype || bufferX->dtype!=bufferY->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for A and X and Y", 0);
        return;
    }

    switch (bufferX->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_sgemv(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)trans,
                (blasint)m,(blasint)n,
                (float)alpha,
                &(((float *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((float *)bufferX->data)[offsetX]), (blasint)incX,
                (float)beta,
                &(((float *)bufferY->data)[offsetY]), (blasint)incY);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_dgemv(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)trans,
                (blasint)m,(blasint)n,
                (double)alpha,
                &(((double *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((double *)bufferX->data)[offsetX]), (blasint)incX,
                (double)beta,
                &(((double *)bufferY->data)[offsetY]), (blasint)incY);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function gemm(
        int $order,
        int $transA,
        int $transB,
        int $m,
        int $n,
        int $k,
        float $alpha,
        Buffer $A, int $offsetA, int $ldA,
        Buffer $B, int $offsetB, int $ldB,
        float $beta,
        Buffer $C, int $offsetC, int $ldC ) : void
 {{{ */
static PHP_METHOD(Blas, gemm)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferA;
    php_interop_polite_math_matrix_linear_buffer_t* bufferB;
    php_interop_polite_math_matrix_linear_buffer_t* bufferC;
    zend_long order;
    zend_long transA;
    zend_long transB;
    zend_long m;
    zend_long n;
    zend_long k;
    double alpha;
    zval* a=NULL;
    zend_long offsetA;
    zend_long ldA;
    zval* b=NULL;
    zend_long offsetB;
    zend_long ldB;
    double beta;
    zval* c=NULL;
    zend_long offsetC;
    zend_long ldC;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 17, 17)
        Z_PARAM_LONG(order)
        Z_PARAM_LONG(transA)
        Z_PARAM_LONG(transB)
        Z_PARAM_LONG(m)
        Z_PARAM_LONG(n)
        Z_PARAM_LONG(k)
        Z_PARAM_DOUBLE(alpha)
        Z_PARAM_OBJECT(a) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetA)
        Z_PARAM_LONG(ldA)
        Z_PARAM_OBJECT(b) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetB)
        Z_PARAM_LONG(ldB)
        Z_PARAM_DOUBLE(beta)
        Z_PARAM_OBJECT(c) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetC)
        Z_PARAM_LONG(ldC)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_M, m)) {
        return;
    }
    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_K, k)) {
        return;
    }
    // Check Buffer A
    bufferA = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(a);
    if(php_rindow_openblas_assert_buffer_type(bufferA,"a")) {
        return;
    }
    {
        zend_long rows,cols;
        if(transA==CblasNoTrans || transA==CblasConjNoTrans) {
            rows = m; cols = k;
        } else if(transA==CblasTrans || transA==CblasConjTrans) {
            rows = k; cols = m;
        } else {
            zend_throw_exception(spl_ce_RuntimeException, "unknown transpose mode for bufferA.", 0);
            return;
        }
        if(php_rindow_openblas_assert_matrix_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_A, bufferA,rows,cols,offsetA,ldA)) {
            return;
        }
    }

    // Check Buffer B
    bufferB = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(b);
    if(php_rindow_openblas_assert_buffer_type(bufferB,"b")) {
        return;
    }
    {
        zend_long rows,cols;
        if(transB==CblasNoTrans || transB==CblasConjNoTrans) {
            rows = k; cols = n;
        } else if(transB==CblasTrans || transB==CblasConjTrans) {
            rows = n; cols = k;
        } else {
            zend_throw_exception(spl_ce_RuntimeException, "unknown transpose mode for bufferB.", 0);
            return;
        }
        if(php_rindow_openblas_assert_matrix_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_B, bufferB,rows,cols,offsetB,ldB)) {
            return;
        }
    }

    // Check Buffer C
    bufferC = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(c);
    if(php_rindow_openblas_assert_buffer_type(bufferC,"c")) {
        return;
    }
    if(php_rindow_openblas_assert_matrix_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_C, bufferC,m,n,offsetC,ldC)) {
        return;
    }

    // Check Buffer A and B and C
    if(bufferA->dtype!=bufferB->dtype || bufferB->dtype!=bufferC->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for A and B and C", 0);
        return;
    }

    switch (bufferA->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_sgemm(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)transA,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)transB,
                (blasint)m,(blasint)n,(blasint)k,
                (float)alpha,
                &(((float *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((float *)bufferB->data)[offsetB]), (blasint)ldB,
                (float)beta,
                &(((float *)bufferC->data)[offsetC]), (blasint)ldC);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_dgemm(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)transA,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)transB,
                (blasint)m,(blasint)n,(blasint)k,
                (double)alpha,
                &(((double *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((double *)bufferB->data)[offsetB]), (blasint)ldB,
                (double)beta,
                &(((double *)bufferC->data)[offsetC]), (blasint)ldC);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function symm(
        int $order,
        int $side,
        int $uplo,
        int $m,
        int $n,
        float $alpha,
        Buffer $A, int $offsetA, int $ldA,
        Buffer $B, int $offsetB, int $ldB,
        float $beta,
        Buffer $C, int $offsetC, int $ldC ) : void
 {{{ */
static PHP_METHOD(Blas, symm)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferA;
    php_interop_polite_math_matrix_linear_buffer_t* bufferB;
    php_interop_polite_math_matrix_linear_buffer_t* bufferC;
    zend_long order;
    zend_long side;
    zend_long uplo;
    zend_long m;
    zend_long n;
    double alpha;
    zval* a=NULL;
    zend_long offsetA;
    zend_long ldA;
    zval* b=NULL;
    zend_long offsetB;
    zend_long ldB;
    double beta;
    zval* c=NULL;
    zend_long offsetC;
    zend_long ldC;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 16, 16)
        Z_PARAM_LONG(order)
        Z_PARAM_LONG(side)
        Z_PARAM_LONG(uplo)
        Z_PARAM_LONG(m)
        Z_PARAM_LONG(n)
        Z_PARAM_DOUBLE(alpha)
        Z_PARAM_OBJECT(a) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetA)
        Z_PARAM_LONG(ldA)
        Z_PARAM_OBJECT(b) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetB)
        Z_PARAM_LONG(ldB)
        Z_PARAM_DOUBLE(beta)
        Z_PARAM_OBJECT(c) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetC)
        Z_PARAM_LONG(ldC)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_M, m)) {
        return;
    }
    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    // Check Buffer A
    bufferA = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(a);
    if(php_rindow_openblas_assert_buffer_type(bufferA,"a")) {
        return;
    }
    if(!php_interop_polite_math_matrix_is_linear_buffer(bufferA)) {
        zend_throw_exception(zend_ce_type_error, "invalid type.", 0);
        return;
    }
    {
        zend_long rows;
        if(side==CblasLeft) {
            rows = m;
        } else if(side==CblasRight) {
            rows = n;
        } else {
            zend_throw_exception(spl_ce_RuntimeException, "unknown side mode for bufferA.", 0);
            return;
        }
        if(php_rindow_openblas_assert_matrix_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_A, bufferA,rows,rows,offsetA,ldA)) {
            return;
        }
    }

    // Check Buffer B
    bufferB = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(b);
    if(php_rindow_openblas_assert_buffer_type(bufferB,"b")) {
        return;
    }
    {
        if(php_rindow_openblas_assert_matrix_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_B, bufferB,m,n,offsetB,ldB)) {
            return;
        }
    }

    // Check Buffer C
    bufferC = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(c);
    if(php_rindow_openblas_assert_buffer_type(bufferC,"c")) {
        return;
    }
    if(php_rindow_openblas_assert_matrix_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_C, bufferC,m,n,offsetC,ldC)) {
        return;
    }

    // Check Buffer A and B and C
    if(bufferA->dtype!=bufferB->dtype || bufferB->dtype!=bufferC->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for A and B and C", 0);
        return;
    }

    switch (bufferA->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_ssymm(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_SIDE)side,
                (OPENBLAS_CONST enum CBLAS_UPLO)uplo,
                (blasint)m,(blasint)n,
                (float)alpha,
                &(((float *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((float *)bufferB->data)[offsetB]), (blasint)ldB,
                (float)beta,
                &(((float *)bufferC->data)[offsetC]), (blasint)ldC);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_dsymm(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_SIDE)side,
                (OPENBLAS_CONST enum CBLAS_UPLO)uplo,
                (blasint)m,(blasint)n,
                (double)alpha,
                &(((double *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((double *)bufferB->data)[offsetB]), (blasint)ldB,
                (double)beta,
                &(((double *)bufferC->data)[offsetC]), (blasint)ldC);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function syrk(
        int $order,
        int $uplo,
        int $trans,
        int $n,
        int $k,
        float $alpha,
        Buffer $A, int $offsetA, int $ldA,
        float $beta,
        Buffer $C, int $offsetC, int $ldC ) : void
 {{{ */
static PHP_METHOD(Blas, syrk)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferA;
    php_interop_polite_math_matrix_linear_buffer_t* bufferC;
    zend_long order;
    zend_long uplo;
    zend_long trans;
    zend_long n;
    zend_long k;
    double alpha;
    zval* a=NULL;
    zend_long offsetA;
    zend_long ldA;
    double beta;
    zval* c=NULL;
    zend_long offsetC;
    zend_long ldC;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 13, 13)
        Z_PARAM_LONG(order)
        Z_PARAM_LONG(uplo)
        Z_PARAM_LONG(trans)
        Z_PARAM_LONG(n)
        Z_PARAM_LONG(k)
        Z_PARAM_DOUBLE(alpha)
        Z_PARAM_OBJECT(a) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetA)
        Z_PARAM_LONG(ldA)
        Z_PARAM_DOUBLE(beta)
        Z_PARAM_OBJECT(c) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetC)
        Z_PARAM_LONG(ldC)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_K, k)) {
        return;
    }
    // Check Buffer A
    bufferA = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(a);
    if(php_rindow_openblas_assert_buffer_type(bufferA,"a")) {
        return;
    }
    {
        zend_long rows,cols;
        if(trans==CblasNoTrans || trans==CblasConjNoTrans) {
            rows = n; cols = k;
        } else if(trans==CblasTrans || trans==CblasConjTrans) {
            rows = k; cols = n;
        } else {
            zend_throw_exception(spl_ce_RuntimeException, "unknown transpose mode for bufferA.", 0);
            return;
        }
        if(php_rindow_openblas_assert_matrix_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_A, bufferA,rows,cols,offsetA,ldA)) {
            return;
        }
    }

    // Check Buffer C
    bufferC = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(c);
    if(php_rindow_openblas_assert_buffer_type(bufferC,"c")) {
        return;
    }
    if(php_rindow_openblas_assert_matrix_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_C, bufferC,n,n,offsetC,ldC)) {
        return;
    }

    // Check Buffer A and C
    if(bufferA->dtype!=bufferC->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for A and C", 0);
        return;
    }

    switch (bufferA->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_ssyrk(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_UPLO)uplo,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)trans,
                (blasint)n,(blasint)k,
                (float)alpha,
                &(((float *)bufferA->data)[offsetA]), (blasint)ldA,
                (float)beta,
                &(((float *)bufferC->data)[offsetC]), (blasint)ldC);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_dsyrk(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_UPLO)uplo,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)trans,
                (blasint)n,(blasint)k,
                (double)alpha,
                &(((double *)bufferA->data)[offsetA]), (blasint)ldA,
                (double)beta,
                &(((double *)bufferC->data)[offsetC]), (blasint)ldC);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function syr2k(
        int $order,
        int $uplo,
        int $trans,
        int $n,
        int $k,
        float $alpha,
        Buffer $A, int $offsetA, int $ldA,
        Buffer $B, int $offsetB, int $ldB,
        float $beta,
        Buffer $C, int $offsetC, int $ldC ) : void
 {{{ */
static PHP_METHOD(Blas, syr2k)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferA;
    php_interop_polite_math_matrix_linear_buffer_t* bufferB;
    php_interop_polite_math_matrix_linear_buffer_t* bufferC;
    zend_long order;
    zend_long uplo;
    zend_long trans;
    zend_long n;
    zend_long k;
    double alpha;
    zval* a=NULL;
    zend_long offsetA;
    zend_long ldA;
    zval* b=NULL;
    zend_long offsetB;
    zend_long ldB;
    double beta;
    zval* c=NULL;
    zend_long offsetC;
    zend_long ldC;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 16, 16)
        Z_PARAM_LONG(order)
        Z_PARAM_LONG(uplo)
        Z_PARAM_LONG(trans)
        Z_PARAM_LONG(n)
        Z_PARAM_LONG(k)
        Z_PARAM_DOUBLE(alpha)
        Z_PARAM_OBJECT(a) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetA)
        Z_PARAM_LONG(ldA)
        Z_PARAM_OBJECT(b) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetB)
        Z_PARAM_LONG(ldB)
        Z_PARAM_DOUBLE(beta)
        Z_PARAM_OBJECT(c) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetC)
        Z_PARAM_LONG(ldC)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_M, k)) {
        return;
    }
    // Check Buffer A and B
    bufferA = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(a);
    if(php_rindow_openblas_assert_buffer_type(bufferA,"a")) {
        return;
    }
    bufferB = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(b);
    if(php_rindow_openblas_assert_buffer_type(bufferB,"b")) {
        return;
    }
    {
        zend_long rows,cols;
        if(trans==CblasNoTrans || trans==CblasConjNoTrans) {
            rows = n; cols = k;
        } else if(trans==CblasTrans || trans==CblasConjTrans) {
            rows = k; cols = n;
        } else {
            zend_throw_exception(spl_ce_RuntimeException, "unknown transpose mode for bufferA.", 0);
            return;
        }
        if(php_rindow_openblas_assert_matrix_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_A, bufferA,rows,cols,offsetA,ldA)) {
            return;
        }
        if(php_rindow_openblas_assert_matrix_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_B, bufferB,rows,cols,offsetB,ldB)) {
            return;
        }
    }

    // Check Buffer C
    bufferC = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(c);
    if(php_rindow_openblas_assert_buffer_type(bufferC,"c")) {
        return;
    }
    if(php_rindow_openblas_assert_matrix_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_C, bufferC,n,n,offsetC,ldC)) {
        return;
    }

    // Check Buffer A and B and C
    if(bufferA->dtype!=bufferB->dtype || bufferB->dtype!=bufferC->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for A and B and C", 0);
        return;
    }

    switch (bufferA->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_ssyr2k(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_UPLO)uplo,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)trans,
                (blasint)n,(blasint)k,
                (float)alpha,
                &(((float *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((float *)bufferB->data)[offsetB]), (blasint)ldB,
                (float)beta,
                &(((float *)bufferC->data)[offsetC]), (blasint)ldC);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_dsyr2k(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_UPLO)uplo,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)trans,
                (blasint)n,(blasint)k,
                (double)alpha,
                &(((double *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((double *)bufferB->data)[offsetB]), (blasint)ldB,
                (double)beta,
                &(((double *)bufferC->data)[offsetC]), (blasint)ldC);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function trmm(
        int $order,
        int $side,
        int $uplo,
        int $trans,
        int $diag,
        int $m,
        int $n,
        float $alpha,
        Buffer $A, int $offsetA, int $ldA,
        Buffer $B, int $offsetB, int $ldB) : void
 {{{ */
static PHP_METHOD(Blas, trmm)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferA;
    php_interop_polite_math_matrix_linear_buffer_t* bufferB;
    zend_long order;
    zend_long side;
    zend_long uplo;
    zend_long trans;
    zend_long diag;
    zend_long m;
    zend_long n;
    double alpha;
    zval* a=NULL;
    zend_long offsetA;
    zend_long ldA;
    zval* b=NULL;
    zend_long offsetB;
    zend_long ldB;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 14, 14)
        Z_PARAM_LONG(order)
        Z_PARAM_LONG(side)
        Z_PARAM_LONG(uplo)
        Z_PARAM_LONG(trans)
        Z_PARAM_LONG(diag)
        Z_PARAM_LONG(m)
        Z_PARAM_LONG(n)
        Z_PARAM_DOUBLE(alpha)
        Z_PARAM_OBJECT(a) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetA)
        Z_PARAM_LONG(ldA)
        Z_PARAM_OBJECT(b) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetB)
        Z_PARAM_LONG(ldB)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_M, m)) {
        return;
    }
    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    // Check Buffer A
    bufferA = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(a);
    if(php_rindow_openblas_assert_buffer_type(bufferA,"a")) {
        return;
    }
    {
        zend_long sizeA;
        if(side==CblasLeft) {
            sizeA = m;
        } else if(side==CblasRight) {
            sizeA = n;
        } else {
            zend_throw_exception(spl_ce_RuntimeException, "unknown transpose mode for bufferA.", 0);
            return;
        }
        if(php_rindow_openblas_assert_matrix_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_A, bufferA,sizeA,sizeA,offsetA,ldA)) {
            return;
        }
    }
    bufferB = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(b);
    if(php_rindow_openblas_assert_buffer_type(bufferB,"b")) {
        return;
    }
    if(php_rindow_openblas_assert_matrix_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_B, bufferB,m,n,offsetB,ldB)) {
        return;
    }

    // Check Buffer A and B
    if(bufferA->dtype!=bufferB->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for A and B", 0);
        return;
    }

    switch (bufferA->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_strmm(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_SIDE)side,
                (OPENBLAS_CONST enum CBLAS_UPLO)uplo,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)trans,
                (OPENBLAS_CONST enum CBLAS_DIAG)diag,
                (blasint)m,(blasint)n,
                (float)alpha,
                &(((float *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((float *)bufferB->data)[offsetB]), (blasint)ldB);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_dtrmm(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_SIDE)side,
                (OPENBLAS_CONST enum CBLAS_UPLO)uplo,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)trans,
                (OPENBLAS_CONST enum CBLAS_DIAG)diag,
                (blasint)m,(blasint)n,
                (double)alpha,
                &(((double *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((double *)bufferB->data)[offsetB]), (blasint)ldB);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

/* Method Rindow\OpenBLAS\Blas::
    public function trsm(
        int $order,
        int $side,
        int $uplo,
        int $trans,
        int $diag,
        int $m,
        int $n,
        float $alpha,
        Buffer $A, int $offsetA, int $ldA,
        Buffer $B, int $offsetB, int $ldB) : void
 {{{ */
static PHP_METHOD(Blas, trsm)
{
    php_interop_polite_math_matrix_linear_buffer_t* bufferA;
    php_interop_polite_math_matrix_linear_buffer_t* bufferB;
    zend_long order;
    zend_long side;
    zend_long uplo;
    zend_long trans;
    zend_long diag;
    zend_long m;
    zend_long n;
    double alpha;
    zval* a=NULL;
    zend_long offsetA;
    zend_long ldA;
    zval* b=NULL;
    zend_long offsetB;
    zend_long ldB;

    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 14, 14)
        Z_PARAM_LONG(order)
        Z_PARAM_LONG(side)
        Z_PARAM_LONG(uplo)
        Z_PARAM_LONG(trans)
        Z_PARAM_LONG(diag)
        Z_PARAM_LONG(m)
        Z_PARAM_LONG(n)
        Z_PARAM_DOUBLE(alpha)
        Z_PARAM_OBJECT(a) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetA)
        Z_PARAM_LONG(ldA)
        Z_PARAM_OBJECT(b) // Interop\Polite\Math\Matrix\LinearBuffer
        Z_PARAM_LONG(offsetB)
        Z_PARAM_LONG(ldB)
    ZEND_PARSE_PARAMETERS_END();

    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_M, m)) {
        return;
    }
    if(php_rindow_openblas_assert_shape_parameter(
        PHP_RINDOW_OPENBLAS_ASSERT_N, n)) {
        return;
    }
    // Check Buffer A
    bufferA = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(a);
    if(php_rindow_openblas_assert_buffer_type(bufferA,"a")) {
        return;
    }
    {
        zend_long sizeA;
        if(side==CblasLeft) {
            sizeA = m;
        } else if(side==CblasRight) {
            sizeA = n;
        } else {
            zend_throw_exception(spl_ce_RuntimeException, "unknown transpose mode for bufferA.", 0);
            return;
        }
        if(php_rindow_openblas_assert_matrix_buffer_spec(
            PHP_RINDOW_OPENBLAS_ASSERT_A, bufferA,sizeA,sizeA,offsetA,ldA)) {
            return;
        }
    }
    bufferB = Z_INTEROP_POLITE_MATH_MATRIX_LINEAR_BUFFER_OBJ_P(b);
    if(php_rindow_openblas_assert_buffer_type(bufferB,"b")) {
        return;
    }
    if(php_rindow_openblas_assert_matrix_buffer_spec(
        PHP_RINDOW_OPENBLAS_ASSERT_B, bufferB,m,n,offsetB,ldB)) {
        return;
    }

    // Check Buffer A and B
    if(bufferA->dtype!=bufferB->dtype) {
        zend_throw_exception(spl_ce_InvalidArgumentException, "Unmatch data type for A and B", 0);
        return;
    }

    switch (bufferA->dtype) {
        case php_interop_polite_math_matrix_dtype_float32:
            cblas_strsm(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_SIDE)side,
                (OPENBLAS_CONST enum CBLAS_UPLO)uplo,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)trans,
                (OPENBLAS_CONST enum CBLAS_DIAG)diag,
                (blasint)m,(blasint)n,
                (float)alpha,
                &(((float *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((float *)bufferB->data)[offsetB]), (blasint)ldB);
            break;
        case php_interop_polite_math_matrix_dtype_float64:
            cblas_dtrsm(
                (OPENBLAS_CONST enum CBLAS_ORDER)order,
                (OPENBLAS_CONST enum CBLAS_SIDE)side,
                (OPENBLAS_CONST enum CBLAS_UPLO)uplo,
                (OPENBLAS_CONST enum CBLAS_TRANSPOSE)trans,
                (OPENBLAS_CONST enum CBLAS_DIAG)diag,
                (blasint)m,(blasint)n,
                (double)alpha,
                &(((double *)bufferA->data)[offsetA]), (blasint)ldA,
                &(((double *)bufferB->data)[offsetB]), (blasint)ldB);
            break;
        default:
            zend_throw_exception(spl_ce_RuntimeException, "Unsupported data type.", 0);
            return;
    }
}
/* }}} */

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_scal, 0, 0, 5)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_INFO(0, alpha)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_axpy, 0, 0, 8)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_INFO(0, alpha)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
    ZEND_ARG_OBJ_INFO(0, y, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetY)
    ZEND_ARG_INFO(0, incY)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_dot, 0, 0, 7)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
    ZEND_ARG_OBJ_INFO(0, y, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetY)
    ZEND_ARG_INFO(0, incY)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_asum, 0, 0, 4)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_iamax, 0, 0, 4)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
ZEND_END_ARG_INFO()

#ifdef OPENBLAS_HAVE_IAMIN
ZEND_BEGIN_ARG_INFO_EX(ai_Blas_iamin, 0, 0, 4)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
ZEND_END_ARG_INFO()
#endif

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_copy, 0, 0, 7)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
    ZEND_ARG_OBJ_INFO(0, y, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetY)
    ZEND_ARG_INFO(0, incY)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_nrm2, 0, 0, 4)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_rotg, 0, 0, 8)
    ZEND_ARG_OBJ_INFO(0, a, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetA)
    ZEND_ARG_OBJ_INFO(0, b, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetB)
    ZEND_ARG_OBJ_INFO(0, c, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetC)
    ZEND_ARG_OBJ_INFO(0, s, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetS)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_rot, 0, 0, 11)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
    ZEND_ARG_OBJ_INFO(0, y, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetY)
    ZEND_ARG_INFO(0, incY)
    ZEND_ARG_OBJ_INFO(0, c, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetC)
    ZEND_ARG_OBJ_INFO(0, s, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetS)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_swap, 0, 0, 7)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
    ZEND_ARG_OBJ_INFO(0, y, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetY)
    ZEND_ARG_INFO(0, incY)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_gemv, 0, 0, 15)
    ZEND_ARG_INFO(0, order)
    ZEND_ARG_INFO(0, trans)
    ZEND_ARG_INFO(0, m)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_INFO(0, alpha)
    ZEND_ARG_OBJ_INFO(0, a, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetA)
    ZEND_ARG_INFO(0, ldA)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetX)
    ZEND_ARG_INFO(0, incX)
    ZEND_ARG_INFO(0, beta)
    ZEND_ARG_OBJ_INFO(0, x, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetY)
    ZEND_ARG_INFO(0, incY)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_gemm, 0, 0, 17)
    ZEND_ARG_INFO(0, order)
    ZEND_ARG_INFO(0, transA)
    ZEND_ARG_INFO(0, transB)
    ZEND_ARG_INFO(0, m)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_INFO(0, k)
    ZEND_ARG_INFO(0, alpha)
    ZEND_ARG_OBJ_INFO(0, a, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetA)
    ZEND_ARG_INFO(0, ldA)
    ZEND_ARG_OBJ_INFO(0, b, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetB)
    ZEND_ARG_INFO(0, ldB)
    ZEND_ARG_INFO(0, beta)
    ZEND_ARG_OBJ_INFO(0, c, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetC)
    ZEND_ARG_INFO(0, ldC)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_symm, 0, 0, 16)
    ZEND_ARG_INFO(0, order)
    ZEND_ARG_INFO(0, side)
    ZEND_ARG_INFO(0, uplo)
    ZEND_ARG_INFO(0, m)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_INFO(0, alpha)
    ZEND_ARG_OBJ_INFO(0, a, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetA)
    ZEND_ARG_INFO(0, ldA)
    ZEND_ARG_OBJ_INFO(0, b, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetB)
    ZEND_ARG_INFO(0, ldB)
    ZEND_ARG_INFO(0, beta)
    ZEND_ARG_OBJ_INFO(0, c, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetC)
    ZEND_ARG_INFO(0, ldC)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_syrk, 0, 0, 13)
    ZEND_ARG_INFO(0, order)
    ZEND_ARG_INFO(0, uplo)
    ZEND_ARG_INFO(0, trans)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_INFO(0, k)
    ZEND_ARG_INFO(0, alpha)
    ZEND_ARG_OBJ_INFO(0, a, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetA)
    ZEND_ARG_INFO(0, ldA)
    ZEND_ARG_INFO(0, beta)
    ZEND_ARG_OBJ_INFO(0, c, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetC)
    ZEND_ARG_INFO(0, ldC)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_syr2k, 0, 0, 16)
    ZEND_ARG_INFO(0, order)
    ZEND_ARG_INFO(0, uplo)
    ZEND_ARG_INFO(0, trans)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_INFO(0, k)
    ZEND_ARG_INFO(0, alpha)
    ZEND_ARG_OBJ_INFO(0, a, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetA)
    ZEND_ARG_INFO(0, ldA)
    ZEND_ARG_OBJ_INFO(0, b, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetB)
    ZEND_ARG_INFO(0, ldB)
    ZEND_ARG_INFO(0, beta)
    ZEND_ARG_OBJ_INFO(0, c, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetC)
    ZEND_ARG_INFO(0, ldC)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_trmm, 0, 0, 14)
    ZEND_ARG_INFO(0, order)
    ZEND_ARG_INFO(0, side)
    ZEND_ARG_INFO(0, uplo)
    ZEND_ARG_INFO(0, trans)
    ZEND_ARG_INFO(0, diag)
    ZEND_ARG_INFO(0, m)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_INFO(0, alpha)
    ZEND_ARG_OBJ_INFO(0, a, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetA)
    ZEND_ARG_INFO(0, ldA)
    ZEND_ARG_OBJ_INFO(0, b, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetB)
    ZEND_ARG_INFO(0, ldB)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_trsm, 0, 0, 14)
    ZEND_ARG_INFO(0, order)
    ZEND_ARG_INFO(0, side)
    ZEND_ARG_INFO(0, uplo)
    ZEND_ARG_INFO(0, trans)
    ZEND_ARG_INFO(0, diag)
    ZEND_ARG_INFO(0, m)
    ZEND_ARG_INFO(0, n)
    ZEND_ARG_INFO(0, alpha)
    ZEND_ARG_OBJ_INFO(0, a, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetA)
    ZEND_ARG_INFO(0, ldA)
    ZEND_ARG_OBJ_INFO(0, b, Interop\\Polite\\Math\\Matrix\\LinearBuffer, 0)
    ZEND_ARG_INFO(0, offsetB)
    ZEND_ARG_INFO(0, ldB)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_Blas_void, 0, 0, 0)
ZEND_END_ARG_INFO()

/* {{{ Rindow\OpenBLAS\Blas function entries */
static zend_function_entry php_rindow_openblas_blas_me[] = {
    /* clang-format off */
    PHP_ME(Blas, getNumThreads, ai_Blas_void, ZEND_ACC_PUBLIC)
    PHP_ME(Blas, getNumProcs,   ai_Blas_void, ZEND_ACC_PUBLIC)
    PHP_ME(Blas, getConfig,     ai_Blas_void, ZEND_ACC_PUBLIC)
    PHP_ME(Blas, getCorename,   ai_Blas_void, ZEND_ACC_PUBLIC)
    PHP_ME(Blas, scal,  ai_Blas_scal,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, axpy,  ai_Blas_axpy,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, dot,   ai_Blas_dot,   ZEND_ACC_PUBLIC)
    PHP_ME(Blas, asum,  ai_Blas_asum,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, iamax, ai_Blas_iamax, ZEND_ACC_PUBLIC)
#ifdef OPENBLAS_HAVE_IAMIN
    PHP_ME(Blas, iamin, ai_Blas_iamin, ZEND_ACC_PUBLIC)
#endif
    PHP_ME(Blas, copy,  ai_Blas_copy,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, nrm2,  ai_Blas_nrm2,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, rotg,  ai_Blas_rotg,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, rot,   ai_Blas_rot,   ZEND_ACC_PUBLIC)
    PHP_ME(Blas, swap,  ai_Blas_swap,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, gemv,  ai_Blas_gemv,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, gemm,  ai_Blas_gemm,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, symm,  ai_Blas_symm,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, syrk,  ai_Blas_syrk,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, syr2k, ai_Blas_syr2k, ZEND_ACC_PUBLIC)
    PHP_ME(Blas, trmm,  ai_Blas_trmm,  ZEND_ACC_PUBLIC)
    PHP_ME(Blas, trsm,  ai_Blas_trsm,  ZEND_ACC_PUBLIC)
    PHP_FE_END
    /* clang-format on */
};
/* }}} */

/* Class Rindow\OpenBLAS\Blas {{{ */
static zend_class_entry* rindow_openblas_blas_ce;

void php_rindow_openblas_blas_init_ce(INIT_FUNC_ARGS)
{
    zend_class_entry ce;

    INIT_NS_CLASS_ENTRY(ce, "Rindow\\OpenBLAS", "Blas", php_rindow_openblas_blas_me);
    rindow_openblas_blas_ce = zend_register_internal_class(&ce);
    rindow_openblas_blas_ce->create_object = php_rindow_openblas_blas_create_object;

    memcpy(&rindow_openblas_blas_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
    rindow_openblas_blas_object_handlers.offset    = 0;
    rindow_openblas_blas_object_handlers.free_obj  = php_rindow_openblas_blas_free_object;
    rindow_openblas_blas_object_handlers.clone_obj = NULL;

    //zend_class_implements(rindow_openblas_blas_ce, 2, spl_ce_ArrayAccess, spl_ce_Countable);
}
/* }}} */